jpayne@68: import glob jpayne@68: import os jpayne@68: import subprocess jpayne@68: import sys jpayne@68: import tempfile jpayne@68: from functools import partial jpayne@68: jpayne@68: from . import _reqs jpayne@68: from ._reqs import _StrOrIter jpayne@68: from .warnings import SetuptoolsDeprecationWarning jpayne@68: from .wheel import Wheel jpayne@68: jpayne@68: from distutils import log jpayne@68: from distutils.errors import DistutilsError jpayne@68: jpayne@68: jpayne@68: def _fixup_find_links(find_links): jpayne@68: """Ensure find-links option end-up being a list of strings.""" jpayne@68: if isinstance(find_links, str): jpayne@68: return find_links.split() jpayne@68: assert isinstance(find_links, (tuple, list)) jpayne@68: return find_links jpayne@68: jpayne@68: jpayne@68: def fetch_build_egg(dist, req): jpayne@68: """Fetch an egg needed for building. jpayne@68: jpayne@68: Use pip/wheel to fetch/build a wheel.""" jpayne@68: _DeprecatedInstaller.emit() jpayne@68: _warn_wheel_not_available(dist) jpayne@68: return _fetch_build_egg_no_warn(dist, req) jpayne@68: jpayne@68: jpayne@68: def _fetch_build_eggs(dist, requires: _StrOrIter): jpayne@68: import pkg_resources # Delay import to avoid unnecessary side-effects jpayne@68: jpayne@68: _DeprecatedInstaller.emit(stacklevel=3) jpayne@68: _warn_wheel_not_available(dist) jpayne@68: jpayne@68: resolved_dists = pkg_resources.working_set.resolve( jpayne@68: _reqs.parse(requires, pkg_resources.Requirement), # required for compatibility jpayne@68: installer=partial(_fetch_build_egg_no_warn, dist), # avoid warning twice jpayne@68: replace_conflicting=True, jpayne@68: ) jpayne@68: for dist in resolved_dists: jpayne@68: pkg_resources.working_set.add(dist, replace=True) jpayne@68: return resolved_dists jpayne@68: jpayne@68: jpayne@68: def _fetch_build_egg_no_warn(dist, req): # noqa: C901 # is too complex (16) # FIXME jpayne@68: import pkg_resources # Delay import to avoid unnecessary side-effects jpayne@68: jpayne@68: # Ignore environment markers; if supplied, it is required. jpayne@68: req = strip_marker(req) jpayne@68: # Take easy_install options into account, but do not override relevant jpayne@68: # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll jpayne@68: # take precedence. jpayne@68: opts = dist.get_option_dict('easy_install') jpayne@68: if 'allow_hosts' in opts: jpayne@68: raise DistutilsError( jpayne@68: 'the `allow-hosts` option is not supported ' jpayne@68: 'when using pip to install requirements.' jpayne@68: ) jpayne@68: quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ jpayne@68: if 'PIP_INDEX_URL' in os.environ: jpayne@68: index_url = None jpayne@68: elif 'index_url' in opts: jpayne@68: index_url = opts['index_url'][1] jpayne@68: else: jpayne@68: index_url = None jpayne@68: find_links = ( jpayne@68: _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts else [] jpayne@68: ) jpayne@68: if dist.dependency_links: jpayne@68: find_links.extend(dist.dependency_links) jpayne@68: eggs_dir = os.path.realpath(dist.get_egg_cache_dir()) jpayne@68: environment = pkg_resources.Environment() jpayne@68: for egg_dist in pkg_resources.find_distributions(eggs_dir): jpayne@68: if egg_dist in req and environment.can_add(egg_dist): jpayne@68: return egg_dist jpayne@68: with tempfile.TemporaryDirectory() as tmpdir: jpayne@68: cmd = [ jpayne@68: sys.executable, jpayne@68: '-m', jpayne@68: 'pip', jpayne@68: '--disable-pip-version-check', jpayne@68: 'wheel', jpayne@68: '--no-deps', jpayne@68: '-w', jpayne@68: tmpdir, jpayne@68: ] jpayne@68: if quiet: jpayne@68: cmd.append('--quiet') jpayne@68: if index_url is not None: jpayne@68: cmd.extend(('--index-url', index_url)) jpayne@68: for link in find_links or []: jpayne@68: cmd.extend(('--find-links', link)) jpayne@68: # If requirement is a PEP 508 direct URL, directly pass jpayne@68: # the URL to pip, as `req @ url` does not work on the jpayne@68: # command line. jpayne@68: cmd.append(req.url or str(req)) jpayne@68: try: jpayne@68: subprocess.check_call(cmd) jpayne@68: except subprocess.CalledProcessError as e: jpayne@68: raise DistutilsError(str(e)) from e jpayne@68: wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0]) jpayne@68: dist_location = os.path.join(eggs_dir, wheel.egg_name()) jpayne@68: wheel.install_as_egg(dist_location) jpayne@68: dist_metadata = pkg_resources.PathMetadata( jpayne@68: dist_location, os.path.join(dist_location, 'EGG-INFO') jpayne@68: ) jpayne@68: return pkg_resources.Distribution.from_filename( jpayne@68: dist_location, metadata=dist_metadata jpayne@68: ) jpayne@68: jpayne@68: jpayne@68: def strip_marker(req): jpayne@68: """ jpayne@68: Return a new requirement without the environment marker to avoid jpayne@68: calling pip with something like `babel; extra == "i18n"`, which jpayne@68: would always be ignored. jpayne@68: """ jpayne@68: import pkg_resources # Delay import to avoid unnecessary side-effects jpayne@68: jpayne@68: # create a copy to avoid mutating the input jpayne@68: req = pkg_resources.Requirement.parse(str(req)) jpayne@68: req.marker = None jpayne@68: return req jpayne@68: jpayne@68: jpayne@68: def _warn_wheel_not_available(dist): jpayne@68: import pkg_resources # Delay import to avoid unnecessary side-effects jpayne@68: jpayne@68: try: jpayne@68: pkg_resources.get_distribution('wheel') jpayne@68: except pkg_resources.DistributionNotFound: jpayne@68: dist.announce('WARNING: The wheel package is not available.', log.WARN) jpayne@68: jpayne@68: jpayne@68: class _DeprecatedInstaller(SetuptoolsDeprecationWarning): jpayne@68: _SUMMARY = "setuptools.installer and fetch_build_eggs are deprecated." jpayne@68: _DETAILS = """ jpayne@68: Requirements should be satisfied by a PEP 517 installer. jpayne@68: If you are using pip, you can try `pip install --use-pep517`. jpayne@68: """ jpayne@68: # _DUE_DATE not decided yet