Home | History | Annotate | Download | only in command
      1 #!/usr/bin/env python
      2 """
      3 Easy Install
      4 ------------
      5 
      6 A tool for doing automatic download/extract/build of distutils-based Python
      7 packages.  For detailed documentation, see the accompanying EasyInstall.txt
      8 file, or visit the `EasyInstall home page`__.
      9 
     10 __ https://setuptools.readthedocs.io/en/latest/easy_install.html
     11 
     12 """
     13 
     14 from glob import glob
     15 from distutils.util import get_platform
     16 from distutils.util import convert_path, subst_vars
     17 from distutils.errors import (
     18     DistutilsArgError, DistutilsOptionError,
     19     DistutilsError, DistutilsPlatformError,
     20 )
     21 from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
     22 from distutils import log, dir_util
     23 from distutils.command.build_scripts import first_line_re
     24 from distutils.spawn import find_executable
     25 import sys
     26 import os
     27 import zipimport
     28 import shutil
     29 import tempfile
     30 import zipfile
     31 import re
     32 import stat
     33 import random
     34 import textwrap
     35 import warnings
     36 import site
     37 import struct
     38 import contextlib
     39 import subprocess
     40 import shlex
     41 import io
     42 
     43 from setuptools.extern import six
     44 from setuptools.extern.six.moves import configparser, map
     45 
     46 from setuptools import Command
     47 from setuptools.sandbox import run_setup
     48 from setuptools.py31compat import get_path, get_config_vars
     49 from setuptools.py27compat import rmtree_safe
     50 from setuptools.command import setopt
     51 from setuptools.archive_util import unpack_archive
     52 from setuptools.package_index import (
     53     PackageIndex, parse_requirement_arg, URL_SCHEME,
     54 )
     55 from setuptools.command import bdist_egg, egg_info
     56 from setuptools.wheel import Wheel
     57 from pkg_resources import (
     58     yield_lines, normalize_path, resource_string, ensure_directory,
     59     get_distribution, find_distributions, Environment, Requirement,
     60     Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound,
     61     VersionConflict, DEVELOP_DIST,
     62 )
     63 import pkg_resources.py31compat
     64 
     65 # Turn on PEP440Warnings
     66 warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
     67 
     68 __all__ = [
     69     'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
     70     'main', 'get_exe_prefixes',
     71 ]
     72 
     73 
     74 def is_64bit():
     75     return struct.calcsize("P") == 8
     76 
     77 
     78 def samefile(p1, p2):
     79     """
     80     Determine if two paths reference the same file.
     81 
     82     Augments os.path.samefile to work on Windows and
     83     suppresses errors if the path doesn't exist.
     84     """
     85     both_exist = os.path.exists(p1) and os.path.exists(p2)
     86     use_samefile = hasattr(os.path, 'samefile') and both_exist
     87     if use_samefile:
     88         return os.path.samefile(p1, p2)
     89     norm_p1 = os.path.normpath(os.path.normcase(p1))
     90     norm_p2 = os.path.normpath(os.path.normcase(p2))
     91     return norm_p1 == norm_p2
     92 
     93 
     94 if six.PY2:
     95 
     96     def _to_ascii(s):
     97         return s
     98 
     99     def isascii(s):
    100         try:
    101             six.text_type(s, 'ascii')
    102             return True
    103         except UnicodeError:
    104             return False
    105 else:
    106 
    107     def _to_ascii(s):
    108         return s.encode('ascii')
    109 
    110     def isascii(s):
    111         try:
    112             s.encode('ascii')
    113             return True
    114         except UnicodeError:
    115             return False
    116 
    117 
    118 _one_liner = lambda text: textwrap.dedent(text).strip().replace('\n', '; ')
    119 
    120 
    121 class easy_install(Command):
    122     """Manage a download/build/install process"""
    123     description = "Find/get/install Python packages"
    124     command_consumes_arguments = True
    125 
    126     user_options = [
    127         ('prefix=', None, "installation prefix"),
    128         ("zip-ok", "z", "install package as a zipfile"),
    129         ("multi-version", "m", "make apps have to require() a version"),
    130         ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"),
    131         ("install-dir=", "d", "install package to DIR"),
    132         ("script-dir=", "s", "install scripts to DIR"),
    133         ("exclude-scripts", "x", "Don't install scripts"),
    134         ("always-copy", "a", "Copy all needed packages to install dir"),
    135         ("index-url=", "i", "base URL of Python Package Index"),
    136         ("find-links=", "f", "additional URL(s) to search for packages"),
    137         ("build-directory=", "b",
    138          "download/extract/build in DIR; keep the results"),
    139         ('optimize=', 'O',
    140          "also compile with optimization: -O1 for \"python -O\", "
    141          "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
    142         ('record=', None,
    143          "filename in which to record list of installed files"),
    144         ('always-unzip', 'Z', "don't install as a zipfile, no matter what"),
    145         ('site-dirs=', 'S', "list of directories where .pth files work"),
    146         ('editable', 'e', "Install specified packages in editable form"),
    147         ('no-deps', 'N', "don't install dependencies"),
    148         ('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
    149         ('local-snapshots-ok', 'l',
    150          "allow building eggs from local checkouts"),
    151         ('version', None, "print version information and exit"),
    152         ('no-find-links', None,
    153          "Don't load find-links defined in packages being installed")
    154     ]
    155     boolean_options = [
    156         'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
    157         'editable',
    158         'no-deps', 'local-snapshots-ok', 'version'
    159     ]
    160 
    161     if site.ENABLE_USER_SITE:
    162         help_msg = "install in user site-package '%s'" % site.USER_SITE
    163         user_options.append(('user', None, help_msg))
    164         boolean_options.append('user')
    165 
    166     negative_opt = {'always-unzip': 'zip-ok'}
    167     create_index = PackageIndex
    168 
    169     def initialize_options(self):
    170         # the --user option seems to be an opt-in one,
    171         # so the default should be False.
    172         self.user = 0
    173         self.zip_ok = self.local_snapshots_ok = None
    174         self.install_dir = self.script_dir = self.exclude_scripts = None
    175         self.index_url = None
    176         self.find_links = None
    177         self.build_directory = None
    178         self.args = None
    179         self.optimize = self.record = None
    180         self.upgrade = self.always_copy = self.multi_version = None
    181         self.editable = self.no_deps = self.allow_hosts = None
    182         self.root = self.prefix = self.no_report = None
    183         self.version = None
    184         self.install_purelib = None  # for pure module distributions
    185         self.install_platlib = None  # non-pure (dists w/ extensions)
    186         self.install_headers = None  # for C/C++ headers
    187         self.install_lib = None  # set to either purelib or platlib
    188         self.install_scripts = None
    189         self.install_data = None
    190         self.install_base = None
    191         self.install_platbase = None
    192         if site.ENABLE_USER_SITE:
    193             self.install_userbase = site.USER_BASE
    194             self.install_usersite = site.USER_SITE
    195         else:
    196             self.install_userbase = None
    197             self.install_usersite = None
    198         self.no_find_links = None
    199 
    200         # Options not specifiable via command line
    201         self.package_index = None
    202         self.pth_file = self.always_copy_from = None
    203         self.site_dirs = None
    204         self.installed_projects = {}
    205         self.sitepy_installed = False
    206         # Always read easy_install options, even if we are subclassed, or have
    207         # an independent instance created.  This ensures that defaults will
    208         # always come from the standard configuration file(s)' "easy_install"
    209         # section, even if this is a "develop" or "install" command, or some
    210         # other embedding.
    211         self._dry_run = None
    212         self.verbose = self.distribution.verbose
    213         self.distribution._set_command_options(
    214             self, self.distribution.get_option_dict('easy_install')
    215         )
    216 
    217     def delete_blockers(self, blockers):
    218         extant_blockers = (
    219             filename for filename in blockers
    220             if os.path.exists(filename) or os.path.islink(filename)
    221         )
    222         list(map(self._delete_path, extant_blockers))
    223 
    224     def _delete_path(self, path):
    225         log.info("Deleting %s", path)
    226         if self.dry_run:
    227             return
    228 
    229         is_tree = os.path.isdir(path) and not os.path.islink(path)
    230         remover = rmtree if is_tree else os.unlink
    231         remover(path)
    232 
    233     @staticmethod
    234     def _render_version():
    235         """
    236         Render the Setuptools version and installation details, then exit.
    237         """
    238         ver = sys.version[:3]
    239         dist = get_distribution('setuptools')
    240         tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'
    241         print(tmpl.format(**locals()))
    242         raise SystemExit()
    243 
    244     def finalize_options(self):
    245         self.version and self._render_version()
    246 
    247         py_version = sys.version.split()[0]
    248         prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
    249 
    250         self.config_vars = {
    251             'dist_name': self.distribution.get_name(),
    252             'dist_version': self.distribution.get_version(),
    253             'dist_fullname': self.distribution.get_fullname(),
    254             'py_version': py_version,
    255             'py_version_short': py_version[0:3],
    256             'py_version_nodot': py_version[0] + py_version[2],
    257             'sys_prefix': prefix,
    258             'prefix': prefix,
    259             'sys_exec_prefix': exec_prefix,
    260             'exec_prefix': exec_prefix,
    261             # Only python 3.2+ has abiflags
    262             'abiflags': getattr(sys, 'abiflags', ''),
    263         }
    264 
    265         if site.ENABLE_USER_SITE:
    266             self.config_vars['userbase'] = self.install_userbase
    267             self.config_vars['usersite'] = self.install_usersite
    268 
    269         self._fix_install_dir_for_user_site()
    270 
    271         self.expand_basedirs()
    272         self.expand_dirs()
    273 
    274         self._expand(
    275             'install_dir', 'script_dir', 'build_directory',
    276             'site_dirs',
    277         )
    278         # If a non-default installation directory was specified, default the
    279         # script directory to match it.
    280         if self.script_dir is None:
    281             self.script_dir = self.install_dir
    282 
    283         if self.no_find_links is None:
    284             self.no_find_links = False
    285 
    286         # Let install_dir get set by install_lib command, which in turn
    287         # gets its info from the install command, and takes into account
    288         # --prefix and --home and all that other crud.
    289         self.set_undefined_options(
    290             'install_lib', ('install_dir', 'install_dir')
    291         )
    292         # Likewise, set default script_dir from 'install_scripts.install_dir'
    293         self.set_undefined_options(
    294             'install_scripts', ('install_dir', 'script_dir')
    295         )
    296 
    297         if self.user and self.install_purelib:
    298             self.install_dir = self.install_purelib
    299             self.script_dir = self.install_scripts
    300         # default --record from the install command
    301         self.set_undefined_options('install', ('record', 'record'))
    302         # Should this be moved to the if statement below? It's not used
    303         # elsewhere
    304         normpath = map(normalize_path, sys.path)
    305         self.all_site_dirs = get_site_dirs()
    306         if self.site_dirs is not None:
    307             site_dirs = [
    308                 os.path.expanduser(s.strip()) for s in
    309                 self.site_dirs.split(',')
    310             ]
    311             for d in site_dirs:
    312                 if not os.path.isdir(d):
    313                     log.warn("%s (in --site-dirs) does not exist", d)
    314                 elif normalize_path(d) not in normpath:
    315                     raise DistutilsOptionError(
    316                         d + " (in --site-dirs) is not on sys.path"
    317                     )
    318                 else:
    319                     self.all_site_dirs.append(normalize_path(d))
    320         if not self.editable:
    321             self.check_site_dir()
    322         self.index_url = self.index_url or "https://pypi.org/simple/"
    323         self.shadow_path = self.all_site_dirs[:]
    324         for path_item in self.install_dir, normalize_path(self.script_dir):
    325             if path_item not in self.shadow_path:
    326                 self.shadow_path.insert(0, path_item)
    327 
    328         if self.allow_hosts is not None:
    329             hosts = [s.strip() for s in self.allow_hosts.split(',')]
    330         else:
    331             hosts = ['*']
    332         if self.package_index is None:
    333             self.package_index = self.create_index(
    334                 self.index_url, search_path=self.shadow_path, hosts=hosts,
    335             )
    336         self.local_index = Environment(self.shadow_path + sys.path)
    337 
    338         if self.find_links is not None:
    339             if isinstance(self.find_links, six.string_types):
    340                 self.find_links = self.find_links.split()
    341         else:
    342             self.find_links = []
    343         if self.local_snapshots_ok:
    344             self.package_index.scan_egg_links(self.shadow_path + sys.path)
    345         if not self.no_find_links:
    346             self.package_index.add_find_links(self.find_links)
    347         self.set_undefined_options('install_lib', ('optimize', 'optimize'))
    348         if not isinstance(self.optimize, int):
    349             try:
    350                 self.optimize = int(self.optimize)
    351                 if not (0 <= self.optimize <= 2):
    352                     raise ValueError
    353             except ValueError:
    354                 raise DistutilsOptionError("--optimize must be 0, 1, or 2")
    355 
    356         if self.editable and not self.build_directory:
    357             raise DistutilsArgError(
    358                 "Must specify a build directory (-b) when using --editable"
    359             )
    360         if not self.args:
    361             raise DistutilsArgError(
    362                 "No urls, filenames, or requirements specified (see --help)")
    363 
    364         self.outputs = []
    365 
    366     def _fix_install_dir_for_user_site(self):
    367         """
    368         Fix the install_dir if "--user" was used.
    369         """
    370         if not self.user or not site.ENABLE_USER_SITE:
    371             return
    372 
    373         self.create_home_path()
    374         if self.install_userbase is None:
    375             msg = "User base directory is not specified"
    376             raise DistutilsPlatformError(msg)
    377         self.install_base = self.install_platbase = self.install_userbase
    378         scheme_name = os.name.replace('posix', 'unix') + '_user'
    379         self.select_scheme(scheme_name)
    380 
    381     def _expand_attrs(self, attrs):
    382         for attr in attrs:
    383             val = getattr(self, attr)
    384             if val is not None:
    385                 if os.name == 'posix' or os.name == 'nt':
    386                     val = os.path.expanduser(val)
    387                 val = subst_vars(val, self.config_vars)
    388                 setattr(self, attr, val)
    389 
    390     def expand_basedirs(self):
    391         """Calls `os.path.expanduser` on install_base, install_platbase and
    392         root."""
    393         self._expand_attrs(['install_base', 'install_platbase', 'root'])
    394 
    395     def expand_dirs(self):
    396         """Calls `os.path.expanduser` on install dirs."""
    397         dirs = [
    398             'install_purelib',
    399             'install_platlib',
    400             'install_lib',
    401             'install_headers',
    402             'install_scripts',
    403             'install_data',
    404         ]
    405         self._expand_attrs(dirs)
    406 
    407     def run(self):
    408         if self.verbose != self.distribution.verbose:
    409             log.set_verbosity(self.verbose)
    410         try:
    411             for spec in self.args:
    412                 self.easy_install(spec, not self.no_deps)
    413             if self.record:
    414                 outputs = self.outputs
    415                 if self.root:  # strip any package prefix
    416                     root_len = len(self.root)
    417                     for counter in range(len(outputs)):
    418                         outputs[counter] = outputs[counter][root_len:]
    419                 from distutils import file_util
    420 
    421                 self.execute(
    422                     file_util.write_file, (self.record, outputs),
    423                     "writing list of installed files to '%s'" %
    424                     self.record
    425                 )
    426             self.warn_deprecated_options()
    427         finally:
    428             log.set_verbosity(self.distribution.verbose)
    429 
    430     def pseudo_tempname(self):
    431         """Return a pseudo-tempname base in the install directory.
    432         This code is intentionally naive; if a malicious party can write to
    433         the target directory you're already in deep doodoo.
    434         """
    435         try:
    436             pid = os.getpid()
    437         except Exception:
    438             pid = random.randint(0, sys.maxsize)
    439         return os.path.join(self.install_dir, "test-easy-install-%s" % pid)
    440 
    441     def warn_deprecated_options(self):
    442         pass
    443 
    444     def check_site_dir(self):
    445         """Verify that self.install_dir is .pth-capable dir, if needed"""
    446 
    447         instdir = normalize_path(self.install_dir)
    448         pth_file = os.path.join(instdir, 'easy-install.pth')
    449 
    450         # Is it a configured, PYTHONPATH, implicit, or explicit site dir?
    451         is_site_dir = instdir in self.all_site_dirs
    452 
    453         if not is_site_dir and not self.multi_version:
    454             # No?  Then directly test whether it does .pth file processing
    455             is_site_dir = self.check_pth_processing()
    456         else:
    457             # make sure we can write to target dir
    458             testfile = self.pseudo_tempname() + '.write-test'
    459             test_exists = os.path.exists(testfile)
    460             try:
    461                 if test_exists:
    462                     os.unlink(testfile)
    463                 open(testfile, 'w').close()
    464                 os.unlink(testfile)
    465             except (OSError, IOError):
    466                 self.cant_write_to_target()
    467 
    468         if not is_site_dir and not self.multi_version:
    469             # Can't install non-multi to non-site dir
    470             raise DistutilsError(self.no_default_version_msg())
    471 
    472         if is_site_dir:
    473             if self.pth_file is None:
    474                 self.pth_file = PthDistributions(pth_file, self.all_site_dirs)
    475         else:
    476             self.pth_file = None
    477 
    478         if instdir not in map(normalize_path, _pythonpath()):
    479             # only PYTHONPATH dirs need a site.py, so pretend it's there
    480             self.sitepy_installed = True
    481         elif self.multi_version and not os.path.exists(pth_file):
    482             self.sitepy_installed = True  # don't need site.py in this case
    483             self.pth_file = None  # and don't create a .pth file
    484         self.install_dir = instdir
    485 
    486     __cant_write_msg = textwrap.dedent("""
    487         can't create or remove files in install directory
    488 
    489         The following error occurred while trying to add or remove files in the
    490         installation directory:
    491 
    492             %s
    493 
    494         The installation directory you specified (via --install-dir, --prefix, or
    495         the distutils default setting) was:
    496 
    497             %s
    498         """).lstrip()
    499 
    500     __not_exists_id = textwrap.dedent("""
    501         This directory does not currently exist.  Please create it and try again, or
    502         choose a different installation directory (using the -d or --install-dir
    503         option).
    504         """).lstrip()
    505 
    506     __access_msg = textwrap.dedent("""
    507         Perhaps your account does not have write access to this directory?  If the
    508         installation directory is a system-owned directory, you may need to sign in
    509         as the administrator or "root" account.  If you do not have administrative
    510         access to this machine, you may wish to choose a different installation
    511         directory, preferably one that is listed in your PYTHONPATH environment
    512         variable.
    513 
    514         For information on other options, you may wish to consult the
    515         documentation at:
    516 
    517           https://setuptools.readthedocs.io/en/latest/easy_install.html
    518 
    519         Please make the appropriate changes for your system and try again.
    520         """).lstrip()
    521 
    522     def cant_write_to_target(self):
    523         msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,)
    524 
    525         if not os.path.exists(self.install_dir):
    526             msg += '\n' + self.__not_exists_id
    527         else:
    528             msg += '\n' + self.__access_msg
    529         raise DistutilsError(msg)
    530 
    531     def check_pth_processing(self):
    532         """Empirically verify whether .pth files are supported in inst. dir"""
    533         instdir = self.install_dir
    534         log.info("Checking .pth file support in %s", instdir)
    535         pth_file = self.pseudo_tempname() + ".pth"
    536         ok_file = pth_file + '.ok'
    537         ok_exists = os.path.exists(ok_file)
    538         tmpl = _one_liner("""
    539             import os
    540             f = open({ok_file!r}, 'w')
    541             f.write('OK')
    542             f.close()
    543             """) + '\n'
    544         try:
    545             if ok_exists:
    546                 os.unlink(ok_file)
    547             dirname = os.path.dirname(ok_file)
    548             pkg_resources.py31compat.makedirs(dirname, exist_ok=True)
    549             f = open(pth_file, 'w')
    550         except (OSError, IOError):
    551             self.cant_write_to_target()
    552         else:
    553             try:
    554                 f.write(tmpl.format(**locals()))
    555                 f.close()
    556                 f = None
    557                 executable = sys.executable
    558                 if os.name == 'nt':
    559                     dirname, basename = os.path.split(executable)
    560                     alt = os.path.join(dirname, 'pythonw.exe')
    561                     use_alt = (
    562                         basename.lower() == 'python.exe' and
    563                         os.path.exists(alt)
    564                     )
    565                     if use_alt:
    566                         # use pythonw.exe to avoid opening a console window
    567                         executable = alt
    568 
    569                 from distutils.spawn import spawn
    570 
    571                 spawn([executable, '-E', '-c', 'pass'], 0)
    572 
    573                 if os.path.exists(ok_file):
    574                     log.info(
    575                         "TEST PASSED: %s appears to support .pth files",
    576                         instdir
    577                     )
    578                     return True
    579             finally:
    580                 if f:
    581                     f.close()
    582                 if os.path.exists(ok_file):
    583                     os.unlink(ok_file)
    584                 if os.path.exists(pth_file):
    585                     os.unlink(pth_file)
    586         if not self.multi_version:
    587             log.warn("TEST FAILED: %s does NOT support .pth files", instdir)
    588         return False
    589 
    590     def install_egg_scripts(self, dist):
    591         """Write all the scripts for `dist`, unless scripts are excluded"""
    592         if not self.exclude_scripts and dist.metadata_isdir('scripts'):
    593             for script_name in dist.metadata_listdir('scripts'):
    594                 if dist.metadata_isdir('scripts/' + script_name):
    595                     # The "script" is a directory, likely a Python 3
    596                     # __pycache__ directory, so skip it.
    597                     continue
    598                 self.install_script(
    599                     dist, script_name,
    600                     dist.get_metadata('scripts/' + script_name)
    601                 )
    602         self.install_wrapper_scripts(dist)
    603 
    604     def add_output(self, path):
    605         if os.path.isdir(path):
    606             for base, dirs, files in os.walk(path):
    607                 for filename in files:
    608                     self.outputs.append(os.path.join(base, filename))
    609         else:
    610             self.outputs.append(path)
    611 
    612     def not_editable(self, spec):
    613         if self.editable:
    614             raise DistutilsArgError(
    615                 "Invalid argument %r: you can't use filenames or URLs "
    616                 "with --editable (except via the --find-links option)."
    617                 % (spec,)
    618             )
    619 
    620     def check_editable(self, spec):
    621         if not self.editable:
    622             return
    623 
    624         if os.path.exists(os.path.join(self.build_directory, spec.key)):
    625             raise DistutilsArgError(
    626                 "%r already exists in %s; can't do a checkout there" %
    627                 (spec.key, self.build_directory)
    628             )
    629 
    630     @contextlib.contextmanager
    631     def _tmpdir(self):
    632         tmpdir = tempfile.mkdtemp(prefix=six.u("easy_install-"))
    633         try:
    634             # cast to str as workaround for #709 and #710 and #712
    635             yield str(tmpdir)
    636         finally:
    637             os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir))
    638 
    639     def easy_install(self, spec, deps=False):
    640         if not self.editable:
    641             self.install_site_py()
    642 
    643         with self._tmpdir() as tmpdir:
    644             if not isinstance(spec, Requirement):
    645                 if URL_SCHEME(spec):
    646                     # It's a url, download it to tmpdir and process
    647                     self.not_editable(spec)
    648                     dl = self.package_index.download(spec, tmpdir)
    649                     return self.install_item(None, dl, tmpdir, deps, True)
    650 
    651                 elif os.path.exists(spec):
    652                     # Existing file or directory, just process it directly
    653                     self.not_editable(spec)
    654                     return self.install_item(None, spec, tmpdir, deps, True)
    655                 else:
    656                     spec = parse_requirement_arg(spec)
    657 
    658             self.check_editable(spec)
    659             dist = self.package_index.fetch_distribution(
    660                 spec, tmpdir, self.upgrade, self.editable,
    661                 not self.always_copy, self.local_index
    662             )
    663             if dist is None:
    664                 msg = "Could not find suitable distribution for %r" % spec
    665                 if self.always_copy:
    666                     msg += " (--always-copy skips system and development eggs)"
    667                 raise DistutilsError(msg)
    668             elif dist.precedence == DEVELOP_DIST:
    669                 # .egg-info dists don't need installing, just process deps
    670                 self.process_distribution(spec, dist, deps, "Using")
    671                 return dist
    672             else:
    673                 return self.install_item(spec, dist.location, tmpdir, deps)
    674 
    675     def install_item(self, spec, download, tmpdir, deps, install_needed=False):
    676 
    677         # Installation is also needed if file in tmpdir or is not an egg
    678         install_needed = install_needed or self.always_copy
    679         install_needed = install_needed or os.path.dirname(download) == tmpdir
    680         install_needed = install_needed or not download.endswith('.egg')
    681         install_needed = install_needed or (
    682             self.always_copy_from is not None and
    683             os.path.dirname(normalize_path(download)) ==
    684             normalize_path(self.always_copy_from)
    685         )
    686 
    687         if spec and not install_needed:
    688             # at this point, we know it's a local .egg, we just don't know if
    689             # it's already installed.
    690             for dist in self.local_index[spec.project_name]:
    691                 if dist.location == download:
    692                     break
    693             else:
    694                 install_needed = True  # it's not in the local index
    695 
    696         log.info("Processing %s", os.path.basename(download))
    697 
    698         if install_needed:
    699             dists = self.install_eggs(spec, download, tmpdir)
    700             for dist in dists:
    701                 self.process_distribution(spec, dist, deps)
    702         else:
    703             dists = [self.egg_distribution(download)]
    704             self.process_distribution(spec, dists[0], deps, "Using")
    705 
    706         if spec is not None:
    707             for dist in dists:
    708                 if dist in spec:
    709                     return dist
    710 
    711     def select_scheme(self, name):
    712         """Sets the install directories by applying the install schemes."""
    713         # it's the caller's problem if they supply a bad name!
    714         scheme = INSTALL_SCHEMES[name]
    715         for key in SCHEME_KEYS:
    716             attrname = 'install_' + key
    717             if getattr(self, attrname) is None:
    718                 setattr(self, attrname, scheme[key])
    719 
    720     def process_distribution(self, requirement, dist, deps=True, *info):
    721         self.update_pth(dist)
    722         self.package_index.add(dist)
    723         if dist in self.local_index[dist.key]:
    724             self.local_index.remove(dist)
    725         self.local_index.add(dist)
    726         self.install_egg_scripts(dist)
    727         self.installed_projects[dist.key] = dist
    728         log.info(self.installation_report(requirement, dist, *info))
    729         if (dist.has_metadata('dependency_links.txt') and
    730                 not self.no_find_links):
    731             self.package_index.add_find_links(
    732                 dist.get_metadata_lines('dependency_links.txt')
    733             )
    734         if not deps and not self.always_copy:
    735             return
    736         elif requirement is not None and dist.key != requirement.key:
    737             log.warn("Skipping dependencies for %s", dist)
    738             return  # XXX this is not the distribution we were looking for
    739         elif requirement is None or dist not in requirement:
    740             # if we wound up with a different version, resolve what we've got
    741             distreq = dist.as_requirement()
    742             requirement = Requirement(str(distreq))
    743         log.info("Processing dependencies for %s", requirement)
    744         try:
    745             distros = WorkingSet([]).resolve(
    746                 [requirement], self.local_index, self.easy_install
    747             )
    748         except DistributionNotFound as e:
    749             raise DistutilsError(str(e))
    750         except VersionConflict as e:
    751             raise DistutilsError(e.report())
    752         if self.always_copy or self.always_copy_from:
    753             # Force all the relevant distros to be copied or activated
    754             for dist in distros:
    755                 if dist.key not in self.installed_projects:
    756                     self.easy_install(dist.as_requirement())
    757         log.info("Finished processing dependencies for %s", requirement)
    758 
    759     def should_unzip(self, dist):
    760         if self.zip_ok is not None:
    761             return not self.zip_ok
    762         if dist.has_metadata('not-zip-safe'):
    763             return True
    764         if not dist.has_metadata('zip-safe'):
    765             return True
    766         return False
    767 
    768     def maybe_move(self, spec, dist_filename, setup_base):
    769         dst = os.path.join(self.build_directory, spec.key)
    770         if os.path.exists(dst):
    771             msg = (
    772                 "%r already exists in %s; build directory %s will not be kept"
    773             )
    774             log.warn(msg, spec.key, self.build_directory, setup_base)
    775             return setup_base
    776         if os.path.isdir(dist_filename):
    777             setup_base = dist_filename
    778         else:
    779             if os.path.dirname(dist_filename) == setup_base:
    780                 os.unlink(dist_filename)  # get it out of the tmp dir
    781             contents = os.listdir(setup_base)
    782             if len(contents) == 1:
    783                 dist_filename = os.path.join(setup_base, contents[0])
    784                 if os.path.isdir(dist_filename):
    785                     # if the only thing there is a directory, move it instead
    786                     setup_base = dist_filename
    787         ensure_directory(dst)
    788         shutil.move(setup_base, dst)
    789         return dst
    790 
    791     def install_wrapper_scripts(self, dist):
    792         if self.exclude_scripts:
    793             return
    794         for args in ScriptWriter.best().get_args(dist):
    795             self.write_script(*args)
    796 
    797     def install_script(self, dist, script_name, script_text, dev_path=None):
    798         """Generate a legacy script wrapper and install it"""
    799         spec = str(dist.as_requirement())
    800         is_script = is_python_script(script_text, script_name)
    801 
    802         if is_script:
    803             body = self._load_template(dev_path) % locals()
    804             script_text = ScriptWriter.get_header(script_text) + body
    805         self.write_script(script_name, _to_ascii(script_text), 'b')
    806 
    807     @staticmethod
    808     def _load_template(dev_path):
    809         """
    810         There are a couple of template scripts in the package. This
    811         function loads one of them and prepares it for use.
    812         """
    813         # See https://github.com/pypa/setuptools/issues/134 for info
    814         # on script file naming and downstream issues with SVR4
    815         name = 'script.tmpl'
    816         if dev_path:
    817             name = name.replace('.tmpl', ' (dev).tmpl')
    818 
    819         raw_bytes = resource_string('setuptools', name)
    820         return raw_bytes.decode('utf-8')
    821 
    822     def write_script(self, script_name, contents, mode="t", blockers=()):
    823         """Write an executable file to the scripts directory"""
    824         self.delete_blockers(  # clean up old .py/.pyw w/o a script
    825             [os.path.join(self.script_dir, x) for x in blockers]
    826         )
    827         log.info("Installing %s script to %s", script_name, self.script_dir)
    828         target = os.path.join(self.script_dir, script_name)
    829         self.add_output(target)
    830 
    831         if self.dry_run:
    832             return
    833 
    834         mask = current_umask()
    835         ensure_directory(target)
    836         if os.path.exists(target):
    837             os.unlink(target)
    838         with open(target, "w" + mode) as f:
    839             f.write(contents)
    840         chmod(target, 0o777 - mask)
    841 
    842     def install_eggs(self, spec, dist_filename, tmpdir):
    843         # .egg dirs or files are already built, so just return them
    844         if dist_filename.lower().endswith('.egg'):
    845             return [self.install_egg(dist_filename, tmpdir)]
    846         elif dist_filename.lower().endswith('.exe'):
    847             return [self.install_exe(dist_filename, tmpdir)]
    848         elif dist_filename.lower().endswith('.whl'):
    849             return [self.install_wheel(dist_filename, tmpdir)]
    850 
    851         # Anything else, try to extract and build
    852         setup_base = tmpdir
    853         if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'):
    854             unpack_archive(dist_filename, tmpdir, self.unpack_progress)
    855         elif os.path.isdir(dist_filename):
    856             setup_base = os.path.abspath(dist_filename)
    857 
    858         if (setup_base.startswith(tmpdir)  # something we downloaded
    859                 and self.build_directory and spec is not None):
    860             setup_base = self.maybe_move(spec, dist_filename, setup_base)
    861 
    862         # Find the setup.py file
    863         setup_script = os.path.join(setup_base, 'setup.py')
    864 
    865         if not os.path.exists(setup_script):
    866             setups = glob(os.path.join(setup_base, '*', 'setup.py'))
    867             if not setups:
    868                 raise DistutilsError(
    869                     "Couldn't find a setup script in %s" %
    870                     os.path.abspath(dist_filename)
    871                 )
    872             if len(setups) > 1:
    873                 raise DistutilsError(
    874                     "Multiple setup scripts in %s" %
    875                     os.path.abspath(dist_filename)
    876                 )
    877             setup_script = setups[0]
    878 
    879         # Now run it, and return the result
    880         if self.editable:
    881             log.info(self.report_editable(spec, setup_script))
    882             return []
    883         else:
    884             return self.build_and_install(setup_script, setup_base)
    885 
    886     def egg_distribution(self, egg_path):
    887         if os.path.isdir(egg_path):
    888             metadata = PathMetadata(egg_path, os.path.join(egg_path,
    889                                                            'EGG-INFO'))
    890         else:
    891             metadata = EggMetadata(zipimport.zipimporter(egg_path))
    892         return Distribution.from_filename(egg_path, metadata=metadata)
    893 
    894     def install_egg(self, egg_path, tmpdir):
    895         destination = os.path.join(
    896             self.install_dir,
    897             os.path.basename(egg_path),
    898         )
    899         destination = os.path.abspath(destination)
    900         if not self.dry_run:
    901             ensure_directory(destination)
    902 
    903         dist = self.egg_distribution(egg_path)
    904         if not samefile(egg_path, destination):
    905             if os.path.isdir(destination) and not os.path.islink(destination):
    906                 dir_util.remove_tree(destination, dry_run=self.dry_run)
    907             elif os.path.exists(destination):
    908                 self.execute(
    909                     os.unlink,
    910                     (destination,),
    911                     "Removing " + destination,
    912                 )
    913             try:
    914                 new_dist_is_zipped = False
    915                 if os.path.isdir(egg_path):
    916                     if egg_path.startswith(tmpdir):
    917                         f, m = shutil.move, "Moving"
    918                     else:
    919                         f, m = shutil.copytree, "Copying"
    920                 elif self.should_unzip(dist):
    921                     self.mkpath(destination)
    922                     f, m = self.unpack_and_compile, "Extracting"
    923                 else:
    924                     new_dist_is_zipped = True
    925                     if egg_path.startswith(tmpdir):
    926                         f, m = shutil.move, "Moving"
    927                     else:
    928                         f, m = shutil.copy2, "Copying"
    929                 self.execute(
    930                     f,
    931                     (egg_path, destination),
    932                     (m + " %s to %s") % (
    933                         os.path.basename(egg_path),
    934                         os.path.dirname(destination)
    935                     ),
    936                 )
    937                 update_dist_caches(
    938                     destination,
    939                     fix_zipimporter_caches=new_dist_is_zipped,
    940                 )
    941             except Exception:
    942                 update_dist_caches(destination, fix_zipimporter_caches=False)
    943                 raise
    944 
    945         self.add_output(destination)
    946         return self.egg_distribution(destination)
    947 
    948     def install_exe(self, dist_filename, tmpdir):
    949         # See if it's valid, get data
    950         cfg = extract_wininst_cfg(dist_filename)
    951         if cfg is None:
    952             raise DistutilsError(
    953                 "%s is not a valid distutils Windows .exe" % dist_filename
    954             )
    955         # Create a dummy distribution object until we build the real distro
    956         dist = Distribution(
    957             None,
    958             project_name=cfg.get('metadata', 'name'),
    959             version=cfg.get('metadata', 'version'), platform=get_platform(),
    960         )
    961 
    962         # Convert the .exe to an unpacked egg
    963         egg_path = os.path.join(tmpdir, dist.egg_name() + '.egg')
    964         dist.location = egg_path
    965         egg_tmp = egg_path + '.tmp'
    966         _egg_info = os.path.join(egg_tmp, 'EGG-INFO')
    967         pkg_inf = os.path.join(_egg_info, 'PKG-INFO')
    968         ensure_directory(pkg_inf)  # make sure EGG-INFO dir exists
    969         dist._provider = PathMetadata(egg_tmp, _egg_info)  # XXX
    970         self.exe_to_egg(dist_filename, egg_tmp)
    971 
    972         # Write EGG-INFO/PKG-INFO
    973         if not os.path.exists(pkg_inf):
    974             f = open(pkg_inf, 'w')
    975             f.write('Metadata-Version: 1.0\n')
    976             for k, v in cfg.items('metadata'):
    977                 if k != 'target_version':
    978                     f.write('%s: %s\n' % (k.replace('_', '-').title(), v))
    979             f.close()
    980         script_dir = os.path.join(_egg_info, 'scripts')
    981         # delete entry-point scripts to avoid duping
    982         self.delete_blockers([
    983             os.path.join(script_dir, args[0])
    984             for args in ScriptWriter.get_args(dist)
    985         ])
    986         # Build .egg file from tmpdir
    987         bdist_egg.make_zipfile(
    988             egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run,
    989         )
    990         # install the .egg
    991         return self.install_egg(egg_path, tmpdir)
    992 
    993     def exe_to_egg(self, dist_filename, egg_tmp):
    994         """Extract a bdist_wininst to the directories an egg would use"""
    995         # Check for .pth file and set up prefix translations
    996         prefixes = get_exe_prefixes(dist_filename)
    997         to_compile = []
    998         native_libs = []
    999         top_level = {}
   1000 
   1001         def process(src, dst):
   1002             s = src.lower()
   1003             for old, new in prefixes:
   1004                 if s.startswith(old):
   1005                     src = new + src[len(old):]
   1006                     parts = src.split('/')
   1007                     dst = os.path.join(egg_tmp, *parts)
   1008                     dl = dst.lower()
   1009                     if dl.endswith('.pyd') or dl.endswith('.dll'):
   1010                         parts[-1] = bdist_egg.strip_module(parts[-1])
   1011                         top_level[os.path.splitext(parts[0])[0]] = 1
   1012                         native_libs.append(src)
   1013                     elif dl.endswith('.py') and old != 'SCRIPTS/':
   1014                         top_level[os.path.splitext(parts[0])[0]] = 1
   1015                         to_compile.append(dst)
   1016                     return dst
   1017             if not src.endswith('.pth'):
   1018                 log.warn("WARNING: can't process %s", src)
   1019             return None
   1020 
   1021         # extract, tracking .pyd/.dll->native_libs and .py -> to_compile
   1022         unpack_archive(dist_filename, egg_tmp, process)
   1023         stubs = []
   1024         for res in native_libs:
   1025             if res.lower().endswith('.pyd'):  # create stubs for .pyd's
   1026                 parts = res.split('/')
   1027                 resource = parts[-1]
   1028                 parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py'
   1029                 pyfile = os.path.join(egg_tmp, *parts)
   1030                 to_compile.append(pyfile)
   1031                 stubs.append(pyfile)
   1032                 bdist_egg.write_stub(resource, pyfile)
   1033         self.byte_compile(to_compile)  # compile .py's
   1034         bdist_egg.write_safety_flag(
   1035             os.path.join(egg_tmp, 'EGG-INFO'),
   1036             bdist_egg.analyze_egg(egg_tmp, stubs))  # write zip-safety flag
   1037 
   1038         for name in 'top_level', 'native_libs':
   1039             if locals()[name]:
   1040                 txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt')
   1041                 if not os.path.exists(txt):
   1042                     f = open(txt, 'w')
   1043                     f.write('\n'.join(locals()[name]) + '\n')
   1044                     f.close()
   1045 
   1046     def install_wheel(self, wheel_path, tmpdir):
   1047         wheel = Wheel(wheel_path)
   1048         assert wheel.is_compatible()
   1049         destination = os.path.join(self.install_dir, wheel.egg_name())
   1050         destination = os.path.abspath(destination)
   1051         if not self.dry_run:
   1052             ensure_directory(destination)
   1053         if os.path.isdir(destination) and not os.path.islink(destination):
   1054             dir_util.remove_tree(destination, dry_run=self.dry_run)
   1055         elif os.path.exists(destination):
   1056             self.execute(
   1057                 os.unlink,
   1058                 (destination,),
   1059                 "Removing " + destination,
   1060             )
   1061         try:
   1062             self.execute(
   1063                 wheel.install_as_egg,
   1064                 (destination,),
   1065                 ("Installing %s to %s") % (
   1066                     os.path.basename(wheel_path),
   1067                     os.path.dirname(destination)
   1068                 ),
   1069             )
   1070         finally:
   1071             update_dist_caches(destination, fix_zipimporter_caches=False)
   1072         self.add_output(destination)
   1073         return self.egg_distribution(destination)
   1074 
   1075     __mv_warning = textwrap.dedent("""
   1076         Because this distribution was installed --multi-version, before you can
   1077         import modules from this package in an application, you will need to
   1078         'import pkg_resources' and then use a 'require()' call similar to one of
   1079         these examples, in order to select the desired version:
   1080 
   1081             pkg_resources.require("%(name)s")  # latest installed version
   1082             pkg_resources.require("%(name)s==%(version)s")  # this exact version
   1083             pkg_resources.require("%(name)s>=%(version)s")  # this version or higher
   1084         """).lstrip()
   1085 
   1086     __id_warning = textwrap.dedent("""
   1087         Note also that the installation directory must be on sys.path at runtime for
   1088         this to work.  (e.g. by being the application's script directory, by being on
   1089         PYTHONPATH, or by being added to sys.path by your code.)
   1090         """)
   1091 
   1092     def installation_report(self, req, dist, what="Installed"):
   1093         """Helpful installation message for display to package users"""
   1094         msg = "\n%(what)s %(eggloc)s%(extras)s"
   1095         if self.multi_version and not self.no_report:
   1096             msg += '\n' + self.__mv_warning
   1097             if self.install_dir not in map(normalize_path, sys.path):
   1098                 msg += '\n' + self.__id_warning
   1099 
   1100         eggloc = dist.location
   1101         name = dist.project_name
   1102         version = dist.version
   1103         extras = ''  # TODO: self.report_extras(req, dist)
   1104         return msg % locals()
   1105 
   1106     __editable_msg = textwrap.dedent("""
   1107         Extracted editable version of %(spec)s to %(dirname)s
   1108 
   1109         If it uses setuptools in its setup script, you can activate it in
   1110         "development" mode by going to that directory and running::
   1111 
   1112             %(python)s setup.py develop
   1113 
   1114         See the setuptools documentation for the "develop" command for more info.
   1115         """).lstrip()
   1116 
   1117     def report_editable(self, spec, setup_script):
   1118         dirname = os.path.dirname(setup_script)
   1119         python = sys.executable
   1120         return '\n' + self.__editable_msg % locals()
   1121 
   1122     def run_setup(self, setup_script, setup_base, args):
   1123         sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)
   1124         sys.modules.setdefault('distutils.command.egg_info', egg_info)
   1125 
   1126         args = list(args)
   1127         if self.verbose > 2:
   1128             v = 'v' * (self.verbose - 1)
   1129             args.insert(0, '-' + v)
   1130         elif self.verbose < 2:
   1131             args.insert(0, '-q')
   1132         if self.dry_run:
   1133             args.insert(0, '-n')
   1134         log.info(
   1135             "Running %s %s", setup_script[len(setup_base) + 1:], ' '.join(args)
   1136         )
   1137         try:
   1138             run_setup(setup_script, args)
   1139         except SystemExit as v:
   1140             raise DistutilsError("Setup script exited with %s" % (v.args[0],))
   1141 
   1142     def build_and_install(self, setup_script, setup_base):
   1143         args = ['bdist_egg', '--dist-dir']
   1144 
   1145         dist_dir = tempfile.mkdtemp(
   1146             prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script)
   1147         )
   1148         try:
   1149             self._set_fetcher_options(os.path.dirname(setup_script))
   1150             args.append(dist_dir)
   1151 
   1152             self.run_setup(setup_script, setup_base, args)
   1153             all_eggs = Environment([dist_dir])
   1154             eggs = []
   1155             for key in all_eggs:
   1156                 for dist in all_eggs[key]:
   1157                     eggs.append(self.install_egg(dist.location, setup_base))
   1158             if not eggs and not self.dry_run:
   1159                 log.warn("No eggs found in %s (setup script problem?)",
   1160                          dist_dir)
   1161             return eggs
   1162         finally:
   1163             rmtree(dist_dir)
   1164             log.set_verbosity(self.verbose)  # restore our log verbosity
   1165 
   1166     def _set_fetcher_options(self, base):
   1167         """
   1168         When easy_install is about to run bdist_egg on a source dist, that
   1169         source dist might have 'setup_requires' directives, requiring
   1170         additional fetching. Ensure the fetcher options given to easy_install
   1171         are available to that command as well.
   1172         """
   1173         # find the fetch options from easy_install and write them out
   1174         # to the setup.cfg file.
   1175         ei_opts = self.distribution.get_option_dict('easy_install').copy()
   1176         fetch_directives = (
   1177             'find_links', 'site_dirs', 'index_url', 'optimize',
   1178             'site_dirs', 'allow_hosts',
   1179         )
   1180         fetch_options = {}
   1181         for key, val in ei_opts.items():
   1182             if key not in fetch_directives:
   1183                 continue
   1184             fetch_options[key.replace('_', '-')] = val[1]
   1185         # create a settings dictionary suitable for `edit_config`
   1186         settings = dict(easy_install=fetch_options)
   1187         cfg_filename = os.path.join(base, 'setup.cfg')
   1188         setopt.edit_config(cfg_filename, settings)
   1189 
   1190     def update_pth(self, dist):
   1191         if self.pth_file is None:
   1192             return
   1193 
   1194         for d in self.pth_file[dist.key]:  # drop old entries
   1195             if self.multi_version or d.location != dist.location:
   1196                 log.info("Removing %s from easy-install.pth file", d)
   1197                 self.pth_file.remove(d)
   1198                 if d.location in self.shadow_path:
   1199                     self.shadow_path.remove(d.location)
   1200 
   1201         if not self.multi_version:
   1202             if dist.location in self.pth_file.paths:
   1203                 log.info(
   1204                     "%s is already the active version in easy-install.pth",
   1205                     dist,
   1206                 )
   1207             else:
   1208                 log.info("Adding %s to easy-install.pth file", dist)
   1209                 self.pth_file.add(dist)  # add new entry
   1210                 if dist.location not in self.shadow_path:
   1211                     self.shadow_path.append(dist.location)
   1212 
   1213         if not self.dry_run:
   1214 
   1215             self.pth_file.save()
   1216 
   1217             if dist.key == 'setuptools':
   1218                 # Ensure that setuptools itself never becomes unavailable!
   1219                 # XXX should this check for latest version?
   1220                 filename = os.path.join(self.install_dir, 'setuptools.pth')
   1221                 if os.path.islink(filename):
   1222                     os.unlink(filename)
   1223                 f = open(filename, 'wt')
   1224                 f.write(self.pth_file.make_relative(dist.location) + '\n')
   1225                 f.close()
   1226 
   1227     def unpack_progress(self, src, dst):
   1228         # Progress filter for unpacking
   1229         log.debug("Unpacking %s to %s", src, dst)
   1230         return dst  # only unpack-and-compile skips files for dry run
   1231 
   1232     def unpack_and_compile(self, egg_path, destination):
   1233         to_compile = []
   1234         to_chmod = []
   1235 
   1236         def pf(src, dst):
   1237             if dst.endswith('.py') and not src.startswith('EGG-INFO/'):
   1238                 to_compile.append(dst)
   1239             elif dst.endswith('.dll') or dst.endswith('.so'):
   1240                 to_chmod.append(dst)
   1241             self.unpack_progress(src, dst)
   1242             return not self.dry_run and dst or None
   1243 
   1244         unpack_archive(egg_path, destination, pf)
   1245         self.byte_compile(to_compile)
   1246         if not self.dry_run:
   1247             for f in to_chmod:
   1248                 mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755
   1249                 chmod(f, mode)
   1250 
   1251     def byte_compile(self, to_compile):
   1252         if sys.dont_write_bytecode:
   1253             return
   1254 
   1255         from distutils.util import byte_compile
   1256 
   1257         try:
   1258             # try to make the byte compile messages quieter
   1259             log.set_verbosity(self.verbose - 1)
   1260 
   1261             byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run)
   1262             if self.optimize:
   1263                 byte_compile(
   1264                     to_compile, optimize=self.optimize, force=1,
   1265                     dry_run=self.dry_run,
   1266                 )
   1267         finally:
   1268             log.set_verbosity(self.verbose)  # restore original verbosity
   1269 
   1270     __no_default_msg = textwrap.dedent("""
   1271         bad install directory or PYTHONPATH
   1272 
   1273         You are attempting to install a package to a directory that is not
   1274         on PYTHONPATH and which Python does not read ".pth" files from.  The
   1275         installation directory you specified (via --install-dir, --prefix, or
   1276         the distutils default setting) was:
   1277 
   1278             %s
   1279 
   1280         and your PYTHONPATH environment variable currently contains:
   1281 
   1282             %r
   1283 
   1284         Here are some of your options for correcting the problem:
   1285 
   1286         * You can choose a different installation directory, i.e., one that is
   1287           on PYTHONPATH or supports .pth files
   1288 
   1289         * You can add the installation directory to the PYTHONPATH environment
   1290           variable.  (It must then also be on PYTHONPATH whenever you run
   1291           Python and want to use the package(s) you are installing.)
   1292 
   1293         * You can set up the installation directory to support ".pth" files by
   1294           using one of the approaches described here:
   1295 
   1296           https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations
   1297 
   1298 
   1299         Please make the appropriate changes for your system and try again.""").lstrip()
   1300 
   1301     def no_default_version_msg(self):
   1302         template = self.__no_default_msg
   1303         return template % (self.install_dir, os.environ.get('PYTHONPATH', ''))
   1304 
   1305     def install_site_py(self):
   1306         """Make sure there's a site.py in the target dir, if needed"""
   1307 
   1308         if self.sitepy_installed:
   1309             return  # already did it, or don't need to
   1310 
   1311         sitepy = os.path.join(self.install_dir, "site.py")
   1312         source = resource_string("setuptools", "site-patch.py")
   1313         source = source.decode('utf-8')
   1314         current = ""
   1315 
   1316         if os.path.exists(sitepy):
   1317             log.debug("Checking existing site.py in %s", self.install_dir)
   1318             with io.open(sitepy) as strm:
   1319                 current = strm.read()
   1320 
   1321             if not current.startswith('def __boot():'):
   1322                 raise DistutilsError(
   1323                     "%s is not a setuptools-generated site.py; please"
   1324                     " remove it." % sitepy
   1325                 )
   1326 
   1327         if current != source:
   1328             log.info("Creating %s", sitepy)
   1329             if not self.dry_run:
   1330                 ensure_directory(sitepy)
   1331                 with io.open(sitepy, 'w', encoding='utf-8') as strm:
   1332                     strm.write(source)
   1333             self.byte_compile([sitepy])
   1334 
   1335         self.sitepy_installed = True
   1336 
   1337     def create_home_path(self):
   1338         """Create directories under ~."""
   1339         if not self.user:
   1340             return
   1341         home = convert_path(os.path.expanduser("~"))
   1342         for name, path in six.iteritems(self.config_vars):
   1343             if path.startswith(home) and not os.path.isdir(path):
   1344                 self.debug_print("os.makedirs('%s', 0o700)" % path)
   1345                 os.makedirs(path, 0o700)
   1346 
   1347     INSTALL_SCHEMES = dict(
   1348         posix=dict(
   1349             install_dir='$base/lib/python$py_version_short/site-packages',
   1350             script_dir='$base/bin',
   1351         ),
   1352     )
   1353 
   1354     DEFAULT_SCHEME = dict(
   1355         install_dir='$base/Lib/site-packages',
   1356         script_dir='$base/Scripts',
   1357     )
   1358 
   1359     def _expand(self, *attrs):
   1360         config_vars = self.get_finalized_command('install').config_vars
   1361 
   1362         if self.prefix:
   1363             # Set default install_dir/scripts from --prefix
   1364             config_vars = config_vars.copy()
   1365             config_vars['base'] = self.prefix
   1366             scheme = self.INSTALL_SCHEMES.get(os.name, self.DEFAULT_SCHEME)
   1367             for attr, val in scheme.items():
   1368                 if getattr(self, attr, None) is None:
   1369                     setattr(self, attr, val)
   1370 
   1371         from distutils.util import subst_vars
   1372 
   1373         for attr in attrs:
   1374             val = getattr(self, attr)
   1375             if val is not None:
   1376                 val = subst_vars(val, config_vars)
   1377                 if os.name == 'posix':
   1378                     val = os.path.expanduser(val)
   1379                 setattr(self, attr, val)
   1380 
   1381 
   1382 def _pythonpath():
   1383     items = os.environ.get('PYTHONPATH', '').split(os.pathsep)
   1384     return filter(None, items)
   1385 
   1386 
   1387 def get_site_dirs():
   1388     """
   1389     Return a list of 'site' dirs
   1390     """
   1391 
   1392     sitedirs = []
   1393 
   1394     # start with PYTHONPATH
   1395     sitedirs.extend(_pythonpath())
   1396 
   1397     prefixes = [sys.prefix]
   1398     if sys.exec_prefix != sys.prefix:
   1399         prefixes.append(sys.exec_prefix)
   1400     for prefix in prefixes:
   1401         if prefix:
   1402             if sys.platform in ('os2emx', 'riscos'):
   1403                 sitedirs.append(os.path.join(prefix, "Lib", "site-packages"))
   1404             elif os.sep == '/':
   1405                 sitedirs.extend([
   1406                     os.path.join(
   1407                         prefix,
   1408                         "lib",
   1409                         "python" + sys.version[:3],
   1410                         "site-packages",
   1411                     ),
   1412                     os.path.join(prefix, "lib", "site-python"),
   1413                 ])
   1414             else:
   1415                 sitedirs.extend([
   1416                     prefix,
   1417                     os.path.join(prefix, "lib", "site-packages"),
   1418                 ])
   1419             if sys.platform == 'darwin':
   1420                 # for framework builds *only* we add the standard Apple
   1421                 # locations. Currently only per-user, but /Library and
   1422                 # /Network/Library could be added too
   1423                 if 'Python.framework' in prefix:
   1424                     home = os.environ.get('HOME')
   1425                     if home:
   1426                         home_sp = os.path.join(
   1427                             home,
   1428                             'Library',
   1429                             'Python',
   1430                             sys.version[:3],
   1431                             'site-packages',
   1432                         )
   1433                         sitedirs.append(home_sp)
   1434     lib_paths = get_path('purelib'), get_path('platlib')
   1435     for site_lib in lib_paths:
   1436         if site_lib not in sitedirs:
   1437             sitedirs.append(site_lib)
   1438 
   1439     if site.ENABLE_USER_SITE:
   1440         sitedirs.append(site.USER_SITE)
   1441 
   1442     try:
   1443         sitedirs.extend(site.getsitepackages())
   1444     except AttributeError:
   1445         pass
   1446 
   1447     sitedirs = list(map(normalize_path, sitedirs))
   1448 
   1449     return sitedirs
   1450 
   1451 
   1452 def expand_paths(inputs):
   1453     """Yield sys.path directories that might contain "old-style" packages"""
   1454 
   1455     seen = {}
   1456 
   1457     for dirname in inputs:
   1458         dirname = normalize_path(dirname)
   1459         if dirname in seen:
   1460             continue
   1461 
   1462         seen[dirname] = 1
   1463         if not os.path.isdir(dirname):
   1464             continue
   1465 
   1466         files = os.listdir(dirname)
   1467         yield dirname, files
   1468 
   1469         for name in files:
   1470             if not name.endswith('.pth'):
   1471                 # We only care about the .pth files
   1472                 continue
   1473             if name in ('easy-install.pth', 'setuptools.pth'):
   1474                 # Ignore .pth files that we control
   1475                 continue
   1476 
   1477             # Read the .pth file
   1478             f = open(os.path.join(dirname, name))
   1479             lines = list(yield_lines(f))
   1480             f.close()
   1481 
   1482             # Yield existing non-dupe, non-import directory lines from it
   1483             for line in lines:
   1484                 if not line.startswith("import"):
   1485                     line = normalize_path(line.rstrip())
   1486                     if line not in seen:
   1487                         seen[line] = 1
   1488                         if not os.path.isdir(line):
   1489                             continue
   1490                         yield line, os.listdir(line)
   1491 
   1492 
   1493 def extract_wininst_cfg(dist_filename):
   1494     """Extract configuration data from a bdist_wininst .exe
   1495 
   1496     Returns a configparser.RawConfigParser, or None
   1497     """
   1498     f = open(dist_filename, 'rb')
   1499     try:
   1500         endrec = zipfile._EndRecData(f)
   1501         if endrec is None:
   1502             return None
   1503 
   1504         prepended = (endrec[9] - endrec[5]) - endrec[6]
   1505         if prepended < 12:  # no wininst data here
   1506             return None
   1507         f.seek(prepended - 12)
   1508 
   1509         tag, cfglen, bmlen = struct.unpack("<iii", f.read(12))
   1510         if tag not in (0x1234567A, 0x1234567B):
   1511             return None  # not a valid tag
   1512 
   1513         f.seek(prepended - (12 + cfglen))
   1514         init = {'version': '', 'target_version': ''}
   1515         cfg = configparser.RawConfigParser(init)
   1516         try:
   1517             part = f.read(cfglen)
   1518             # Read up to the first null byte.
   1519             config = part.split(b'\0', 1)[0]
   1520             # Now the config is in bytes, but for RawConfigParser, it should
   1521             #  be text, so decode it.
   1522             config = config.decode(sys.getfilesystemencoding())
   1523             cfg.readfp(six.StringIO(config))
   1524         except configparser.Error:
   1525             return None
   1526         if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
   1527             return None
   1528         return cfg
   1529 
   1530     finally:
   1531         f.close()
   1532 
   1533 
   1534 def get_exe_prefixes(exe_filename):
   1535     """Get exe->egg path translations for a given .exe file"""
   1536 
   1537     prefixes = [
   1538         ('PURELIB/', ''),
   1539         ('PLATLIB/pywin32_system32', ''),
   1540         ('PLATLIB/', ''),
   1541         ('SCRIPTS/', 'EGG-INFO/scripts/'),
   1542         ('DATA/lib/site-packages', ''),
   1543     ]
   1544     z = zipfile.ZipFile(exe_filename)
   1545     try:
   1546         for info in z.infolist():
   1547             name = info.filename
   1548             parts = name.split('/')
   1549             if len(parts) == 3 and parts[2] == 'PKG-INFO':
   1550                 if parts[1].endswith('.egg-info'):
   1551                     prefixes.insert(0, ('/'.join(parts[:2]), 'EGG-INFO/'))
   1552                     break
   1553             if len(parts) != 2 or not name.endswith('.pth'):
   1554                 continue
   1555             if name.endswith('-nspkg.pth'):
   1556                 continue
   1557             if parts[0].upper() in ('PURELIB', 'PLATLIB'):
   1558                 contents = z.read(name)
   1559                 if six.PY3:
   1560                     contents = contents.decode()
   1561                 for pth in yield_lines(contents):
   1562                     pth = pth.strip().replace('\\', '/')
   1563                     if not pth.startswith('import'):
   1564                         prefixes.append((('%s/%s/' % (parts[0], pth)), ''))
   1565     finally:
   1566         z.close()
   1567     prefixes = [(x.lower(), y) for x, y in prefixes]
   1568     prefixes.sort()
   1569     prefixes.reverse()
   1570     return prefixes
   1571 
   1572 
   1573 class PthDistributions(Environment):
   1574     """A .pth file with Distribution paths in it"""
   1575 
   1576     dirty = False
   1577 
   1578     def __init__(self, filename, sitedirs=()):
   1579         self.filename = filename
   1580         self.sitedirs = list(map(normalize_path, sitedirs))
   1581         self.basedir = normalize_path(os.path.dirname(self.filename))
   1582         self._load()
   1583         Environment.__init__(self, [], None, None)
   1584         for path in yield_lines(self.paths):
   1585             list(map(self.add, find_distributions(path, True)))
   1586 
   1587     def _load(self):
   1588         self.paths = []
   1589         saw_import = False
   1590         seen = dict.fromkeys(self.sitedirs)
   1591         if os.path.isfile(self.filename):
   1592             f = open(self.filename, 'rt')
   1593             for line in f:
   1594                 if line.startswith('import'):
   1595                     saw_import = True
   1596                     continue
   1597                 path = line.rstrip()
   1598                 self.paths.append(path)
   1599                 if not path.strip() or path.strip().startswith('#'):
   1600                     continue
   1601                 # skip non-existent paths, in case somebody deleted a package
   1602                 # manually, and duplicate paths as well
   1603                 path = self.paths[-1] = normalize_path(
   1604                     os.path.join(self.basedir, path)
   1605                 )
   1606                 if not os.path.exists(path) or path in seen:
   1607                     self.paths.pop()  # skip it
   1608                     self.dirty = True  # we cleaned up, so we're dirty now :)
   1609                     continue
   1610                 seen[path] = 1
   1611             f.close()
   1612 
   1613         if self.paths and not saw_import:
   1614             self.dirty = True  # ensure anything we touch has import wrappers
   1615         while self.paths and not self.paths[-1].strip():
   1616             self.paths.pop()
   1617 
   1618     def save(self):
   1619         """Write changed .pth file back to disk"""
   1620         if not self.dirty:
   1621             return
   1622 
   1623         rel_paths = list(map(self.make_relative, self.paths))
   1624         if rel_paths:
   1625             log.debug("Saving %s", self.filename)
   1626             lines = self._wrap_lines(rel_paths)
   1627             data = '\n'.join(lines) + '\n'
   1628 
   1629             if os.path.islink(self.filename):
   1630                 os.unlink(self.filename)
   1631             with open(self.filename, 'wt') as f:
   1632                 f.write(data)
   1633 
   1634         elif os.path.exists(self.filename):
   1635             log.debug("Deleting empty %s", self.filename)
   1636             os.unlink(self.filename)
   1637 
   1638         self.dirty = False
   1639 
   1640     @staticmethod
   1641     def _wrap_lines(lines):
   1642         return lines
   1643 
   1644     def add(self, dist):
   1645         """Add `dist` to the distribution map"""
   1646         new_path = (
   1647             dist.location not in self.paths and (
   1648                 dist.location not in self.sitedirs or
   1649                 # account for '.' being in PYTHONPATH
   1650                 dist.location == os.getcwd()
   1651             )
   1652         )
   1653         if new_path:
   1654             self.paths.append(dist.location)
   1655             self.dirty = True
   1656         Environment.add(self, dist)
   1657 
   1658     def remove(self, dist):
   1659         """Remove `dist` from the distribution map"""
   1660         while dist.location in self.paths:
   1661             self.paths.remove(dist.location)
   1662             self.dirty = True
   1663         Environment.remove(self, dist)
   1664 
   1665     def make_relative(self, path):
   1666         npath, last = os.path.split(normalize_path(path))
   1667         baselen = len(self.basedir)
   1668         parts = [last]
   1669         sep = os.altsep == '/' and '/' or os.sep
   1670         while len(npath) >= baselen:
   1671             if npath == self.basedir:
   1672                 parts.append(os.curdir)
   1673                 parts.reverse()
   1674                 return sep.join(parts)
   1675             npath, last = os.path.split(npath)
   1676             parts.append(last)
   1677         else:
   1678             return path
   1679 
   1680 
   1681 class RewritePthDistributions(PthDistributions):
   1682     @classmethod
   1683     def _wrap_lines(cls, lines):
   1684         yield cls.prelude
   1685         for line in lines:
   1686             yield line
   1687         yield cls.postlude
   1688 
   1689     prelude = _one_liner("""
   1690         import sys
   1691         sys.__plen = len(sys.path)
   1692         """)
   1693     postlude = _one_liner("""
   1694         import sys
   1695         new = sys.path[sys.__plen:]
   1696         del sys.path[sys.__plen:]
   1697         p = getattr(sys, '__egginsert', 0)
   1698         sys.path[p:p] = new
   1699         sys.__egginsert = p + len(new)
   1700         """)
   1701 
   1702 
   1703 if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite':
   1704     PthDistributions = RewritePthDistributions
   1705 
   1706 
   1707 def _first_line_re():
   1708     """
   1709     Return a regular expression based on first_line_re suitable for matching
   1710     strings.
   1711     """
   1712     if isinstance(first_line_re.pattern, str):
   1713         return first_line_re
   1714 
   1715     # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.
   1716     return re.compile(first_line_re.pattern.decode())
   1717 
   1718 
   1719 def auto_chmod(func, arg, exc):
   1720     if func in [os.unlink, os.remove] and os.name == 'nt':
   1721         chmod(arg, stat.S_IWRITE)
   1722         return func(arg)
   1723     et, ev, _ = sys.exc_info()
   1724     six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg))))
   1725 
   1726 
   1727 def update_dist_caches(dist_path, fix_zipimporter_caches):
   1728     """
   1729     Fix any globally cached `dist_path` related data
   1730 
   1731     `dist_path` should be a path of a newly installed egg distribution (zipped
   1732     or unzipped).
   1733 
   1734     sys.path_importer_cache contains finder objects that have been cached when
   1735     importing data from the original distribution. Any such finders need to be
   1736     cleared since the replacement distribution might be packaged differently,
   1737     e.g. a zipped egg distribution might get replaced with an unzipped egg
   1738     folder or vice versa. Having the old finders cached may then cause Python
   1739     to attempt loading modules from the replacement distribution using an
   1740     incorrect loader.
   1741 
   1742     zipimport.zipimporter objects are Python loaders charged with importing
   1743     data packaged inside zip archives. If stale loaders referencing the
   1744     original distribution, are left behind, they can fail to load modules from
   1745     the replacement distribution. E.g. if an old zipimport.zipimporter instance
   1746     is used to load data from a new zipped egg archive, it may cause the
   1747     operation to attempt to locate the requested data in the wrong location -
   1748     one indicated by the original distribution's zip archive directory
   1749     information. Such an operation may then fail outright, e.g. report having
   1750     read a 'bad local file header', or even worse, it may fail silently &
   1751     return invalid data.
   1752 
   1753     zipimport._zip_directory_cache contains cached zip archive directory
   1754     information for all existing zipimport.zipimporter instances and all such
   1755     instances connected to the same archive share the same cached directory
   1756     information.
   1757 
   1758     If asked, and the underlying Python implementation allows it, we can fix
   1759     all existing zipimport.zipimporter instances instead of having to track
   1760     them down and remove them one by one, by updating their shared cached zip
   1761     archive directory information. This, of course, assumes that the
   1762     replacement distribution is packaged as a zipped egg.
   1763 
   1764     If not asked to fix existing zipimport.zipimporter instances, we still do
   1765     our best to clear any remaining zipimport.zipimporter related cached data
   1766     that might somehow later get used when attempting to load data from the new
   1767     distribution and thus cause such load operations to fail. Note that when
   1768     tracking down such remaining stale data, we can not catch every conceivable
   1769     usage from here, and we clear only those that we know of and have found to
   1770     cause problems if left alive. Any remaining caches should be updated by
   1771     whomever is in charge of maintaining them, i.e. they should be ready to
   1772     handle us replacing their zip archives with new distributions at runtime.
   1773 
   1774     """
   1775     # There are several other known sources of stale zipimport.zipimporter
   1776     # instances that we do not clear here, but might if ever given a reason to
   1777     # do so:
   1778     # * Global setuptools pkg_resources.working_set (a.k.a. 'master working
   1779     # set') may contain distributions which may in turn contain their
   1780     #   zipimport.zipimporter loaders.
   1781     # * Several zipimport.zipimporter loaders held by local variables further
   1782     #   up the function call stack when running the setuptools installation.
   1783     # * Already loaded modules may have their __loader__ attribute set to the
   1784     #   exact loader instance used when importing them. Python 3.4 docs state
   1785     #   that this information is intended mostly for introspection and so is
   1786     #   not expected to cause us problems.
   1787     normalized_path = normalize_path(dist_path)
   1788     _uncache(normalized_path, sys.path_importer_cache)
   1789     if fix_zipimporter_caches:
   1790         _replace_zip_directory_cache_data(normalized_path)
   1791     else:
   1792         # Here, even though we do not want to fix existing and now stale
   1793         # zipimporter cache information, we still want to remove it. Related to
   1794         # Python's zip archive directory information cache, we clear each of
   1795         # its stale entries in two phases:
   1796         #   1. Clear the entry so attempting to access zip archive information
   1797         #      via any existing stale zipimport.zipimporter instances fails.
   1798         #   2. Remove the entry from the cache so any newly constructed
   1799         #      zipimport.zipimporter instances do not end up using old stale
   1800         #      zip archive directory information.
   1801         # This whole stale data removal step does not seem strictly necessary,
   1802         # but has been left in because it was done before we started replacing
   1803         # the zip archive directory information cache content if possible, and
   1804         # there are no relevant unit tests that we can depend on to tell us if
   1805         # this is really needed.
   1806         _remove_and_clear_zip_directory_cache_data(normalized_path)
   1807 
   1808 
   1809 def _collect_zipimporter_cache_entries(normalized_path, cache):
   1810     """
   1811     Return zipimporter cache entry keys related to a given normalized path.
   1812 
   1813     Alternative path spellings (e.g. those using different character case or
   1814     those using alternative path separators) related to the same path are
   1815     included. Any sub-path entries are included as well, i.e. those
   1816     corresponding to zip archives embedded in other zip archives.
   1817 
   1818     """
   1819     result = []
   1820     prefix_len = len(normalized_path)
   1821     for p in cache:
   1822         np = normalize_path(p)
   1823         if (np.startswith(normalized_path) and
   1824                 np[prefix_len:prefix_len + 1] in (os.sep, '')):
   1825             result.append(p)
   1826     return result
   1827 
   1828 
   1829 def _update_zipimporter_cache(normalized_path, cache, updater=None):
   1830     """
   1831     Update zipimporter cache data for a given normalized path.
   1832 
   1833     Any sub-path entries are processed as well, i.e. those corresponding to zip
   1834     archives embedded in other zip archives.
   1835 
   1836     Given updater is a callable taking a cache entry key and the original entry
   1837     (after already removing the entry from the cache), and expected to update
   1838     the entry and possibly return a new one to be inserted in its place.
   1839     Returning None indicates that the entry should not be replaced with a new
   1840     one. If no updater is given, the cache entries are simply removed without
   1841     any additional processing, the same as if the updater simply returned None.
   1842 
   1843     """
   1844     for p in _collect_zipimporter_cache_entries(normalized_path, cache):
   1845         # N.B. pypy's custom zipimport._zip_directory_cache implementation does
   1846         # not support the complete dict interface:
   1847         # * Does not support item assignment, thus not allowing this function
   1848         #    to be used only for removing existing cache entries.
   1849         #  * Does not support the dict.pop() method, forcing us to use the
   1850         #    get/del patterns instead. For more detailed information see the
   1851         #    following links:
   1852         #      https://github.com/pypa/setuptools/issues/202#issuecomment-202913420
   1853         #      http://bit.ly/2h9itJX
   1854         old_entry = cache[p]
   1855         del cache[p]
   1856         new_entry = updater and updater(p, old_entry)
   1857         if new_entry is not None:
   1858             cache[p] = new_entry
   1859 
   1860 
   1861 def _uncache(normalized_path, cache):
   1862     _update_zipimporter_cache(normalized_path, cache)
   1863 
   1864 
   1865 def _remove_and_clear_zip_directory_cache_data(normalized_path):
   1866     def clear_and_remove_cached_zip_archive_directory_data(path, old_entry):
   1867         old_entry.clear()
   1868 
   1869     _update_zipimporter_cache(
   1870         normalized_path, zipimport._zip_directory_cache,
   1871         updater=clear_and_remove_cached_zip_archive_directory_data)
   1872 
   1873 
   1874 # PyPy Python implementation does not allow directly writing to the
   1875 # zipimport._zip_directory_cache and so prevents us from attempting to correct
   1876 # its content. The best we can do there is clear the problematic cache content
   1877 # and have PyPy repopulate it as needed. The downside is that if there are any
   1878 # stale zipimport.zipimporter instances laying around, attempting to use them
   1879 # will fail due to not having its zip archive directory information available
   1880 # instead of being automatically corrected to use the new correct zip archive
   1881 # directory information.
   1882 if '__pypy__' in sys.builtin_module_names:
   1883     _replace_zip_directory_cache_data = \
   1884         _remove_and_clear_zip_directory_cache_data
   1885 else:
   1886 
   1887     def _replace_zip_directory_cache_data(normalized_path):
   1888         def replace_cached_zip_archive_directory_data(path, old_entry):
   1889             # N.B. In theory, we could load the zip directory information just
   1890             # once for all updated path spellings, and then copy it locally and
   1891             # update its contained path strings to contain the correct
   1892             # spelling, but that seems like a way too invasive move (this cache
   1893             # structure is not officially documented anywhere and could in
   1894             # theory change with new Python releases) for no significant
   1895             # benefit.
   1896             old_entry.clear()
   1897             zipimport.zipimporter(path)
   1898             old_entry.update(zipimport._zip_directory_cache[path])
   1899             return old_entry
   1900 
   1901         _update_zipimporter_cache(
   1902             normalized_path, zipimport._zip_directory_cache,
   1903             updater=replace_cached_zip_archive_directory_data)
   1904 
   1905 
   1906 def is_python(text, filename='<string>'):
   1907     "Is this string a valid Python script?"
   1908     try:
   1909         compile(text, filename, 'exec')
   1910     except (SyntaxError, TypeError):
   1911         return False
   1912     else:
   1913         return True
   1914 
   1915 
   1916 def is_sh(executable):
   1917     """Determine if the specified executable is a .sh (contains a #! line)"""
   1918     try:
   1919         with io.open(executable, encoding='latin-1') as fp:
   1920             magic = fp.read(2)
   1921     except (OSError, IOError):
   1922         return executable
   1923     return magic == '#!'
   1924 
   1925 
   1926 def nt_quote_arg(arg):
   1927     """Quote a command line argument according to Windows parsing rules"""
   1928     return subprocess.list2cmdline([arg])
   1929 
   1930 
   1931 def is_python_script(script_text, filename):
   1932     """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.
   1933     """
   1934     if filename.endswith('.py') or filename.endswith('.pyw'):
   1935         return True  # extension says it's Python
   1936     if is_python(script_text, filename):
   1937         return True  # it's syntactically valid Python
   1938     if script_text.startswith('#!'):
   1939         # It begins with a '#!' line, so check if 'python' is in it somewhere
   1940         return 'python' in script_text.splitlines()[0].lower()
   1941 
   1942     return False  # Not any Python I can recognize
   1943 
   1944 
   1945 try:
   1946     from os import chmod as _chmod
   1947 except ImportError:
   1948     # Jython compatibility
   1949     def _chmod(*args):
   1950         pass
   1951 
   1952 
   1953 def chmod(path, mode):
   1954     log.debug("changing mode of %s to %o", path, mode)
   1955     try:
   1956         _chmod(path, mode)
   1957     except os.error as e:
   1958         log.debug("chmod failed: %s", e)
   1959 
   1960 
   1961 class CommandSpec(list):
   1962     """
   1963     A command spec for a #! header, specified as a list of arguments akin to
   1964     those passed to Popen.
   1965     """
   1966 
   1967     options = []
   1968     split_args = dict()
   1969 
   1970     @classmethod
   1971     def best(cls):
   1972         """
   1973         Choose the best CommandSpec class based on environmental conditions.
   1974         """
   1975         return cls
   1976 
   1977     @classmethod
   1978     def _sys_executable(cls):
   1979         _default = os.path.normpath(sys.executable)
   1980         return os.environ.get('__PYVENV_LAUNCHER__', _default)
   1981 
   1982     @classmethod
   1983     def from_param(cls, param):
   1984         """
   1985         Construct a CommandSpec from a parameter to build_scripts, which may
   1986         be None.
   1987         """
   1988         if isinstance(param, cls):
   1989             return param
   1990         if isinstance(param, list):
   1991             return cls(param)
   1992         if param is None:
   1993             return cls.from_environment()
   1994         # otherwise, assume it's a string.
   1995         return cls.from_string(param)
   1996 
   1997     @classmethod
   1998     def from_environment(cls):
   1999         return cls([cls._sys_executable()])
   2000 
   2001     @classmethod
   2002     def from_string(cls, string):
   2003         """
   2004         Construct a command spec from a simple string representing a command
   2005         line parseable by shlex.split.
   2006         """
   2007         items = shlex.split(string, **cls.split_args)
   2008         return cls(items)
   2009 
   2010     def install_options(self, script_text):
   2011         self.options = shlex.split(self._extract_options(script_text))
   2012         cmdline = subprocess.list2cmdline(self)
   2013         if not isascii(cmdline):
   2014             self.options[:0] = ['-x']
   2015 
   2016     @staticmethod
   2017     def _extract_options(orig_script):
   2018         """
   2019         Extract any options from the first line of the script.
   2020         """
   2021         first = (orig_script + '\n').splitlines()[0]
   2022         match = _first_line_re().match(first)
   2023         options = match.group(1) or '' if match else ''
   2024         return options.strip()
   2025 
   2026     def as_header(self):
   2027         return self._render(self + list(self.options))
   2028 
   2029     @staticmethod
   2030     def _strip_quotes(item):
   2031         _QUOTES = '"\''
   2032         for q in _QUOTES:
   2033             if item.startswith(q) and item.endswith(q):
   2034                 return item[1:-1]
   2035         return item
   2036 
   2037     @staticmethod
   2038     def _render(items):
   2039         cmdline = subprocess.list2cmdline(
   2040             CommandSpec._strip_quotes(item.strip()) for item in items)
   2041         return '#!' + cmdline + '\n'
   2042 
   2043 
   2044 # For pbr compat; will be removed in a future version.
   2045 sys_executable = CommandSpec._sys_executable()
   2046 
   2047 
   2048 class WindowsCommandSpec(CommandSpec):
   2049     split_args = dict(posix=False)
   2050 
   2051 
   2052 class ScriptWriter(object):
   2053     """
   2054     Encapsulates behavior around writing entry point scripts for console and
   2055     gui apps.
   2056     """
   2057 
   2058     template = textwrap.dedent(r"""
   2059         # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
   2060         __requires__ = %(spec)r
   2061         import re
   2062         import sys
   2063         from pkg_resources import load_entry_point
   2064 
   2065         if __name__ == '__main__':
   2066             sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
   2067             sys.exit(
   2068                 load_entry_point(%(spec)r, %(group)r, %(name)r)()
   2069             )
   2070     """).lstrip()
   2071 
   2072     command_spec_class = CommandSpec
   2073 
   2074     @classmethod
   2075     def get_script_args(cls, dist, executable=None, wininst=False):
   2076         # for backward compatibility
   2077         warnings.warn("Use get_args", DeprecationWarning)
   2078         writer = (WindowsScriptWriter if wininst else ScriptWriter).best()
   2079         header = cls.get_script_header("", executable, wininst)
   2080         return writer.get_args(dist, header)
   2081 
   2082     @classmethod
   2083     def get_script_header(cls, script_text, executable=None, wininst=False):
   2084         # for backward compatibility
   2085         warnings.warn("Use get_header", DeprecationWarning)
   2086         if wininst:
   2087             executable = "python.exe"
   2088         cmd = cls.command_spec_class.best().from_param(executable)
   2089         cmd.install_options(script_text)
   2090         return cmd.as_header()
   2091 
   2092     @classmethod
   2093     def get_args(cls, dist, header=None):
   2094         """
   2095         Yield write_script() argument tuples for a distribution's
   2096         console_scripts and gui_scripts entry points.
   2097         """
   2098         if header is None:
   2099             header = cls.get_header()
   2100         spec = str(dist.as_requirement())
   2101         for type_ in 'console', 'gui':
   2102             group = type_ + '_scripts'
   2103             for name, ep in dist.get_entry_map(group).items():
   2104                 cls._ensure_safe_name(name)
   2105                 script_text = cls.template % locals()
   2106                 args = cls._get_script_args(type_, name, header, script_text)
   2107                 for res in args:
   2108                     yield res
   2109 
   2110     @staticmethod
   2111     def _ensure_safe_name(name):
   2112         """
   2113         Prevent paths in *_scripts entry point names.
   2114         """
   2115         has_path_sep = re.search(r'[\\/]', name)
   2116         if has_path_sep:
   2117             raise ValueError("Path separators not allowed in script names")
   2118 
   2119     @classmethod
   2120     def get_writer(cls, force_windows):
   2121         # for backward compatibility
   2122         warnings.warn("Use best", DeprecationWarning)
   2123         return WindowsScriptWriter.best() if force_windows else cls.best()
   2124 
   2125     @classmethod
   2126     def best(cls):
   2127         """
   2128         Select the best ScriptWriter for this environment.
   2129         """
   2130         if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'):
   2131             return WindowsScriptWriter.best()
   2132         else:
   2133             return cls
   2134 
   2135     @classmethod
   2136     def _get_script_args(cls, type_, name, header, script_text):
   2137         # Simply write the stub with no extension.
   2138         yield (name, header + script_text)
   2139 
   2140     @classmethod
   2141     def get_header(cls, script_text="", executable=None):
   2142         """Create a #! line, getting options (if any) from script_text"""
   2143         cmd = cls.command_spec_class.best().from_param(executable)
   2144         cmd.install_options(script_text)
   2145         return cmd.as_header()
   2146 
   2147 
   2148 class WindowsScriptWriter(ScriptWriter):
   2149     command_spec_class = WindowsCommandSpec
   2150 
   2151     @classmethod
   2152     def get_writer(cls):
   2153         # for backward compatibility
   2154         warnings.warn("Use best", DeprecationWarning)
   2155         return cls.best()
   2156 
   2157     @classmethod
   2158     def best(cls):
   2159         """
   2160         Select the best ScriptWriter suitable for Windows
   2161         """
   2162         writer_lookup = dict(
   2163             executable=WindowsExecutableLauncherWriter,
   2164             natural=cls,
   2165         )
   2166         # for compatibility, use the executable launcher by default
   2167         launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable')
   2168         return writer_lookup[launcher]
   2169 
   2170     @classmethod
   2171     def _get_script_args(cls, type_, name, header, script_text):
   2172         "For Windows, add a .py extension"
   2173         ext = dict(console='.pya', gui='.pyw')[type_]
   2174         if ext not in os.environ['PATHEXT'].lower().split(';'):
   2175             msg = (
   2176                 "{ext} not listed in PATHEXT; scripts will not be "
   2177                 "recognized as executables."
   2178             ).format(**locals())
   2179             warnings.warn(msg, UserWarning)
   2180         old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe']
   2181         old.remove(ext)
   2182         header = cls._adjust_header(type_, header)
   2183         blockers = [name + x for x in old]
   2184         yield name + ext, header + script_text, 't', blockers
   2185 
   2186     @classmethod
   2187     def _adjust_header(cls, type_, orig_header):
   2188         """
   2189         Make sure 'pythonw' is used for gui and and 'python' is used for
   2190         console (regardless of what sys.executable is).
   2191         """
   2192         pattern = 'pythonw.exe'
   2193         repl = 'python.exe'
   2194         if type_ == 'gui':
   2195             pattern, repl = repl, pattern
   2196         pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE)
   2197         new_header = pattern_ob.sub(string=orig_header, repl=repl)
   2198         return new_header if cls._use_header(new_header) else orig_header
   2199 
   2200     @staticmethod
   2201     def _use_header(new_header):
   2202         """
   2203         Should _adjust_header use the replaced header?
   2204 
   2205         On non-windows systems, always use. On
   2206         Windows systems, only use the replaced header if it resolves
   2207         to an executable on the system.
   2208         """
   2209         clean_header = new_header[2:-1].strip('"')
   2210         return sys.platform != 'win32' or find_executable(clean_header)
   2211 
   2212 
   2213 class WindowsExecutableLauncherWriter(WindowsScriptWriter):
   2214     @classmethod
   2215     def _get_script_args(cls, type_, name, header, script_text):
   2216         """
   2217         For Windows, add a .py extension and an .exe launcher
   2218         """
   2219         if type_ == 'gui':
   2220             launcher_type = 'gui'
   2221             ext = '-script.pyw'
   2222             old = ['.pyw']
   2223         else:
   2224             launcher_type = 'cli'
   2225             ext = '-script.py'
   2226             old = ['.py', '.pyc', '.pyo']
   2227         hdr = cls._adjust_header(type_, header)
   2228         blockers = [name + x for x in old]
   2229         yield (name + ext, hdr + script_text, 't', blockers)
   2230         yield (
   2231             name + '.exe', get_win_launcher(launcher_type),
   2232             'b'  # write in binary mode
   2233         )
   2234         if not is_64bit():
   2235             # install a manifest for the launcher to prevent Windows
   2236             # from detecting it as an installer (which it will for
   2237             #  launchers like easy_install.exe). Consider only
   2238             #  adding a manifest for launchers detected as installers.
   2239             #  See Distribute #143 for details.
   2240             m_name = name + '.exe.manifest'
   2241             yield (m_name, load_launcher_manifest(name), 't')
   2242 
   2243 
   2244 # for backward-compatibility
   2245 get_script_args = ScriptWriter.get_script_args
   2246 get_script_header = ScriptWriter.get_script_header
   2247 
   2248 
   2249 def get_win_launcher(type):
   2250     """
   2251     Load the Windows launcher (executable) suitable for launching a script.
   2252 
   2253     `type` should be either 'cli' or 'gui'
   2254 
   2255     Returns the executable as a byte string.
   2256     """
   2257     launcher_fn = '%s.exe' % type
   2258     if is_64bit():
   2259         launcher_fn = launcher_fn.replace(".", "-64.")
   2260     else:
   2261         launcher_fn = launcher_fn.replace(".", "-32.")
   2262     return resource_string('setuptools', launcher_fn)
   2263 
   2264 
   2265 def load_launcher_manifest(name):
   2266     manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')
   2267     if six.PY2:
   2268         return manifest % vars()
   2269     else:
   2270         return manifest.decode('utf-8') % vars()
   2271 
   2272 
   2273 def rmtree(path, ignore_errors=False, onerror=auto_chmod):
   2274     return shutil.rmtree(path, ignore_errors, onerror)
   2275 
   2276 
   2277 def current_umask():
   2278     tmp = os.umask(0o022)
   2279     os.umask(tmp)
   2280     return tmp
   2281 
   2282 
   2283 def bootstrap():
   2284     # This function is called when setuptools*.egg is run using /bin/sh
   2285     import setuptools
   2286 
   2287     argv0 = os.path.dirname(setuptools.__path__[0])
   2288     sys.argv[0] = argv0
   2289     sys.argv.append(argv0)
   2290     main()
   2291 
   2292 
   2293 def main(argv=None, **kw):
   2294     from setuptools import setup
   2295     from setuptools.dist import Distribution
   2296 
   2297     class DistributionWithoutHelpCommands(Distribution):
   2298         common_usage = ""
   2299 
   2300         def _show_help(self, *args, **kw):
   2301             with _patch_usage():
   2302                 Distribution._show_help(self, *args, **kw)
   2303 
   2304     if argv is None:
   2305         argv = sys.argv[1:]
   2306 
   2307     with _patch_usage():
   2308         setup(
   2309             script_args=['-q', 'easy_install', '-v'] + argv,
   2310             script_name=sys.argv[0] or 'easy_install',
   2311             distclass=DistributionWithoutHelpCommands,
   2312             **kw
   2313         )
   2314 
   2315 
   2316 @contextlib.contextmanager
   2317 def _patch_usage():
   2318     import distutils.core
   2319     USAGE = textwrap.dedent("""
   2320         usage: %(script)s [options] requirement_or_url ...
   2321            or: %(script)s --help
   2322         """).lstrip()
   2323 
   2324     def gen_usage(script_name):
   2325         return USAGE % dict(
   2326             script=os.path.basename(script_name),
   2327         )
   2328 
   2329     saved = distutils.core.gen_usage
   2330     distutils.core.gen_usage = gen_usage
   2331     try:
   2332         yield
   2333     finally:
   2334         distutils.core.gen_usage = saved
   2335