1import glob 2import os 3import subprocess 4import sys 5import tempfile 6import warnings 7from distutils import log 8from distutils.errors import DistutilsError 9 10import pkg_resources 11from setuptools.wheel import Wheel 12from ._deprecation_warning import SetuptoolsDeprecationWarning 13 14 15def _fixup_find_links(find_links): 16 """Ensure find-links option end-up being a list of strings.""" 17 if isinstance(find_links, str): 18 return find_links.split() 19 assert isinstance(find_links, (tuple, list)) 20 return find_links 21 22 23def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME 24 """Fetch an egg needed for building. 25 26 Use pip/wheel to fetch/build a wheel.""" 27 warnings.warn( 28 "setuptools.installer is deprecated. Requirements should " 29 "be satisfied by a PEP 517 installer.", 30 SetuptoolsDeprecationWarning, 31 ) 32 # Warn if wheel is not available 33 try: 34 pkg_resources.get_distribution('wheel') 35 except pkg_resources.DistributionNotFound: 36 dist.announce('WARNING: The wheel package is not available.', log.WARN) 37 # Ignore environment markers; if supplied, it is required. 38 req = strip_marker(req) 39 # Take easy_install options into account, but do not override relevant 40 # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll 41 # take precedence. 42 opts = dist.get_option_dict('easy_install') 43 if 'allow_hosts' in opts: 44 raise DistutilsError('the `allow-hosts` option is not supported ' 45 'when using pip to install requirements.') 46 quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ 47 if 'PIP_INDEX_URL' in os.environ: 48 index_url = None 49 elif 'index_url' in opts: 50 index_url = opts['index_url'][1] 51 else: 52 index_url = None 53 find_links = ( 54 _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts 55 else [] 56 ) 57 if dist.dependency_links: 58 find_links.extend(dist.dependency_links) 59 eggs_dir = os.path.realpath(dist.get_egg_cache_dir()) 60 environment = pkg_resources.Environment() 61 for egg_dist in pkg_resources.find_distributions(eggs_dir): 62 if egg_dist in req and environment.can_add(egg_dist): 63 return egg_dist 64 with tempfile.TemporaryDirectory() as tmpdir: 65 cmd = [ 66 sys.executable, '-m', 'pip', 67 '--disable-pip-version-check', 68 'wheel', '--no-deps', 69 '-w', tmpdir, 70 ] 71 if quiet: 72 cmd.append('--quiet') 73 if index_url is not None: 74 cmd.extend(('--index-url', index_url)) 75 for link in find_links or []: 76 cmd.extend(('--find-links', link)) 77 # If requirement is a PEP 508 direct URL, directly pass 78 # the URL to pip, as `req @ url` does not work on the 79 # command line. 80 cmd.append(req.url or str(req)) 81 try: 82 subprocess.check_call(cmd) 83 except subprocess.CalledProcessError as e: 84 raise DistutilsError(str(e)) from e 85 wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0]) 86 dist_location = os.path.join(eggs_dir, wheel.egg_name()) 87 wheel.install_as_egg(dist_location) 88 dist_metadata = pkg_resources.PathMetadata( 89 dist_location, os.path.join(dist_location, 'EGG-INFO')) 90 dist = pkg_resources.Distribution.from_filename( 91 dist_location, metadata=dist_metadata) 92 return dist 93 94 95def strip_marker(req): 96 """ 97 Return a new requirement without the environment marker to avoid 98 calling pip with something like `babel; extra == "i18n"`, which 99 would always be ignored. 100 """ 101 # create a copy to avoid mutating the input 102 req = pkg_resources.Requirement.parse(str(req)) 103 req.marker = None 104 return req 105