Path: blob/master/venv/Lib/site-packages/setuptools/command/easy_install.py
811 views
"""1Easy Install2------------34A tool for doing automatic download/extract/build of distutils-based Python5packages. For detailed documentation, see the accompanying EasyInstall.txt6file, or visit the `EasyInstall home page`__.78__ https://setuptools.readthedocs.io/en/latest/easy_install.html910"""1112from glob import glob13from distutils.util import get_platform14from distutils.util import convert_path, subst_vars15from distutils.errors import (16DistutilsArgError, DistutilsOptionError,17DistutilsError, DistutilsPlatformError,18)19from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS20from distutils import log, dir_util21from distutils.command.build_scripts import first_line_re22from distutils.spawn import find_executable23import sys24import os25import zipimport26import shutil27import tempfile28import zipfile29import re30import stat31import random32import textwrap33import warnings34import site35import struct36import contextlib37import subprocess38import shlex39import io404142from sysconfig import get_config_vars, get_path4344from setuptools import SetuptoolsDeprecationWarning4546from setuptools.extern import six47from setuptools.extern.six.moves import configparser, map4849from setuptools import Command50from setuptools.sandbox import run_setup51from setuptools.py27compat import rmtree_safe52from setuptools.command import setopt53from setuptools.archive_util import unpack_archive54from setuptools.package_index import (55PackageIndex, parse_requirement_arg, URL_SCHEME,56)57from setuptools.command import bdist_egg, egg_info58from setuptools.wheel import Wheel59from pkg_resources import (60yield_lines, normalize_path, resource_string, ensure_directory,61get_distribution, find_distributions, Environment, Requirement,62Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound,63VersionConflict, DEVELOP_DIST,64)65import pkg_resources6667__metaclass__ = type6869# Turn on PEP440Warnings70warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)7172__all__ = [73'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',74'main', 'get_exe_prefixes',75]767778def is_64bit():79return struct.calcsize("P") == 8808182def samefile(p1, p2):83"""84Determine if two paths reference the same file.8586Augments os.path.samefile to work on Windows and87suppresses errors if the path doesn't exist.88"""89both_exist = os.path.exists(p1) and os.path.exists(p2)90use_samefile = hasattr(os.path, 'samefile') and both_exist91if use_samefile:92return os.path.samefile(p1, p2)93norm_p1 = os.path.normpath(os.path.normcase(p1))94norm_p2 = os.path.normpath(os.path.normcase(p2))95return norm_p1 == norm_p2969798if six.PY2:99100def _to_bytes(s):101return s102103def isascii(s):104try:105six.text_type(s, 'ascii')106return True107except UnicodeError:108return False109else:110111def _to_bytes(s):112return s.encode('utf8')113114def isascii(s):115try:116s.encode('ascii')117return True118except UnicodeError:119return False120121122def _one_liner(text):123return textwrap.dedent(text).strip().replace('\n', '; ')124125126class easy_install(Command):127"""Manage a download/build/install process"""128description = "Find/get/install Python packages"129command_consumes_arguments = True130131user_options = [132('prefix=', None, "installation prefix"),133("zip-ok", "z", "install package as a zipfile"),134("multi-version", "m", "make apps have to require() a version"),135("upgrade", "U", "force upgrade (searches PyPI for latest versions)"),136("install-dir=", "d", "install package to DIR"),137("script-dir=", "s", "install scripts to DIR"),138("exclude-scripts", "x", "Don't install scripts"),139("always-copy", "a", "Copy all needed packages to install dir"),140("index-url=", "i", "base URL of Python Package Index"),141("find-links=", "f", "additional URL(s) to search for packages"),142("build-directory=", "b",143"download/extract/build in DIR; keep the results"),144('optimize=', 'O',145"also compile with optimization: -O1 for \"python -O\", "146"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),147('record=', None,148"filename in which to record list of installed files"),149('always-unzip', 'Z', "don't install as a zipfile, no matter what"),150('site-dirs=', 'S', "list of directories where .pth files work"),151('editable', 'e', "Install specified packages in editable form"),152('no-deps', 'N', "don't install dependencies"),153('allow-hosts=', 'H', "pattern(s) that hostnames must match"),154('local-snapshots-ok', 'l',155"allow building eggs from local checkouts"),156('version', None, "print version information and exit"),157('no-find-links', None,158"Don't load find-links defined in packages being installed"),159('user', None, "install in user site-package '%s'" % site.USER_SITE)160]161boolean_options = [162'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',163'editable',164'no-deps', 'local-snapshots-ok', 'version',165'user'166]167168negative_opt = {'always-unzip': 'zip-ok'}169create_index = PackageIndex170171def initialize_options(self):172# the --user option seems to be an opt-in one,173# so the default should be False.174self.user = 0175self.zip_ok = self.local_snapshots_ok = None176self.install_dir = self.script_dir = self.exclude_scripts = None177self.index_url = None178self.find_links = None179self.build_directory = None180self.args = None181self.optimize = self.record = None182self.upgrade = self.always_copy = self.multi_version = None183self.editable = self.no_deps = self.allow_hosts = None184self.root = self.prefix = self.no_report = None185self.version = None186self.install_purelib = None # for pure module distributions187self.install_platlib = None # non-pure (dists w/ extensions)188self.install_headers = None # for C/C++ headers189self.install_lib = None # set to either purelib or platlib190self.install_scripts = None191self.install_data = None192self.install_base = None193self.install_platbase = None194if site.ENABLE_USER_SITE:195self.install_userbase = site.USER_BASE196self.install_usersite = site.USER_SITE197else:198self.install_userbase = None199self.install_usersite = None200self.no_find_links = None201202# Options not specifiable via command line203self.package_index = None204self.pth_file = self.always_copy_from = None205self.site_dirs = None206self.installed_projects = {}207# Always read easy_install options, even if we are subclassed, or have208# an independent instance created. This ensures that defaults will209# always come from the standard configuration file(s)' "easy_install"210# section, even if this is a "develop" or "install" command, or some211# other embedding.212self._dry_run = None213self.verbose = self.distribution.verbose214self.distribution._set_command_options(215self, self.distribution.get_option_dict('easy_install')216)217218def delete_blockers(self, blockers):219extant_blockers = (220filename for filename in blockers221if os.path.exists(filename) or os.path.islink(filename)222)223list(map(self._delete_path, extant_blockers))224225def _delete_path(self, path):226log.info("Deleting %s", path)227if self.dry_run:228return229230is_tree = os.path.isdir(path) and not os.path.islink(path)231remover = rmtree if is_tree else os.unlink232remover(path)233234@staticmethod235def _render_version():236"""237Render the Setuptools version and installation details, then exit.238"""239ver = '{}.{}'.format(*sys.version_info)240dist = get_distribution('setuptools')241tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'242print(tmpl.format(**locals()))243raise SystemExit()244245def finalize_options(self):246self.version and self._render_version()247248py_version = sys.version.split()[0]249prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')250251self.config_vars = {252'dist_name': self.distribution.get_name(),253'dist_version': self.distribution.get_version(),254'dist_fullname': self.distribution.get_fullname(),255'py_version': py_version,256'py_version_short': py_version[0:3],257'py_version_nodot': py_version[0] + py_version[2],258'sys_prefix': prefix,259'prefix': prefix,260'sys_exec_prefix': exec_prefix,261'exec_prefix': exec_prefix,262# Only python 3.2+ has abiflags263'abiflags': getattr(sys, 'abiflags', ''),264}265266if site.ENABLE_USER_SITE:267self.config_vars['userbase'] = self.install_userbase268self.config_vars['usersite'] = self.install_usersite269270elif self.user:271log.warn("WARNING: The user site-packages directory is disabled.")272273self._fix_install_dir_for_user_site()274275self.expand_basedirs()276self.expand_dirs()277278self._expand(279'install_dir', 'script_dir', 'build_directory',280'site_dirs',281)282# If a non-default installation directory was specified, default the283# script directory to match it.284if self.script_dir is None:285self.script_dir = self.install_dir286287if self.no_find_links is None:288self.no_find_links = False289290# Let install_dir get set by install_lib command, which in turn291# gets its info from the install command, and takes into account292# --prefix and --home and all that other crud.293self.set_undefined_options(294'install_lib', ('install_dir', 'install_dir')295)296# Likewise, set default script_dir from 'install_scripts.install_dir'297self.set_undefined_options(298'install_scripts', ('install_dir', 'script_dir')299)300301if self.user and self.install_purelib:302self.install_dir = self.install_purelib303self.script_dir = self.install_scripts304# default --record from the install command305self.set_undefined_options('install', ('record', 'record'))306# Should this be moved to the if statement below? It's not used307# elsewhere308normpath = map(normalize_path, sys.path)309self.all_site_dirs = get_site_dirs()310if self.site_dirs is not None:311site_dirs = [312os.path.expanduser(s.strip()) for s in313self.site_dirs.split(',')314]315for d in site_dirs:316if not os.path.isdir(d):317log.warn("%s (in --site-dirs) does not exist", d)318elif normalize_path(d) not in normpath:319raise DistutilsOptionError(320d + " (in --site-dirs) is not on sys.path"321)322else:323self.all_site_dirs.append(normalize_path(d))324if not self.editable:325self.check_site_dir()326self.index_url = self.index_url or "https://pypi.org/simple/"327self.shadow_path = self.all_site_dirs[:]328for path_item in self.install_dir, normalize_path(self.script_dir):329if path_item not in self.shadow_path:330self.shadow_path.insert(0, path_item)331332if self.allow_hosts is not None:333hosts = [s.strip() for s in self.allow_hosts.split(',')]334else:335hosts = ['*']336if self.package_index is None:337self.package_index = self.create_index(338self.index_url, search_path=self.shadow_path, hosts=hosts,339)340self.local_index = Environment(self.shadow_path + sys.path)341342if self.find_links is not None:343if isinstance(self.find_links, six.string_types):344self.find_links = self.find_links.split()345else:346self.find_links = []347if self.local_snapshots_ok:348self.package_index.scan_egg_links(self.shadow_path + sys.path)349if not self.no_find_links:350self.package_index.add_find_links(self.find_links)351self.set_undefined_options('install_lib', ('optimize', 'optimize'))352if not isinstance(self.optimize, int):353try:354self.optimize = int(self.optimize)355if not (0 <= self.optimize <= 2):356raise ValueError357except ValueError as e:358raise DistutilsOptionError(359"--optimize must be 0, 1, or 2"360) from e361362if self.editable and not self.build_directory:363raise DistutilsArgError(364"Must specify a build directory (-b) when using --editable"365)366if not self.args:367raise DistutilsArgError(368"No urls, filenames, or requirements specified (see --help)")369370self.outputs = []371372def _fix_install_dir_for_user_site(self):373"""374Fix the install_dir if "--user" was used.375"""376if not self.user or not site.ENABLE_USER_SITE:377return378379self.create_home_path()380if self.install_userbase is None:381msg = "User base directory is not specified"382raise DistutilsPlatformError(msg)383self.install_base = self.install_platbase = self.install_userbase384scheme_name = os.name.replace('posix', 'unix') + '_user'385self.select_scheme(scheme_name)386387def _expand_attrs(self, attrs):388for attr in attrs:389val = getattr(self, attr)390if val is not None:391if os.name == 'posix' or os.name == 'nt':392val = os.path.expanduser(val)393val = subst_vars(val, self.config_vars)394setattr(self, attr, val)395396def expand_basedirs(self):397"""Calls `os.path.expanduser` on install_base, install_platbase and398root."""399self._expand_attrs(['install_base', 'install_platbase', 'root'])400401def expand_dirs(self):402"""Calls `os.path.expanduser` on install dirs."""403dirs = [404'install_purelib',405'install_platlib',406'install_lib',407'install_headers',408'install_scripts',409'install_data',410]411self._expand_attrs(dirs)412413def run(self, show_deprecation=True):414if show_deprecation:415self.announce(416"WARNING: The easy_install command is deprecated "417"and will be removed in a future version.",418log.WARN,419)420if self.verbose != self.distribution.verbose:421log.set_verbosity(self.verbose)422try:423for spec in self.args:424self.easy_install(spec, not self.no_deps)425if self.record:426outputs = self.outputs427if self.root: # strip any package prefix428root_len = len(self.root)429for counter in range(len(outputs)):430outputs[counter] = outputs[counter][root_len:]431from distutils import file_util432433self.execute(434file_util.write_file, (self.record, outputs),435"writing list of installed files to '%s'" %436self.record437)438self.warn_deprecated_options()439finally:440log.set_verbosity(self.distribution.verbose)441442def pseudo_tempname(self):443"""Return a pseudo-tempname base in the install directory.444This code is intentionally naive; if a malicious party can write to445the target directory you're already in deep doodoo.446"""447try:448pid = os.getpid()449except Exception:450pid = random.randint(0, sys.maxsize)451return os.path.join(self.install_dir, "test-easy-install-%s" % pid)452453def warn_deprecated_options(self):454pass455456def check_site_dir(self):457"""Verify that self.install_dir is .pth-capable dir, if needed"""458459instdir = normalize_path(self.install_dir)460pth_file = os.path.join(instdir, 'easy-install.pth')461462if not os.path.exists(instdir):463try:464os.makedirs(instdir)465except (OSError, IOError):466self.cant_write_to_target()467468# Is it a configured, PYTHONPATH, implicit, or explicit site dir?469is_site_dir = instdir in self.all_site_dirs470471if not is_site_dir and not self.multi_version:472# No? Then directly test whether it does .pth file processing473is_site_dir = self.check_pth_processing()474else:475# make sure we can write to target dir476testfile = self.pseudo_tempname() + '.write-test'477test_exists = os.path.exists(testfile)478try:479if test_exists:480os.unlink(testfile)481open(testfile, 'w').close()482os.unlink(testfile)483except (OSError, IOError):484self.cant_write_to_target()485486if not is_site_dir and not self.multi_version:487# Can't install non-multi to non-site dir with easy_install488pythonpath = os.environ.get('PYTHONPATH', '')489log.warn(self.__no_default_msg, self.install_dir, pythonpath)490491if is_site_dir:492if self.pth_file is None:493self.pth_file = PthDistributions(pth_file, self.all_site_dirs)494else:495self.pth_file = None496497if self.multi_version and not os.path.exists(pth_file):498self.pth_file = None # don't create a .pth file499self.install_dir = instdir500501__cant_write_msg = textwrap.dedent("""502can't create or remove files in install directory503504The following error occurred while trying to add or remove files in the505installation directory:506507%s508509The installation directory you specified (via --install-dir, --prefix, or510the distutils default setting) was:511512%s513""").lstrip() # noqa514515__not_exists_id = textwrap.dedent("""516This directory does not currently exist. Please create it and try again, or517choose a different installation directory (using the -d or --install-dir518option).519""").lstrip() # noqa520521__access_msg = textwrap.dedent("""522Perhaps your account does not have write access to this directory? If the523installation directory is a system-owned directory, you may need to sign in524as the administrator or "root" account. If you do not have administrative525access to this machine, you may wish to choose a different installation526directory, preferably one that is listed in your PYTHONPATH environment527variable.528529For information on other options, you may wish to consult the530documentation at:531532https://setuptools.readthedocs.io/en/latest/easy_install.html533534Please make the appropriate changes for your system and try again.535""").lstrip() # noqa536537def cant_write_to_target(self):538msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,)539540if not os.path.exists(self.install_dir):541msg += '\n' + self.__not_exists_id542else:543msg += '\n' + self.__access_msg544raise DistutilsError(msg)545546def check_pth_processing(self):547"""Empirically verify whether .pth files are supported in inst. dir"""548instdir = self.install_dir549log.info("Checking .pth file support in %s", instdir)550pth_file = self.pseudo_tempname() + ".pth"551ok_file = pth_file + '.ok'552ok_exists = os.path.exists(ok_file)553tmpl = _one_liner("""554import os555f = open({ok_file!r}, 'w')556f.write('OK')557f.close()558""") + '\n'559try:560if ok_exists:561os.unlink(ok_file)562dirname = os.path.dirname(ok_file)563os.makedirs(dirname, exist_ok=True)564f = open(pth_file, 'w')565except (OSError, IOError):566self.cant_write_to_target()567else:568try:569f.write(tmpl.format(**locals()))570f.close()571f = None572executable = sys.executable573if os.name == 'nt':574dirname, basename = os.path.split(executable)575alt = os.path.join(dirname, 'pythonw.exe')576use_alt = (577basename.lower() == 'python.exe' and578os.path.exists(alt)579)580if use_alt:581# use pythonw.exe to avoid opening a console window582executable = alt583584from distutils.spawn import spawn585586spawn([executable, '-E', '-c', 'pass'], 0)587588if os.path.exists(ok_file):589log.info(590"TEST PASSED: %s appears to support .pth files",591instdir592)593return True594finally:595if f:596f.close()597if os.path.exists(ok_file):598os.unlink(ok_file)599if os.path.exists(pth_file):600os.unlink(pth_file)601if not self.multi_version:602log.warn("TEST FAILED: %s does NOT support .pth files", instdir)603return False604605def install_egg_scripts(self, dist):606"""Write all the scripts for `dist`, unless scripts are excluded"""607if not self.exclude_scripts and dist.metadata_isdir('scripts'):608for script_name in dist.metadata_listdir('scripts'):609if dist.metadata_isdir('scripts/' + script_name):610# The "script" is a directory, likely a Python 3611# __pycache__ directory, so skip it.612continue613self.install_script(614dist, script_name,615dist.get_metadata('scripts/' + script_name)616)617self.install_wrapper_scripts(dist)618619def add_output(self, path):620if os.path.isdir(path):621for base, dirs, files in os.walk(path):622for filename in files:623self.outputs.append(os.path.join(base, filename))624else:625self.outputs.append(path)626627def not_editable(self, spec):628if self.editable:629raise DistutilsArgError(630"Invalid argument %r: you can't use filenames or URLs "631"with --editable (except via the --find-links option)."632% (spec,)633)634635def check_editable(self, spec):636if not self.editable:637return638639if os.path.exists(os.path.join(self.build_directory, spec.key)):640raise DistutilsArgError(641"%r already exists in %s; can't do a checkout there" %642(spec.key, self.build_directory)643)644645@contextlib.contextmanager646def _tmpdir(self):647tmpdir = tempfile.mkdtemp(prefix=u"easy_install-")648try:649# cast to str as workaround for #709 and #710 and #712650yield str(tmpdir)651finally:652os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir))653654def easy_install(self, spec, deps=False):655with self._tmpdir() as tmpdir:656if not isinstance(spec, Requirement):657if URL_SCHEME(spec):658# It's a url, download it to tmpdir and process659self.not_editable(spec)660dl = self.package_index.download(spec, tmpdir)661return self.install_item(None, dl, tmpdir, deps, True)662663elif os.path.exists(spec):664# Existing file or directory, just process it directly665self.not_editable(spec)666return self.install_item(None, spec, tmpdir, deps, True)667else:668spec = parse_requirement_arg(spec)669670self.check_editable(spec)671dist = self.package_index.fetch_distribution(672spec, tmpdir, self.upgrade, self.editable,673not self.always_copy, self.local_index674)675if dist is None:676msg = "Could not find suitable distribution for %r" % spec677if self.always_copy:678msg += " (--always-copy skips system and development eggs)"679raise DistutilsError(msg)680elif dist.precedence == DEVELOP_DIST:681# .egg-info dists don't need installing, just process deps682self.process_distribution(spec, dist, deps, "Using")683return dist684else:685return self.install_item(spec, dist.location, tmpdir, deps)686687def install_item(self, spec, download, tmpdir, deps, install_needed=False):688689# Installation is also needed if file in tmpdir or is not an egg690install_needed = install_needed or self.always_copy691install_needed = install_needed or os.path.dirname(download) == tmpdir692install_needed = install_needed or not download.endswith('.egg')693install_needed = install_needed or (694self.always_copy_from is not None and695os.path.dirname(normalize_path(download)) ==696normalize_path(self.always_copy_from)697)698699if spec and not install_needed:700# at this point, we know it's a local .egg, we just don't know if701# it's already installed.702for dist in self.local_index[spec.project_name]:703if dist.location == download:704break705else:706install_needed = True # it's not in the local index707708log.info("Processing %s", os.path.basename(download))709710if install_needed:711dists = self.install_eggs(spec, download, tmpdir)712for dist in dists:713self.process_distribution(spec, dist, deps)714else:715dists = [self.egg_distribution(download)]716self.process_distribution(spec, dists[0], deps, "Using")717718if spec is not None:719for dist in dists:720if dist in spec:721return dist722723def select_scheme(self, name):724"""Sets the install directories by applying the install schemes."""725# it's the caller's problem if they supply a bad name!726scheme = INSTALL_SCHEMES[name]727for key in SCHEME_KEYS:728attrname = 'install_' + key729if getattr(self, attrname) is None:730setattr(self, attrname, scheme[key])731732def process_distribution(self, requirement, dist, deps=True, *info):733self.update_pth(dist)734self.package_index.add(dist)735if dist in self.local_index[dist.key]:736self.local_index.remove(dist)737self.local_index.add(dist)738self.install_egg_scripts(dist)739self.installed_projects[dist.key] = dist740log.info(self.installation_report(requirement, dist, *info))741if (dist.has_metadata('dependency_links.txt') and742not self.no_find_links):743self.package_index.add_find_links(744dist.get_metadata_lines('dependency_links.txt')745)746if not deps and not self.always_copy:747return748elif requirement is not None and dist.key != requirement.key:749log.warn("Skipping dependencies for %s", dist)750return # XXX this is not the distribution we were looking for751elif requirement is None or dist not in requirement:752# if we wound up with a different version, resolve what we've got753distreq = dist.as_requirement()754requirement = Requirement(str(distreq))755log.info("Processing dependencies for %s", requirement)756try:757distros = WorkingSet([]).resolve(758[requirement], self.local_index, self.easy_install759)760except DistributionNotFound as e:761raise DistutilsError(str(e)) from e762except VersionConflict as e:763raise DistutilsError(e.report()) from e764if self.always_copy or self.always_copy_from:765# Force all the relevant distros to be copied or activated766for dist in distros:767if dist.key not in self.installed_projects:768self.easy_install(dist.as_requirement())769log.info("Finished processing dependencies for %s", requirement)770771def should_unzip(self, dist):772if self.zip_ok is not None:773return not self.zip_ok774if dist.has_metadata('not-zip-safe'):775return True776if not dist.has_metadata('zip-safe'):777return True778return False779780def maybe_move(self, spec, dist_filename, setup_base):781dst = os.path.join(self.build_directory, spec.key)782if os.path.exists(dst):783msg = (784"%r already exists in %s; build directory %s will not be kept"785)786log.warn(msg, spec.key, self.build_directory, setup_base)787return setup_base788if os.path.isdir(dist_filename):789setup_base = dist_filename790else:791if os.path.dirname(dist_filename) == setup_base:792os.unlink(dist_filename) # get it out of the tmp dir793contents = os.listdir(setup_base)794if len(contents) == 1:795dist_filename = os.path.join(setup_base, contents[0])796if os.path.isdir(dist_filename):797# if the only thing there is a directory, move it instead798setup_base = dist_filename799ensure_directory(dst)800shutil.move(setup_base, dst)801return dst802803def install_wrapper_scripts(self, dist):804if self.exclude_scripts:805return806for args in ScriptWriter.best().get_args(dist):807self.write_script(*args)808809def install_script(self, dist, script_name, script_text, dev_path=None):810"""Generate a legacy script wrapper and install it"""811spec = str(dist.as_requirement())812is_script = is_python_script(script_text, script_name)813814if is_script:815body = self._load_template(dev_path) % locals()816script_text = ScriptWriter.get_header(script_text) + body817self.write_script(script_name, _to_bytes(script_text), 'b')818819@staticmethod820def _load_template(dev_path):821"""822There are a couple of template scripts in the package. This823function loads one of them and prepares it for use.824"""825# See https://github.com/pypa/setuptools/issues/134 for info826# on script file naming and downstream issues with SVR4827name = 'script.tmpl'828if dev_path:829name = name.replace('.tmpl', ' (dev).tmpl')830831raw_bytes = resource_string('setuptools', name)832return raw_bytes.decode('utf-8')833834def write_script(self, script_name, contents, mode="t", blockers=()):835"""Write an executable file to the scripts directory"""836self.delete_blockers( # clean up old .py/.pyw w/o a script837[os.path.join(self.script_dir, x) for x in blockers]838)839log.info("Installing %s script to %s", script_name, self.script_dir)840target = os.path.join(self.script_dir, script_name)841self.add_output(target)842843if self.dry_run:844return845846mask = current_umask()847ensure_directory(target)848if os.path.exists(target):849os.unlink(target)850with open(target, "w" + mode) as f:851f.write(contents)852chmod(target, 0o777 - mask)853854def install_eggs(self, spec, dist_filename, tmpdir):855# .egg dirs or files are already built, so just return them856if dist_filename.lower().endswith('.egg'):857return [self.install_egg(dist_filename, tmpdir)]858elif dist_filename.lower().endswith('.exe'):859return [self.install_exe(dist_filename, tmpdir)]860elif dist_filename.lower().endswith('.whl'):861return [self.install_wheel(dist_filename, tmpdir)]862863# Anything else, try to extract and build864setup_base = tmpdir865if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'):866unpack_archive(dist_filename, tmpdir, self.unpack_progress)867elif os.path.isdir(dist_filename):868setup_base = os.path.abspath(dist_filename)869870if (setup_base.startswith(tmpdir) # something we downloaded871and self.build_directory and spec is not None):872setup_base = self.maybe_move(spec, dist_filename, setup_base)873874# Find the setup.py file875setup_script = os.path.join(setup_base, 'setup.py')876877if not os.path.exists(setup_script):878setups = glob(os.path.join(setup_base, '*', 'setup.py'))879if not setups:880raise DistutilsError(881"Couldn't find a setup script in %s" %882os.path.abspath(dist_filename)883)884if len(setups) > 1:885raise DistutilsError(886"Multiple setup scripts in %s" %887os.path.abspath(dist_filename)888)889setup_script = setups[0]890891# Now run it, and return the result892if self.editable:893log.info(self.report_editable(spec, setup_script))894return []895else:896return self.build_and_install(setup_script, setup_base)897898def egg_distribution(self, egg_path):899if os.path.isdir(egg_path):900metadata = PathMetadata(egg_path, os.path.join(egg_path,901'EGG-INFO'))902else:903metadata = EggMetadata(zipimport.zipimporter(egg_path))904return Distribution.from_filename(egg_path, metadata=metadata)905906def install_egg(self, egg_path, tmpdir):907destination = os.path.join(908self.install_dir,909os.path.basename(egg_path),910)911destination = os.path.abspath(destination)912if not self.dry_run:913ensure_directory(destination)914915dist = self.egg_distribution(egg_path)916if not samefile(egg_path, destination):917if os.path.isdir(destination) and not os.path.islink(destination):918dir_util.remove_tree(destination, dry_run=self.dry_run)919elif os.path.exists(destination):920self.execute(921os.unlink,922(destination,),923"Removing " + destination,924)925try:926new_dist_is_zipped = False927if os.path.isdir(egg_path):928if egg_path.startswith(tmpdir):929f, m = shutil.move, "Moving"930else:931f, m = shutil.copytree, "Copying"932elif self.should_unzip(dist):933self.mkpath(destination)934f, m = self.unpack_and_compile, "Extracting"935else:936new_dist_is_zipped = True937if egg_path.startswith(tmpdir):938f, m = shutil.move, "Moving"939else:940f, m = shutil.copy2, "Copying"941self.execute(942f,943(egg_path, destination),944(m + " %s to %s") % (945os.path.basename(egg_path),946os.path.dirname(destination)947),948)949update_dist_caches(950destination,951fix_zipimporter_caches=new_dist_is_zipped,952)953except Exception:954update_dist_caches(destination, fix_zipimporter_caches=False)955raise956957self.add_output(destination)958return self.egg_distribution(destination)959960def install_exe(self, dist_filename, tmpdir):961# See if it's valid, get data962cfg = extract_wininst_cfg(dist_filename)963if cfg is None:964raise DistutilsError(965"%s is not a valid distutils Windows .exe" % dist_filename966)967# Create a dummy distribution object until we build the real distro968dist = Distribution(969None,970project_name=cfg.get('metadata', 'name'),971version=cfg.get('metadata', 'version'), platform=get_platform(),972)973974# Convert the .exe to an unpacked egg975egg_path = os.path.join(tmpdir, dist.egg_name() + '.egg')976dist.location = egg_path977egg_tmp = egg_path + '.tmp'978_egg_info = os.path.join(egg_tmp, 'EGG-INFO')979pkg_inf = os.path.join(_egg_info, 'PKG-INFO')980ensure_directory(pkg_inf) # make sure EGG-INFO dir exists981dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX982self.exe_to_egg(dist_filename, egg_tmp)983984# Write EGG-INFO/PKG-INFO985if not os.path.exists(pkg_inf):986f = open(pkg_inf, 'w')987f.write('Metadata-Version: 1.0\n')988for k, v in cfg.items('metadata'):989if k != 'target_version':990f.write('%s: %s\n' % (k.replace('_', '-').title(), v))991f.close()992script_dir = os.path.join(_egg_info, 'scripts')993# delete entry-point scripts to avoid duping994self.delete_blockers([995os.path.join(script_dir, args[0])996for args in ScriptWriter.get_args(dist)997])998# Build .egg file from tmpdir999bdist_egg.make_zipfile(1000egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run,1001)1002# install the .egg1003return self.install_egg(egg_path, tmpdir)10041005def exe_to_egg(self, dist_filename, egg_tmp):1006"""Extract a bdist_wininst to the directories an egg would use"""1007# Check for .pth file and set up prefix translations1008prefixes = get_exe_prefixes(dist_filename)1009to_compile = []1010native_libs = []1011top_level = {}10121013def process(src, dst):1014s = src.lower()1015for old, new in prefixes:1016if s.startswith(old):1017src = new + src[len(old):]1018parts = src.split('/')1019dst = os.path.join(egg_tmp, *parts)1020dl = dst.lower()1021if dl.endswith('.pyd') or dl.endswith('.dll'):1022parts[-1] = bdist_egg.strip_module(parts[-1])1023top_level[os.path.splitext(parts[0])[0]] = 11024native_libs.append(src)1025elif dl.endswith('.py') and old != 'SCRIPTS/':1026top_level[os.path.splitext(parts[0])[0]] = 11027to_compile.append(dst)1028return dst1029if not src.endswith('.pth'):1030log.warn("WARNING: can't process %s", src)1031return None10321033# extract, tracking .pyd/.dll->native_libs and .py -> to_compile1034unpack_archive(dist_filename, egg_tmp, process)1035stubs = []1036for res in native_libs:1037if res.lower().endswith('.pyd'): # create stubs for .pyd's1038parts = res.split('/')1039resource = parts[-1]1040parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py'1041pyfile = os.path.join(egg_tmp, *parts)1042to_compile.append(pyfile)1043stubs.append(pyfile)1044bdist_egg.write_stub(resource, pyfile)1045self.byte_compile(to_compile) # compile .py's1046bdist_egg.write_safety_flag(1047os.path.join(egg_tmp, 'EGG-INFO'),1048bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag10491050for name in 'top_level', 'native_libs':1051if locals()[name]:1052txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt')1053if not os.path.exists(txt):1054f = open(txt, 'w')1055f.write('\n'.join(locals()[name]) + '\n')1056f.close()10571058def install_wheel(self, wheel_path, tmpdir):1059wheel = Wheel(wheel_path)1060assert wheel.is_compatible()1061destination = os.path.join(self.install_dir, wheel.egg_name())1062destination = os.path.abspath(destination)1063if not self.dry_run:1064ensure_directory(destination)1065if os.path.isdir(destination) and not os.path.islink(destination):1066dir_util.remove_tree(destination, dry_run=self.dry_run)1067elif os.path.exists(destination):1068self.execute(1069os.unlink,1070(destination,),1071"Removing " + destination,1072)1073try:1074self.execute(1075wheel.install_as_egg,1076(destination,),1077("Installing %s to %s") % (1078os.path.basename(wheel_path),1079os.path.dirname(destination)1080),1081)1082finally:1083update_dist_caches(destination, fix_zipimporter_caches=False)1084self.add_output(destination)1085return self.egg_distribution(destination)10861087__mv_warning = textwrap.dedent("""1088Because this distribution was installed --multi-version, before you can1089import modules from this package in an application, you will need to1090'import pkg_resources' and then use a 'require()' call similar to one of1091these examples, in order to select the desired version:10921093pkg_resources.require("%(name)s") # latest installed version1094pkg_resources.require("%(name)s==%(version)s") # this exact version1095pkg_resources.require("%(name)s>=%(version)s") # this version or higher1096""").lstrip() # noqa10971098__id_warning = textwrap.dedent("""1099Note also that the installation directory must be on sys.path at runtime for1100this to work. (e.g. by being the application's script directory, by being on1101PYTHONPATH, or by being added to sys.path by your code.)1102""") # noqa11031104def installation_report(self, req, dist, what="Installed"):1105"""Helpful installation message for display to package users"""1106msg = "\n%(what)s %(eggloc)s%(extras)s"1107if self.multi_version and not self.no_report:1108msg += '\n' + self.__mv_warning1109if self.install_dir not in map(normalize_path, sys.path):1110msg += '\n' + self.__id_warning11111112eggloc = dist.location1113name = dist.project_name1114version = dist.version1115extras = '' # TODO: self.report_extras(req, dist)1116return msg % locals()11171118__editable_msg = textwrap.dedent("""1119Extracted editable version of %(spec)s to %(dirname)s11201121If it uses setuptools in its setup script, you can activate it in1122"development" mode by going to that directory and running::11231124%(python)s setup.py develop11251126See the setuptools documentation for the "develop" command for more info.1127""").lstrip() # noqa11281129def report_editable(self, spec, setup_script):1130dirname = os.path.dirname(setup_script)1131python = sys.executable1132return '\n' + self.__editable_msg % locals()11331134def run_setup(self, setup_script, setup_base, args):1135sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)1136sys.modules.setdefault('distutils.command.egg_info', egg_info)11371138args = list(args)1139if self.verbose > 2:1140v = 'v' * (self.verbose - 1)1141args.insert(0, '-' + v)1142elif self.verbose < 2:1143args.insert(0, '-q')1144if self.dry_run:1145args.insert(0, '-n')1146log.info(1147"Running %s %s", setup_script[len(setup_base) + 1:], ' '.join(args)1148)1149try:1150run_setup(setup_script, args)1151except SystemExit as v:1152raise DistutilsError(1153"Setup script exited with %s" % (v.args[0],)1154) from v11551156def build_and_install(self, setup_script, setup_base):1157args = ['bdist_egg', '--dist-dir']11581159dist_dir = tempfile.mkdtemp(1160prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script)1161)1162try:1163self._set_fetcher_options(os.path.dirname(setup_script))1164args.append(dist_dir)11651166self.run_setup(setup_script, setup_base, args)1167all_eggs = Environment([dist_dir])1168eggs = []1169for key in all_eggs:1170for dist in all_eggs[key]:1171eggs.append(self.install_egg(dist.location, setup_base))1172if not eggs and not self.dry_run:1173log.warn("No eggs found in %s (setup script problem?)",1174dist_dir)1175return eggs1176finally:1177rmtree(dist_dir)1178log.set_verbosity(self.verbose) # restore our log verbosity11791180def _set_fetcher_options(self, base):1181"""1182When easy_install is about to run bdist_egg on a source dist, that1183source dist might have 'setup_requires' directives, requiring1184additional fetching. Ensure the fetcher options given to easy_install1185are available to that command as well.1186"""1187# find the fetch options from easy_install and write them out1188# to the setup.cfg file.1189ei_opts = self.distribution.get_option_dict('easy_install').copy()1190fetch_directives = (1191'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts',1192)1193fetch_options = {}1194for key, val in ei_opts.items():1195if key not in fetch_directives:1196continue1197fetch_options[key.replace('_', '-')] = val[1]1198# create a settings dictionary suitable for `edit_config`1199settings = dict(easy_install=fetch_options)1200cfg_filename = os.path.join(base, 'setup.cfg')1201setopt.edit_config(cfg_filename, settings)12021203def update_pth(self, dist):1204if self.pth_file is None:1205return12061207for d in self.pth_file[dist.key]: # drop old entries1208if self.multi_version or d.location != dist.location:1209log.info("Removing %s from easy-install.pth file", d)1210self.pth_file.remove(d)1211if d.location in self.shadow_path:1212self.shadow_path.remove(d.location)12131214if not self.multi_version:1215if dist.location in self.pth_file.paths:1216log.info(1217"%s is already the active version in easy-install.pth",1218dist,1219)1220else:1221log.info("Adding %s to easy-install.pth file", dist)1222self.pth_file.add(dist) # add new entry1223if dist.location not in self.shadow_path:1224self.shadow_path.append(dist.location)12251226if not self.dry_run:12271228self.pth_file.save()12291230if dist.key == 'setuptools':1231# Ensure that setuptools itself never becomes unavailable!1232# XXX should this check for latest version?1233filename = os.path.join(self.install_dir, 'setuptools.pth')1234if os.path.islink(filename):1235os.unlink(filename)1236f = open(filename, 'wt')1237f.write(self.pth_file.make_relative(dist.location) + '\n')1238f.close()12391240def unpack_progress(self, src, dst):1241# Progress filter for unpacking1242log.debug("Unpacking %s to %s", src, dst)1243return dst # only unpack-and-compile skips files for dry run12441245def unpack_and_compile(self, egg_path, destination):1246to_compile = []1247to_chmod = []12481249def pf(src, dst):1250if dst.endswith('.py') and not src.startswith('EGG-INFO/'):1251to_compile.append(dst)1252elif dst.endswith('.dll') or dst.endswith('.so'):1253to_chmod.append(dst)1254self.unpack_progress(src, dst)1255return not self.dry_run and dst or None12561257unpack_archive(egg_path, destination, pf)1258self.byte_compile(to_compile)1259if not self.dry_run:1260for f in to_chmod:1261mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o77551262chmod(f, mode)12631264def byte_compile(self, to_compile):1265if sys.dont_write_bytecode:1266return12671268from distutils.util import byte_compile12691270try:1271# try to make the byte compile messages quieter1272log.set_verbosity(self.verbose - 1)12731274byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run)1275if self.optimize:1276byte_compile(1277to_compile, optimize=self.optimize, force=1,1278dry_run=self.dry_run,1279)1280finally:1281log.set_verbosity(self.verbose) # restore original verbosity12821283__no_default_msg = textwrap.dedent("""1284bad install directory or PYTHONPATH12851286You are attempting to install a package to a directory that is not1287on PYTHONPATH and which Python does not read ".pth" files from. The1288installation directory you specified (via --install-dir, --prefix, or1289the distutils default setting) was:12901291%s12921293and your PYTHONPATH environment variable currently contains:12941295%r12961297Here are some of your options for correcting the problem:12981299* You can choose a different installation directory, i.e., one that is1300on PYTHONPATH or supports .pth files13011302* You can add the installation directory to the PYTHONPATH environment1303variable. (It must then also be on PYTHONPATH whenever you run1304Python and want to use the package(s) you are installing.)13051306* You can set up the installation directory to support ".pth" files by1307using one of the approaches described here:13081309https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations131013111312Please make the appropriate changes for your system and try again.1313""").strip()13141315def create_home_path(self):1316"""Create directories under ~."""1317if not self.user:1318return1319home = convert_path(os.path.expanduser("~"))1320for name, path in six.iteritems(self.config_vars):1321if path.startswith(home) and not os.path.isdir(path):1322self.debug_print("os.makedirs('%s', 0o700)" % path)1323os.makedirs(path, 0o700)13241325INSTALL_SCHEMES = dict(1326posix=dict(1327install_dir='$base/lib/python$py_version_short/site-packages',1328script_dir='$base/bin',1329),1330)13311332DEFAULT_SCHEME = dict(1333install_dir='$base/Lib/site-packages',1334script_dir='$base/Scripts',1335)13361337def _expand(self, *attrs):1338config_vars = self.get_finalized_command('install').config_vars13391340if self.prefix:1341# Set default install_dir/scripts from --prefix1342config_vars = config_vars.copy()1343config_vars['base'] = self.prefix1344scheme = self.INSTALL_SCHEMES.get(os.name, self.DEFAULT_SCHEME)1345for attr, val in scheme.items():1346if getattr(self, attr, None) is None:1347setattr(self, attr, val)13481349from distutils.util import subst_vars13501351for attr in attrs:1352val = getattr(self, attr)1353if val is not None:1354val = subst_vars(val, config_vars)1355if os.name == 'posix':1356val = os.path.expanduser(val)1357setattr(self, attr, val)135813591360def _pythonpath():1361items = os.environ.get('PYTHONPATH', '').split(os.pathsep)1362return filter(None, items)136313641365def get_site_dirs():1366"""1367Return a list of 'site' dirs1368"""13691370sitedirs = []13711372# start with PYTHONPATH1373sitedirs.extend(_pythonpath())13741375prefixes = [sys.prefix]1376if sys.exec_prefix != sys.prefix:1377prefixes.append(sys.exec_prefix)1378for prefix in prefixes:1379if prefix:1380if sys.platform in ('os2emx', 'riscos'):1381sitedirs.append(os.path.join(prefix, "Lib", "site-packages"))1382elif os.sep == '/':1383sitedirs.extend([1384os.path.join(1385prefix,1386"lib",1387"python{}.{}".format(*sys.version_info),1388"site-packages",1389),1390os.path.join(prefix, "lib", "site-python"),1391])1392else:1393sitedirs.extend([1394prefix,1395os.path.join(prefix, "lib", "site-packages"),1396])1397if sys.platform == 'darwin':1398# for framework builds *only* we add the standard Apple1399# locations. Currently only per-user, but /Library and1400# /Network/Library could be added too1401if 'Python.framework' in prefix:1402home = os.environ.get('HOME')1403if home:1404home_sp = os.path.join(1405home,1406'Library',1407'Python',1408'{}.{}'.format(*sys.version_info),1409'site-packages',1410)1411sitedirs.append(home_sp)1412lib_paths = get_path('purelib'), get_path('platlib')1413for site_lib in lib_paths:1414if site_lib not in sitedirs:1415sitedirs.append(site_lib)14161417if site.ENABLE_USER_SITE:1418sitedirs.append(site.USER_SITE)14191420try:1421sitedirs.extend(site.getsitepackages())1422except AttributeError:1423pass14241425sitedirs = list(map(normalize_path, sitedirs))14261427return sitedirs142814291430def expand_paths(inputs):1431"""Yield sys.path directories that might contain "old-style" packages"""14321433seen = {}14341435for dirname in inputs:1436dirname = normalize_path(dirname)1437if dirname in seen:1438continue14391440seen[dirname] = 11441if not os.path.isdir(dirname):1442continue14431444files = os.listdir(dirname)1445yield dirname, files14461447for name in files:1448if not name.endswith('.pth'):1449# We only care about the .pth files1450continue1451if name in ('easy-install.pth', 'setuptools.pth'):1452# Ignore .pth files that we control1453continue14541455# Read the .pth file1456f = open(os.path.join(dirname, name))1457lines = list(yield_lines(f))1458f.close()14591460# Yield existing non-dupe, non-import directory lines from it1461for line in lines:1462if not line.startswith("import"):1463line = normalize_path(line.rstrip())1464if line not in seen:1465seen[line] = 11466if not os.path.isdir(line):1467continue1468yield line, os.listdir(line)146914701471def extract_wininst_cfg(dist_filename):1472"""Extract configuration data from a bdist_wininst .exe14731474Returns a configparser.RawConfigParser, or None1475"""1476f = open(dist_filename, 'rb')1477try:1478endrec = zipfile._EndRecData(f)1479if endrec is None:1480return None14811482prepended = (endrec[9] - endrec[5]) - endrec[6]1483if prepended < 12: # no wininst data here1484return None1485f.seek(prepended - 12)14861487tag, cfglen, bmlen = struct.unpack("<iii", f.read(12))1488if tag not in (0x1234567A, 0x1234567B):1489return None # not a valid tag14901491f.seek(prepended - (12 + cfglen))1492init = {'version': '', 'target_version': ''}1493cfg = configparser.RawConfigParser(init)1494try:1495part = f.read(cfglen)1496# Read up to the first null byte.1497config = part.split(b'\0', 1)[0]1498# Now the config is in bytes, but for RawConfigParser, it should1499# be text, so decode it.1500config = config.decode(sys.getfilesystemencoding())1501cfg.readfp(six.StringIO(config))1502except configparser.Error:1503return None1504if not cfg.has_section('metadata') or not cfg.has_section('Setup'):1505return None1506return cfg15071508finally:1509f.close()151015111512def get_exe_prefixes(exe_filename):1513"""Get exe->egg path translations for a given .exe file"""15141515prefixes = [1516('PURELIB/', ''),1517('PLATLIB/pywin32_system32', ''),1518('PLATLIB/', ''),1519('SCRIPTS/', 'EGG-INFO/scripts/'),1520('DATA/lib/site-packages', ''),1521]1522z = zipfile.ZipFile(exe_filename)1523try:1524for info in z.infolist():1525name = info.filename1526parts = name.split('/')1527if len(parts) == 3 and parts[2] == 'PKG-INFO':1528if parts[1].endswith('.egg-info'):1529prefixes.insert(0, ('/'.join(parts[:2]), 'EGG-INFO/'))1530break1531if len(parts) != 2 or not name.endswith('.pth'):1532continue1533if name.endswith('-nspkg.pth'):1534continue1535if parts[0].upper() in ('PURELIB', 'PLATLIB'):1536contents = z.read(name)1537if not six.PY2:1538contents = contents.decode()1539for pth in yield_lines(contents):1540pth = pth.strip().replace('\\', '/')1541if not pth.startswith('import'):1542prefixes.append((('%s/%s/' % (parts[0], pth)), ''))1543finally:1544z.close()1545prefixes = [(x.lower(), y) for x, y in prefixes]1546prefixes.sort()1547prefixes.reverse()1548return prefixes154915501551class PthDistributions(Environment):1552"""A .pth file with Distribution paths in it"""15531554dirty = False15551556def __init__(self, filename, sitedirs=()):1557self.filename = filename1558self.sitedirs = list(map(normalize_path, sitedirs))1559self.basedir = normalize_path(os.path.dirname(self.filename))1560self._load()1561Environment.__init__(self, [], None, None)1562for path in yield_lines(self.paths):1563list(map(self.add, find_distributions(path, True)))15641565def _load(self):1566self.paths = []1567saw_import = False1568seen = dict.fromkeys(self.sitedirs)1569if os.path.isfile(self.filename):1570f = open(self.filename, 'rt')1571for line in f:1572if line.startswith('import'):1573saw_import = True1574continue1575path = line.rstrip()1576self.paths.append(path)1577if not path.strip() or path.strip().startswith('#'):1578continue1579# skip non-existent paths, in case somebody deleted a package1580# manually, and duplicate paths as well1581path = self.paths[-1] = normalize_path(1582os.path.join(self.basedir, path)1583)1584if not os.path.exists(path) or path in seen:1585self.paths.pop() # skip it1586self.dirty = True # we cleaned up, so we're dirty now :)1587continue1588seen[path] = 11589f.close()15901591if self.paths and not saw_import:1592self.dirty = True # ensure anything we touch has import wrappers1593while self.paths and not self.paths[-1].strip():1594self.paths.pop()15951596def save(self):1597"""Write changed .pth file back to disk"""1598if not self.dirty:1599return16001601rel_paths = list(map(self.make_relative, self.paths))1602if rel_paths:1603log.debug("Saving %s", self.filename)1604lines = self._wrap_lines(rel_paths)1605data = '\n'.join(lines) + '\n'16061607if os.path.islink(self.filename):1608os.unlink(self.filename)1609with open(self.filename, 'wt') as f:1610f.write(data)16111612elif os.path.exists(self.filename):1613log.debug("Deleting empty %s", self.filename)1614os.unlink(self.filename)16151616self.dirty = False16171618@staticmethod1619def _wrap_lines(lines):1620return lines16211622def add(self, dist):1623"""Add `dist` to the distribution map"""1624new_path = (1625dist.location not in self.paths and (1626dist.location not in self.sitedirs or1627# account for '.' being in PYTHONPATH1628dist.location == os.getcwd()1629)1630)1631if new_path:1632self.paths.append(dist.location)1633self.dirty = True1634Environment.add(self, dist)16351636def remove(self, dist):1637"""Remove `dist` from the distribution map"""1638while dist.location in self.paths:1639self.paths.remove(dist.location)1640self.dirty = True1641Environment.remove(self, dist)16421643def make_relative(self, path):1644npath, last = os.path.split(normalize_path(path))1645baselen = len(self.basedir)1646parts = [last]1647sep = os.altsep == '/' and '/' or os.sep1648while len(npath) >= baselen:1649if npath == self.basedir:1650parts.append(os.curdir)1651parts.reverse()1652return sep.join(parts)1653npath, last = os.path.split(npath)1654parts.append(last)1655else:1656return path165716581659class RewritePthDistributions(PthDistributions):1660@classmethod1661def _wrap_lines(cls, lines):1662yield cls.prelude1663for line in lines:1664yield line1665yield cls.postlude16661667prelude = _one_liner("""1668import sys1669sys.__plen = len(sys.path)1670""")1671postlude = _one_liner("""1672import sys1673new = sys.path[sys.__plen:]1674del sys.path[sys.__plen:]1675p = getattr(sys, '__egginsert', 0)1676sys.path[p:p] = new1677sys.__egginsert = p + len(new)1678""")167916801681if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite':1682PthDistributions = RewritePthDistributions168316841685def _first_line_re():1686"""1687Return a regular expression based on first_line_re suitable for matching1688strings.1689"""1690if isinstance(first_line_re.pattern, str):1691return first_line_re16921693# first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.1694return re.compile(first_line_re.pattern.decode())169516961697def auto_chmod(func, arg, exc):1698if func in [os.unlink, os.remove] and os.name == 'nt':1699chmod(arg, stat.S_IWRITE)1700return func(arg)1701et, ev, _ = sys.exc_info()1702six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg))))170317041705def update_dist_caches(dist_path, fix_zipimporter_caches):1706"""1707Fix any globally cached `dist_path` related data17081709`dist_path` should be a path of a newly installed egg distribution (zipped1710or unzipped).17111712sys.path_importer_cache contains finder objects that have been cached when1713importing data from the original distribution. Any such finders need to be1714cleared since the replacement distribution might be packaged differently,1715e.g. a zipped egg distribution might get replaced with an unzipped egg1716folder or vice versa. Having the old finders cached may then cause Python1717to attempt loading modules from the replacement distribution using an1718incorrect loader.17191720zipimport.zipimporter objects are Python loaders charged with importing1721data packaged inside zip archives. If stale loaders referencing the1722original distribution, are left behind, they can fail to load modules from1723the replacement distribution. E.g. if an old zipimport.zipimporter instance1724is used to load data from a new zipped egg archive, it may cause the1725operation to attempt to locate the requested data in the wrong location -1726one indicated by the original distribution's zip archive directory1727information. Such an operation may then fail outright, e.g. report having1728read a 'bad local file header', or even worse, it may fail silently &1729return invalid data.17301731zipimport._zip_directory_cache contains cached zip archive directory1732information for all existing zipimport.zipimporter instances and all such1733instances connected to the same archive share the same cached directory1734information.17351736If asked, and the underlying Python implementation allows it, we can fix1737all existing zipimport.zipimporter instances instead of having to track1738them down and remove them one by one, by updating their shared cached zip1739archive directory information. This, of course, assumes that the1740replacement distribution is packaged as a zipped egg.17411742If not asked to fix existing zipimport.zipimporter instances, we still do1743our best to clear any remaining zipimport.zipimporter related cached data1744that might somehow later get used when attempting to load data from the new1745distribution and thus cause such load operations to fail. Note that when1746tracking down such remaining stale data, we can not catch every conceivable1747usage from here, and we clear only those that we know of and have found to1748cause problems if left alive. Any remaining caches should be updated by1749whomever is in charge of maintaining them, i.e. they should be ready to1750handle us replacing their zip archives with new distributions at runtime.17511752"""1753# There are several other known sources of stale zipimport.zipimporter1754# instances that we do not clear here, but might if ever given a reason to1755# do so:1756# * Global setuptools pkg_resources.working_set (a.k.a. 'master working1757# set') may contain distributions which may in turn contain their1758# zipimport.zipimporter loaders.1759# * Several zipimport.zipimporter loaders held by local variables further1760# up the function call stack when running the setuptools installation.1761# * Already loaded modules may have their __loader__ attribute set to the1762# exact loader instance used when importing them. Python 3.4 docs state1763# that this information is intended mostly for introspection and so is1764# not expected to cause us problems.1765normalized_path = normalize_path(dist_path)1766_uncache(normalized_path, sys.path_importer_cache)1767if fix_zipimporter_caches:1768_replace_zip_directory_cache_data(normalized_path)1769else:1770# Here, even though we do not want to fix existing and now stale1771# zipimporter cache information, we still want to remove it. Related to1772# Python's zip archive directory information cache, we clear each of1773# its stale entries in two phases:1774# 1. Clear the entry so attempting to access zip archive information1775# via any existing stale zipimport.zipimporter instances fails.1776# 2. Remove the entry from the cache so any newly constructed1777# zipimport.zipimporter instances do not end up using old stale1778# zip archive directory information.1779# This whole stale data removal step does not seem strictly necessary,1780# but has been left in because it was done before we started replacing1781# the zip archive directory information cache content if possible, and1782# there are no relevant unit tests that we can depend on to tell us if1783# this is really needed.1784_remove_and_clear_zip_directory_cache_data(normalized_path)178517861787def _collect_zipimporter_cache_entries(normalized_path, cache):1788"""1789Return zipimporter cache entry keys related to a given normalized path.17901791Alternative path spellings (e.g. those using different character case or1792those using alternative path separators) related to the same path are1793included. Any sub-path entries are included as well, i.e. those1794corresponding to zip archives embedded in other zip archives.17951796"""1797result = []1798prefix_len = len(normalized_path)1799for p in cache:1800np = normalize_path(p)1801if (np.startswith(normalized_path) and1802np[prefix_len:prefix_len + 1] in (os.sep, '')):1803result.append(p)1804return result180518061807def _update_zipimporter_cache(normalized_path, cache, updater=None):1808"""1809Update zipimporter cache data for a given normalized path.18101811Any sub-path entries are processed as well, i.e. those corresponding to zip1812archives embedded in other zip archives.18131814Given updater is a callable taking a cache entry key and the original entry1815(after already removing the entry from the cache), and expected to update1816the entry and possibly return a new one to be inserted in its place.1817Returning None indicates that the entry should not be replaced with a new1818one. If no updater is given, the cache entries are simply removed without1819any additional processing, the same as if the updater simply returned None.18201821"""1822for p in _collect_zipimporter_cache_entries(normalized_path, cache):1823# N.B. pypy's custom zipimport._zip_directory_cache implementation does1824# not support the complete dict interface:1825# * Does not support item assignment, thus not allowing this function1826# to be used only for removing existing cache entries.1827# * Does not support the dict.pop() method, forcing us to use the1828# get/del patterns instead. For more detailed information see the1829# following links:1830# https://github.com/pypa/setuptools/issues/202#issuecomment-2029134201831# http://bit.ly/2h9itJX1832old_entry = cache[p]1833del cache[p]1834new_entry = updater and updater(p, old_entry)1835if new_entry is not None:1836cache[p] = new_entry183718381839def _uncache(normalized_path, cache):1840_update_zipimporter_cache(normalized_path, cache)184118421843def _remove_and_clear_zip_directory_cache_data(normalized_path):1844def clear_and_remove_cached_zip_archive_directory_data(path, old_entry):1845old_entry.clear()18461847_update_zipimporter_cache(1848normalized_path, zipimport._zip_directory_cache,1849updater=clear_and_remove_cached_zip_archive_directory_data)185018511852# PyPy Python implementation does not allow directly writing to the1853# zipimport._zip_directory_cache and so prevents us from attempting to correct1854# its content. The best we can do there is clear the problematic cache content1855# and have PyPy repopulate it as needed. The downside is that if there are any1856# stale zipimport.zipimporter instances laying around, attempting to use them1857# will fail due to not having its zip archive directory information available1858# instead of being automatically corrected to use the new correct zip archive1859# directory information.1860if '__pypy__' in sys.builtin_module_names:1861_replace_zip_directory_cache_data = \1862_remove_and_clear_zip_directory_cache_data1863else:18641865def _replace_zip_directory_cache_data(normalized_path):1866def replace_cached_zip_archive_directory_data(path, old_entry):1867# N.B. In theory, we could load the zip directory information just1868# once for all updated path spellings, and then copy it locally and1869# update its contained path strings to contain the correct1870# spelling, but that seems like a way too invasive move (this cache1871# structure is not officially documented anywhere and could in1872# theory change with new Python releases) for no significant1873# benefit.1874old_entry.clear()1875zipimport.zipimporter(path)1876old_entry.update(zipimport._zip_directory_cache[path])1877return old_entry18781879_update_zipimporter_cache(1880normalized_path, zipimport._zip_directory_cache,1881updater=replace_cached_zip_archive_directory_data)188218831884def is_python(text, filename='<string>'):1885"Is this string a valid Python script?"1886try:1887compile(text, filename, 'exec')1888except (SyntaxError, TypeError):1889return False1890else:1891return True189218931894def is_sh(executable):1895"""Determine if the specified executable is a .sh (contains a #! line)"""1896try:1897with io.open(executable, encoding='latin-1') as fp:1898magic = fp.read(2)1899except (OSError, IOError):1900return executable1901return magic == '#!'190219031904def nt_quote_arg(arg):1905"""Quote a command line argument according to Windows parsing rules"""1906return subprocess.list2cmdline([arg])190719081909def is_python_script(script_text, filename):1910"""Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.1911"""1912if filename.endswith('.py') or filename.endswith('.pyw'):1913return True # extension says it's Python1914if is_python(script_text, filename):1915return True # it's syntactically valid Python1916if script_text.startswith('#!'):1917# It begins with a '#!' line, so check if 'python' is in it somewhere1918return 'python' in script_text.splitlines()[0].lower()19191920return False # Not any Python I can recognize192119221923try:1924from os import chmod as _chmod1925except ImportError:1926# Jython compatibility1927def _chmod(*args):1928pass192919301931def chmod(path, mode):1932log.debug("changing mode of %s to %o", path, mode)1933try:1934_chmod(path, mode)1935except os.error as e:1936log.debug("chmod failed: %s", e)193719381939class CommandSpec(list):1940"""1941A command spec for a #! header, specified as a list of arguments akin to1942those passed to Popen.1943"""19441945options = []1946split_args = dict()19471948@classmethod1949def best(cls):1950"""1951Choose the best CommandSpec class based on environmental conditions.1952"""1953return cls19541955@classmethod1956def _sys_executable(cls):1957_default = os.path.normpath(sys.executable)1958return os.environ.get('__PYVENV_LAUNCHER__', _default)19591960@classmethod1961def from_param(cls, param):1962"""1963Construct a CommandSpec from a parameter to build_scripts, which may1964be None.1965"""1966if isinstance(param, cls):1967return param1968if isinstance(param, list):1969return cls(param)1970if param is None:1971return cls.from_environment()1972# otherwise, assume it's a string.1973return cls.from_string(param)19741975@classmethod1976def from_environment(cls):1977return cls([cls._sys_executable()])19781979@classmethod1980def from_string(cls, string):1981"""1982Construct a command spec from a simple string representing a command1983line parseable by shlex.split.1984"""1985items = shlex.split(string, **cls.split_args)1986return cls(items)19871988def install_options(self, script_text):1989self.options = shlex.split(self._extract_options(script_text))1990cmdline = subprocess.list2cmdline(self)1991if not isascii(cmdline):1992self.options[:0] = ['-x']19931994@staticmethod1995def _extract_options(orig_script):1996"""1997Extract any options from the first line of the script.1998"""1999first = (orig_script + '\n').splitlines()[0]2000match = _first_line_re().match(first)2001options = match.group(1) or '' if match else ''2002return options.strip()20032004def as_header(self):2005return self._render(self + list(self.options))20062007@staticmethod2008def _strip_quotes(item):2009_QUOTES = '"\''2010for q in _QUOTES:2011if item.startswith(q) and item.endswith(q):2012return item[1:-1]2013return item20142015@staticmethod2016def _render(items):2017cmdline = subprocess.list2cmdline(2018CommandSpec._strip_quotes(item.strip()) for item in items)2019return '#!' + cmdline + '\n'202020212022# For pbr compat; will be removed in a future version.2023sys_executable = CommandSpec._sys_executable()202420252026class WindowsCommandSpec(CommandSpec):2027split_args = dict(posix=False)202820292030class ScriptWriter:2031"""2032Encapsulates behavior around writing entry point scripts for console and2033gui apps.2034"""20352036template = textwrap.dedent(r"""2037# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r2038import re2039import sys20402041# for compatibility with easy_install; see #21982042__requires__ = %(spec)r20432044try:2045from importlib.metadata import distribution2046except ImportError:2047try:2048from importlib_metadata import distribution2049except ImportError:2050from pkg_resources import load_entry_point205120522053def importlib_load_entry_point(spec, group, name):2054dist_name, _, _ = spec.partition('==')2055matches = (2056entry_point2057for entry_point in distribution(dist_name).entry_points2058if entry_point.group == group and entry_point.name == name2059)2060return next(matches).load()206120622063globals().setdefault('load_entry_point', importlib_load_entry_point)206420652066if __name__ == '__main__':2067sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])2068sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)())2069""").lstrip()20702071command_spec_class = CommandSpec20722073@classmethod2074def get_script_args(cls, dist, executable=None, wininst=False):2075# for backward compatibility2076warnings.warn("Use get_args", EasyInstallDeprecationWarning)2077writer = (WindowsScriptWriter if wininst else ScriptWriter).best()2078header = cls.get_script_header("", executable, wininst)2079return writer.get_args(dist, header)20802081@classmethod2082def get_script_header(cls, script_text, executable=None, wininst=False):2083# for backward compatibility2084warnings.warn(2085"Use get_header", EasyInstallDeprecationWarning, stacklevel=2)2086if wininst:2087executable = "python.exe"2088return cls.get_header(script_text, executable)20892090@classmethod2091def get_args(cls, dist, header=None):2092"""2093Yield write_script() argument tuples for a distribution's2094console_scripts and gui_scripts entry points.2095"""2096if header is None:2097header = cls.get_header()2098spec = str(dist.as_requirement())2099for type_ in 'console', 'gui':2100group = type_ + '_scripts'2101for name, ep in dist.get_entry_map(group).items():2102cls._ensure_safe_name(name)2103script_text = cls.template % locals()2104args = cls._get_script_args(type_, name, header, script_text)2105for res in args:2106yield res21072108@staticmethod2109def _ensure_safe_name(name):2110"""2111Prevent paths in *_scripts entry point names.2112"""2113has_path_sep = re.search(r'[\\/]', name)2114if has_path_sep:2115raise ValueError("Path separators not allowed in script names")21162117@classmethod2118def get_writer(cls, force_windows):2119# for backward compatibility2120warnings.warn("Use best", EasyInstallDeprecationWarning)2121return WindowsScriptWriter.best() if force_windows else cls.best()21222123@classmethod2124def best(cls):2125"""2126Select the best ScriptWriter for this environment.2127"""2128if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'):2129return WindowsScriptWriter.best()2130else:2131return cls21322133@classmethod2134def _get_script_args(cls, type_, name, header, script_text):2135# Simply write the stub with no extension.2136yield (name, header + script_text)21372138@classmethod2139def get_header(cls, script_text="", executable=None):2140"""Create a #! line, getting options (if any) from script_text"""2141cmd = cls.command_spec_class.best().from_param(executable)2142cmd.install_options(script_text)2143return cmd.as_header()214421452146class WindowsScriptWriter(ScriptWriter):2147command_spec_class = WindowsCommandSpec21482149@classmethod2150def get_writer(cls):2151# for backward compatibility2152warnings.warn("Use best", EasyInstallDeprecationWarning)2153return cls.best()21542155@classmethod2156def best(cls):2157"""2158Select the best ScriptWriter suitable for Windows2159"""2160writer_lookup = dict(2161executable=WindowsExecutableLauncherWriter,2162natural=cls,2163)2164# for compatibility, use the executable launcher by default2165launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable')2166return writer_lookup[launcher]21672168@classmethod2169def _get_script_args(cls, type_, name, header, script_text):2170"For Windows, add a .py extension"2171ext = dict(console='.pya', gui='.pyw')[type_]2172if ext not in os.environ['PATHEXT'].lower().split(';'):2173msg = (2174"{ext} not listed in PATHEXT; scripts will not be "2175"recognized as executables."2176).format(**locals())2177warnings.warn(msg, UserWarning)2178old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe']2179old.remove(ext)2180header = cls._adjust_header(type_, header)2181blockers = [name + x for x in old]2182yield name + ext, header + script_text, 't', blockers21832184@classmethod2185def _adjust_header(cls, type_, orig_header):2186"""2187Make sure 'pythonw' is used for gui and and 'python' is used for2188console (regardless of what sys.executable is).2189"""2190pattern = 'pythonw.exe'2191repl = 'python.exe'2192if type_ == 'gui':2193pattern, repl = repl, pattern2194pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE)2195new_header = pattern_ob.sub(string=orig_header, repl=repl)2196return new_header if cls._use_header(new_header) else orig_header21972198@staticmethod2199def _use_header(new_header):2200"""2201Should _adjust_header use the replaced header?22022203On non-windows systems, always use. On2204Windows systems, only use the replaced header if it resolves2205to an executable on the system.2206"""2207clean_header = new_header[2:-1].strip('"')2208return sys.platform != 'win32' or find_executable(clean_header)220922102211class WindowsExecutableLauncherWriter(WindowsScriptWriter):2212@classmethod2213def _get_script_args(cls, type_, name, header, script_text):2214"""2215For Windows, add a .py extension and an .exe launcher2216"""2217if type_ == 'gui':2218launcher_type = 'gui'2219ext = '-script.pyw'2220old = ['.pyw']2221else:2222launcher_type = 'cli'2223ext = '-script.py'2224old = ['.py', '.pyc', '.pyo']2225hdr = cls._adjust_header(type_, header)2226blockers = [name + x for x in old]2227yield (name + ext, hdr + script_text, 't', blockers)2228yield (2229name + '.exe', get_win_launcher(launcher_type),2230'b' # write in binary mode2231)2232if not is_64bit():2233# install a manifest for the launcher to prevent Windows2234# from detecting it as an installer (which it will for2235# launchers like easy_install.exe). Consider only2236# adding a manifest for launchers detected as installers.2237# See Distribute #143 for details.2238m_name = name + '.exe.manifest'2239yield (m_name, load_launcher_manifest(name), 't')224022412242# for backward-compatibility2243get_script_args = ScriptWriter.get_script_args2244get_script_header = ScriptWriter.get_script_header224522462247def get_win_launcher(type):2248"""2249Load the Windows launcher (executable) suitable for launching a script.22502251`type` should be either 'cli' or 'gui'22522253Returns the executable as a byte string.2254"""2255launcher_fn = '%s.exe' % type2256if is_64bit():2257launcher_fn = launcher_fn.replace(".", "-64.")2258else:2259launcher_fn = launcher_fn.replace(".", "-32.")2260return resource_string('setuptools', launcher_fn)226122622263def load_launcher_manifest(name):2264manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')2265if six.PY2:2266return manifest % vars()2267else:2268return manifest.decode('utf-8') % vars()226922702271def rmtree(path, ignore_errors=False, onerror=auto_chmod):2272return shutil.rmtree(path, ignore_errors, onerror)227322742275def current_umask():2276tmp = os.umask(0o022)2277os.umask(tmp)2278return tmp227922802281def bootstrap():2282# This function is called when setuptools*.egg is run using /bin/sh2283import setuptools22842285argv0 = os.path.dirname(setuptools.__path__[0])2286sys.argv[0] = argv02287sys.argv.append(argv0)2288main()228922902291def main(argv=None, **kw):2292from setuptools import setup2293from setuptools.dist import Distribution22942295class DistributionWithoutHelpCommands(Distribution):2296common_usage = ""22972298def _show_help(self, *args, **kw):2299with _patch_usage():2300Distribution._show_help(self, *args, **kw)23012302if argv is None:2303argv = sys.argv[1:]23042305with _patch_usage():2306setup(2307script_args=['-q', 'easy_install', '-v'] + argv,2308script_name=sys.argv[0] or 'easy_install',2309distclass=DistributionWithoutHelpCommands,2310**kw2311)231223132314@contextlib.contextmanager2315def _patch_usage():2316import distutils.core2317USAGE = textwrap.dedent("""2318usage: %(script)s [options] requirement_or_url ...2319or: %(script)s --help2320""").lstrip()23212322def gen_usage(script_name):2323return USAGE % dict(2324script=os.path.basename(script_name),2325)23262327saved = distutils.core.gen_usage2328distutils.core.gen_usage = gen_usage2329try:2330yield2331finally:2332distutils.core.gen_usage = saved233323342335class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning):2336"""2337Warning for EasyInstall deprecations, bypassing suppression.2338"""233923402341