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