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