Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
easy_install.py2362 linesDownload Raw Back to command
1"""2Easy Install3------------4 5A tool for doing automatic download/extract/build of distutils-based Python6packages.  For detailed documentation, see the accompanying EasyInstall.txt7file, or visit the `EasyInstall home page`__.8 9__ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html10 11"""12 13from glob import glob14from distutils.util import get_platform15from distutils.util import convert_path, subst_vars16from distutils.errors import (17    DistutilsArgError,18    DistutilsOptionError,19    DistutilsError,20    DistutilsPlatformError,21)22from distutils import log, dir_util23from distutils.command.build_scripts import first_line_re24from distutils.spawn import find_executable25from distutils.command import install26import sys27import os28from typing import Dict, List29import zipimport30import shutil31import tempfile32import zipfile33import re34import stat35import random36import textwrap37import warnings38import site39import struct40import contextlib41import subprocess42import shlex43import io44import configparser45import sysconfig46 47from sysconfig import get_path48 49from setuptools import Command50from setuptools.sandbox import run_setup51from setuptools.command import setopt52from setuptools.archive_util import unpack_archive53from setuptools.package_index import (54    PackageIndex,55    parse_requirement_arg,56    URL_SCHEME,57)58from setuptools.command import bdist_egg, egg_info59from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning60from setuptools.wheel import Wheel61from pkg_resources import (62    normalize_path,63    resource_string,64    get_distribution,65    find_distributions,66    Environment,67    Requirement,68    Distribution,69    PathMetadata,70    EggMetadata,71    WorkingSet,72    DistributionNotFound,73    VersionConflict,74    DEVELOP_DIST,75)76import pkg_resources77from ..compat import py39, py31178from .._path import ensure_directory79from ..extern.jaraco.text import yield_lines80 81 82# Turn on PEP440Warnings83warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)84 85__all__ = [86    'easy_install',87    'PthDistributions',88    'extract_wininst_cfg',89    'get_exe_prefixes',90]91 92 93def is_64bit():94    return struct.calcsize("P") == 895 96 97def _to_bytes(s):98    return s.encode('utf8')99 100 101def isascii(s):102    try:103        s.encode('ascii')104        return True105    except UnicodeError:106        return False107 108 109def _one_liner(text):110    return textwrap.dedent(text).strip().replace('\n', '; ')111 112 113class easy_install(Command):114    """Manage a download/build/install process"""115 116    description = "Find/get/install Python packages"117    command_consumes_arguments = True118 119    user_options = [120        ('prefix=', None, "installation prefix"),121        ("zip-ok", "z", "install package as a zipfile"),122        ("multi-version", "m", "make apps have to require() a version"),123        ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"),124        ("install-dir=", "d", "install package to DIR"),125        ("script-dir=", "s", "install scripts to DIR"),126        ("exclude-scripts", "x", "Don't install scripts"),127        ("always-copy", "a", "Copy all needed packages to install dir"),128        ("index-url=", "i", "base URL of Python Package Index"),129        ("find-links=", "f", "additional URL(s) to search for packages"),130        ("build-directory=", "b", "download/extract/build in DIR; keep the results"),131        (132            'optimize=',133            'O',134            "also compile with optimization: -O1 for \"python -O\", "135            "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",136        ),137        ('record=', None, "filename in which to record list of installed files"),138        ('always-unzip', 'Z', "don't install as a zipfile, no matter what"),139        ('site-dirs=', 'S', "list of directories where .pth files work"),140        ('editable', 'e', "Install specified packages in editable form"),141        ('no-deps', 'N', "don't install dependencies"),142        ('allow-hosts=', 'H', "pattern(s) that hostnames must match"),143        ('local-snapshots-ok', 'l', "allow building eggs from local checkouts"),144        ('version', None, "print version information and exit"),145        (146            'no-find-links',147            None,148            "Don't load find-links defined in packages being installed",149        ),150        ('user', None, "install in user site-package '%s'" % site.USER_SITE),151    ]152    boolean_options = [153        'zip-ok',154        'multi-version',155        'exclude-scripts',156        'upgrade',157        'always-copy',158        'editable',159        'no-deps',160        'local-snapshots-ok',161        'version',162        'user',163    ]164 165    negative_opt = {'always-unzip': 'zip-ok'}166    create_index = PackageIndex167 168    def initialize_options(self):169        EasyInstallDeprecationWarning.emit()170 171        # the --user option seems to be an opt-in one,172        # so the default should be False.173        self.user = 0174        self.zip_ok = self.local_snapshots_ok = None175        self.install_dir = self.script_dir = self.exclude_scripts = None176        self.index_url = None177        self.find_links = None178        self.build_directory = None179        self.args = None180        self.optimize = self.record = None181        self.upgrade = self.always_copy = self.multi_version = None182        self.editable = self.no_deps = self.allow_hosts = None183        self.root = self.prefix = self.no_report = None184        self.version = None185        self.install_purelib = None  # for pure module distributions186        self.install_platlib = None  # non-pure (dists w/ extensions)187        self.install_headers = None  # for C/C++ headers188        self.install_lib = None  # set to either purelib or platlib189        self.install_scripts = None190        self.install_data = None191        self.install_base = None192        self.install_platbase = None193        self.install_userbase = site.USER_BASE194        self.install_usersite = site.USER_SITE195        self.no_find_links = None196 197        # Options not specifiable via command line198        self.package_index = None199        self.pth_file = self.always_copy_from = None200        self.site_dirs = None201        self.installed_projects = {}202        # Always read easy_install options, even if we are subclassed, or have203        # an independent instance created.  This ensures that defaults will204        # always come from the standard configuration file(s)' "easy_install"205        # section, even if this is a "develop" or "install" command, or some206        # other embedding.207        self._dry_run = None208        self.verbose = self.distribution.verbose209        self.distribution._set_command_options(210            self, self.distribution.get_option_dict('easy_install')211        )212 213    def delete_blockers(self, blockers):214        extant_blockers = (215            filename216            for filename in blockers217            if os.path.exists(filename) or os.path.islink(filename)218        )219        list(map(self._delete_path, extant_blockers))220 221    def _delete_path(self, path):222        log.info("Deleting %s", path)223        if self.dry_run:224            return225 226        is_tree = os.path.isdir(path) and not os.path.islink(path)227        remover = _rmtree if is_tree else os.unlink228        remover(path)229 230    @staticmethod231    def _render_version():232        """233        Render the Setuptools version and installation details, then exit.234        """235        ver = '{}.{}'.format(*sys.version_info)236        dist = get_distribution('setuptools')237        tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'238        print(tmpl.format(**locals()))239        raise SystemExit()240 241    def finalize_options(self):  # noqa: C901  # is too complex (25)  # FIXME242        self.version and self._render_version()243 244        py_version = sys.version.split()[0]245 246        self.config_vars = dict(sysconfig.get_config_vars())247 248        self.config_vars.update({249            'dist_name': self.distribution.get_name(),250            'dist_version': self.distribution.get_version(),251            'dist_fullname': self.distribution.get_fullname(),252            'py_version': py_version,253            'py_version_short': f'{sys.version_info.major}.{sys.version_info.minor}',254            'py_version_nodot': f'{sys.version_info.major}{sys.version_info.minor}',255            'sys_prefix': self.config_vars['prefix'],256            'sys_exec_prefix': self.config_vars['exec_prefix'],257            # Only POSIX systems have abiflags258            'abiflags': getattr(sys, 'abiflags', ''),259            # Only python 3.9+ has platlibdir260            'platlibdir': getattr(sys, 'platlibdir', 'lib'),261        })262        with contextlib.suppress(AttributeError):263            # only for distutils outside stdlib264            self.config_vars.update({265                'implementation_lower': install._get_implementation().lower(),266                'implementation': install._get_implementation(),267            })268 269        # pypa/distutils#113 Python 3.9 compat270        self.config_vars.setdefault(271            'py_version_nodot_plat',272            getattr(sys, 'windir', '').replace('.', ''),273        )274 275        self.config_vars['userbase'] = self.install_userbase276        self.config_vars['usersite'] = self.install_usersite277        if self.user and not site.ENABLE_USER_SITE:278            log.warn("WARNING: The user site-packages directory is disabled.")279 280        self._fix_install_dir_for_user_site()281 282        self.expand_basedirs()283        self.expand_dirs()284 285        self._expand(286            'install_dir',287            'script_dir',288            'build_directory',289            'site_dirs',290        )291        # If a non-default installation directory was specified, default the292        # script directory to match it.293        if self.script_dir is None:294            self.script_dir = self.install_dir295 296        if self.no_find_links is None:297            self.no_find_links = False298 299        # Let install_dir get set by install_lib command, which in turn300        # gets its info from the install command, and takes into account301        # --prefix and --home and all that other crud.302        self.set_undefined_options('install_lib', ('install_dir', 'install_dir'))303        # Likewise, set default script_dir from 'install_scripts.install_dir'304        self.set_undefined_options('install_scripts', ('install_dir', 'script_dir'))305 306        if self.user and self.install_purelib:307            self.install_dir = self.install_purelib308            self.script_dir = self.install_scripts309        # default --record from the install command310        self.set_undefined_options('install', ('record', 'record'))311        self.all_site_dirs = get_site_dirs()312        self.all_site_dirs.extend(self._process_site_dirs(self.site_dirs))313 314        if not self.editable:315            self.check_site_dir()316        default_index = os.getenv("__EASYINSTALL_INDEX", "https://pypi.org/simple/")317        # ^ Private API for testing purposes only318        self.index_url = self.index_url or default_index319        self.shadow_path = self.all_site_dirs[:]320        for path_item in self.install_dir, normalize_path(self.script_dir):321            if path_item not in self.shadow_path:322                self.shadow_path.insert(0, path_item)323 324        if self.allow_hosts is not None:325            hosts = [s.strip() for s in self.allow_hosts.split(',')]326        else:327            hosts = ['*']328        if self.package_index is None:329            self.package_index = self.create_index(330                self.index_url,331                search_path=self.shadow_path,332                hosts=hosts,333            )334        self.local_index = Environment(self.shadow_path + sys.path)335 336        if self.find_links is not None:337            if isinstance(self.find_links, str):338                self.find_links = self.find_links.split()339        else:340            self.find_links = []341        if self.local_snapshots_ok:342            self.package_index.scan_egg_links(self.shadow_path + sys.path)343        if not self.no_find_links:344            self.package_index.add_find_links(self.find_links)345        self.set_undefined_options('install_lib', ('optimize', 'optimize'))346        self.optimize = self._validate_optimize(self.optimize)347 348        if self.editable and not self.build_directory:349            raise DistutilsArgError(350                "Must specify a build directory (-b) when using --editable"351            )352        if not self.args:353            raise DistutilsArgError(354                "No urls, filenames, or requirements specified (see --help)"355            )356 357        self.outputs = []358 359    @staticmethod360    def _process_site_dirs(site_dirs):361        if site_dirs is None:362            return363 364        normpath = map(normalize_path, sys.path)365        site_dirs = [os.path.expanduser(s.strip()) for s in site_dirs.split(',')]366        for d in site_dirs:367            if not os.path.isdir(d):368                log.warn("%s (in --site-dirs) does not exist", d)369            elif normalize_path(d) not in normpath:370                raise DistutilsOptionError(d + " (in --site-dirs) is not on sys.path")371            else:372                yield normalize_path(d)373 374    @staticmethod375    def _validate_optimize(value):376        try:377            value = int(value)378            if value not in range(3):379                raise ValueError380        except ValueError as e:381            raise DistutilsOptionError("--optimize must be 0, 1, or 2") from e382 383        return value384 385    def _fix_install_dir_for_user_site(self):386        """387        Fix the install_dir if "--user" was used.388        """389        if not self.user:390            return391 392        self.create_home_path()393        if self.install_userbase is None:394            msg = "User base directory is not specified"395            raise DistutilsPlatformError(msg)396        self.install_base = self.install_platbase = self.install_userbase397        scheme_name = f'{os.name}_user'398        self.select_scheme(scheme_name)399 400    def _expand_attrs(self, attrs):401        for attr in attrs:402            val = getattr(self, attr)403            if val is not None:404                if os.name == 'posix' or os.name == 'nt':405                    val = os.path.expanduser(val)406                val = subst_vars(val, self.config_vars)407                setattr(self, attr, val)408 409    def expand_basedirs(self):410        """Calls `os.path.expanduser` on install_base, install_platbase and411        root."""412        self._expand_attrs(['install_base', 'install_platbase', 'root'])413 414    def expand_dirs(self):415        """Calls `os.path.expanduser` on install dirs."""416        dirs = [417            'install_purelib',418            'install_platlib',419            'install_lib',420            'install_headers',421            'install_scripts',422            'install_data',423        ]424        self._expand_attrs(dirs)425 426    def run(self, show_deprecation=True):427        if show_deprecation:428            self.announce(429                "WARNING: The easy_install command is deprecated "430                "and will be removed in a future version.",431                log.WARN,432            )433        if self.verbose != self.distribution.verbose:434            log.set_verbosity(self.verbose)435        try:436            for spec in self.args:437                self.easy_install(spec, not self.no_deps)438            if self.record:439                outputs = self.outputs440                if self.root:  # strip any package prefix441                    root_len = len(self.root)442                    for counter in range(len(outputs)):443                        outputs[counter] = outputs[counter][root_len:]444                from distutils import file_util445 446                self.execute(447                    file_util.write_file,448                    (self.record, outputs),449                    "writing list of installed files to '%s'" % self.record,450                )451            self.warn_deprecated_options()452        finally:453            log.set_verbosity(self.distribution.verbose)454 455    def pseudo_tempname(self):456        """Return a pseudo-tempname base in the install directory.457        This code is intentionally naive; if a malicious party can write to458        the target directory you're already in deep doodoo.459        """460        try:461            pid = os.getpid()462        except Exception:463            pid = random.randint(0, sys.maxsize)464        return os.path.join(self.install_dir, "test-easy-install-%s" % pid)465 466    def warn_deprecated_options(self):467        pass468 469    def check_site_dir(self):  # noqa: C901  # is too complex (12)  # FIXME470        """Verify that self.install_dir is .pth-capable dir, if needed"""471 472        instdir = normalize_path(self.install_dir)473        pth_file = os.path.join(instdir, 'easy-install.pth')474 475        if not os.path.exists(instdir):476            try:477                os.makedirs(instdir)478            except OSError:479                self.cant_write_to_target()480 481        # Is it a configured, PYTHONPATH, implicit, or explicit site dir?482        is_site_dir = instdir in self.all_site_dirs483 484        if not is_site_dir and not self.multi_version:485            # No?  Then directly test whether it does .pth file processing486            is_site_dir = self.check_pth_processing()487        else:488            # make sure we can write to target dir489            testfile = self.pseudo_tempname() + '.write-test'490            test_exists = os.path.exists(testfile)491            try:492                if test_exists:493                    os.unlink(testfile)494                open(testfile, 'wb').close()495                os.unlink(testfile)496            except OSError:497                self.cant_write_to_target()498 499        if not is_site_dir and not self.multi_version:500            # Can't install non-multi to non-site dir with easy_install501            pythonpath = os.environ.get('PYTHONPATH', '')502            log.warn(self.__no_default_msg, self.install_dir, pythonpath)503 504        if is_site_dir:505            if self.pth_file is None:506                self.pth_file = PthDistributions(pth_file, self.all_site_dirs)507        else:508            self.pth_file = None509 510        if self.multi_version and not os.path.exists(pth_file):511            self.pth_file = None  # don't create a .pth file512        self.install_dir = instdir513 514    __cant_write_msg = textwrap.dedent(515        """516        can't create or remove files in install directory517 518        The following error occurred while trying to add or remove files in the519        installation directory:520 521            %s522 523        The installation directory you specified (via --install-dir, --prefix, or524        the distutils default setting) was:525 526            %s527        """528    ).lstrip()  # noqa529 530    __not_exists_id = textwrap.dedent(531        """532        This directory does not currently exist.  Please create it and try again, or533        choose a different installation directory (using the -d or --install-dir534        option).535        """536    ).lstrip()  # noqa537 538    __access_msg = textwrap.dedent(539        """540        Perhaps your account does not have write access to this directory?  If the541        installation directory is a system-owned directory, you may need to sign in542        as the administrator or "root" account.  If you do not have administrative543        access to this machine, you may wish to choose a different installation544        directory, preferably one that is listed in your PYTHONPATH environment545        variable.546 547        For information on other options, you may wish to consult the548        documentation at:549 550          https://setuptools.pypa.io/en/latest/deprecated/easy_install.html551 552        Please make the appropriate changes for your system and try again.553        """554    ).lstrip()  # noqa555 556    def cant_write_to_target(self):557        msg = self.__cant_write_msg % (558            sys.exc_info()[1],559            self.install_dir,560        )561 562        if not os.path.exists(self.install_dir):563            msg += '\n' + self.__not_exists_id564        else:565            msg += '\n' + self.__access_msg566        raise DistutilsError(msg)567 568    def check_pth_processing(self):  # noqa: C901569        """Empirically verify whether .pth files are supported in inst. dir"""570        instdir = self.install_dir571        log.info("Checking .pth file support in %s", instdir)572        pth_file = self.pseudo_tempname() + ".pth"573        ok_file = pth_file + '.ok'574        ok_exists = os.path.exists(ok_file)575        tmpl = (576            _one_liner(577                """578            import os579            f = open({ok_file!r}, 'w', encoding="utf-8")580            f.write('OK')581            f.close()582            """583            )584            + '\n'585        )586        try:587            if ok_exists:588                os.unlink(ok_file)589            dirname = os.path.dirname(ok_file)590            os.makedirs(dirname, exist_ok=True)591            f = open(pth_file, 'w', encoding=py39.LOCALE_ENCODING)592            # ^-- Requires encoding="locale" instead of "utf-8" (python/cpython#77102).593        except OSError:594            self.cant_write_to_target()595        else:596            try:597                f.write(tmpl.format(**locals()))598                f.close()599                f = None600                executable = sys.executable601                if os.name == 'nt':602                    dirname, basename = os.path.split(executable)603                    alt = os.path.join(dirname, 'pythonw.exe')604                    use_alt = basename.lower() == 'python.exe' and os.path.exists(alt)605                    if use_alt:606                        # use pythonw.exe to avoid opening a console window607                        executable = alt608 609                from distutils.spawn import spawn610 611                spawn([executable, '-E', '-c', 'pass'], 0)612 613                if os.path.exists(ok_file):614                    log.info("TEST PASSED: %s appears to support .pth files", instdir)615                    return True616            finally:617                if f:618                    f.close()619                if os.path.exists(ok_file):620                    os.unlink(ok_file)621                if os.path.exists(pth_file):622                    os.unlink(pth_file)623        if not self.multi_version:624            log.warn("TEST FAILED: %s does NOT support .pth files", instdir)625        return False626 627    def install_egg_scripts(self, dist):628        """Write all the scripts for `dist`, unless scripts are excluded"""629        if not self.exclude_scripts and dist.metadata_isdir('scripts'):630            for script_name in dist.metadata_listdir('scripts'):631                if dist.metadata_isdir('scripts/' + script_name):632                    # The "script" is a directory, likely a Python 3633                    # __pycache__ directory, so skip it.634                    continue635                self.install_script(636                    dist, script_name, dist.get_metadata('scripts/' + script_name)637                )638        self.install_wrapper_scripts(dist)639 640    def add_output(self, path):641        if os.path.isdir(path):642            for base, dirs, files in os.walk(path):643                for filename in files:644                    self.outputs.append(os.path.join(base, filename))645        else:646            self.outputs.append(path)647 648    def not_editable(self, spec):649        if self.editable:650            raise DistutilsArgError(651                "Invalid argument %r: you can't use filenames or URLs "652                "with --editable (except via the --find-links option)." % (spec,)653            )654 655    def check_editable(self, spec):656        if not self.editable:657            return658 659        if os.path.exists(os.path.join(self.build_directory, spec.key)):660            raise DistutilsArgError(661                "%r already exists in %s; can't do a checkout there"662                % (spec.key, self.build_directory)663            )664 665    @contextlib.contextmanager666    def _tmpdir(self):667        tmpdir = tempfile.mkdtemp(prefix="easy_install-")668        try:669            # cast to str as workaround for #709 and #710 and #712670            yield str(tmpdir)671        finally:672            os.path.exists(tmpdir) and _rmtree(tmpdir)673 674    def easy_install(self, spec, deps=False):675        with self._tmpdir() as tmpdir:676            if not isinstance(spec, Requirement):677                if URL_SCHEME(spec):678                    # It's a url, download it to tmpdir and process679                    self.not_editable(spec)680                    dl = self.package_index.download(spec, tmpdir)681                    return self.install_item(None, dl, tmpdir, deps, True)682 683                elif os.path.exists(spec):684                    # Existing file or directory, just process it directly685                    self.not_editable(spec)686                    return self.install_item(None, spec, tmpdir, deps, True)687                else:688                    spec = parse_requirement_arg(spec)689 690            self.check_editable(spec)691            dist = self.package_index.fetch_distribution(692                spec,693                tmpdir,694                self.upgrade,695                self.editable,696                not self.always_copy,697                self.local_index,698            )699            if dist is None:700                msg = "Could not find suitable distribution for %r" % spec701                if self.always_copy:702                    msg += " (--always-copy skips system and development eggs)"703                raise DistutilsError(msg)704            elif dist.precedence == DEVELOP_DIST:705                # .egg-info dists don't need installing, just process deps706                self.process_distribution(spec, dist, deps, "Using")707                return dist708            else:709                return self.install_item(spec, dist.location, tmpdir, deps)710 711    def install_item(self, spec, download, tmpdir, deps, install_needed=False):712        # Installation is also needed if file in tmpdir or is not an egg713        install_needed = install_needed or self.always_copy714        install_needed = install_needed or os.path.dirname(download) == tmpdir715        install_needed = install_needed or not download.endswith('.egg')716        install_needed = install_needed or (717            self.always_copy_from is not None718            and os.path.dirname(normalize_path(download))719            == normalize_path(self.always_copy_from)720        )721 722        if spec and not install_needed:723            # at this point, we know it's a local .egg, we just don't know if724            # it's already installed.725            for dist in self.local_index[spec.project_name]:726                if dist.location == download:727                    break728            else:729                install_needed = True  # it's not in the local index730 731        log.info("Processing %s", os.path.basename(download))732 733        if install_needed:734            dists = self.install_eggs(spec, download, tmpdir)735            for dist in dists:736                self.process_distribution(spec, dist, deps)737        else:738            dists = [self.egg_distribution(download)]739            self.process_distribution(spec, dists[0], deps, "Using")740 741        if spec is not None:742            for dist in dists:743                if dist in spec:744                    return dist745        return None746 747    def select_scheme(self, name):748        try:749            install._select_scheme(self, name)750        except AttributeError:751            # stdlib distutils752            install.install.select_scheme(self, name.replace('posix', 'unix'))753 754    # FIXME: 'easy_install.process_distribution' is too complex (12)755    def process_distribution(  # noqa: C901756        self,757        requirement,758        dist,759        deps=True,760        *info,761    ):762        self.update_pth(dist)763        self.package_index.add(dist)764        if dist in self.local_index[dist.key]:765            self.local_index.remove(dist)766        self.local_index.add(dist)767        self.install_egg_scripts(dist)768        self.installed_projects[dist.key] = dist769        log.info(self.installation_report(requirement, dist, *info))770        if dist.has_metadata('dependency_links.txt') and not self.no_find_links:771            self.package_index.add_find_links(772                dist.get_metadata_lines('dependency_links.txt')773            )774        if not deps and not self.always_copy:775            return776        elif requirement is not None and dist.key != requirement.key:777            log.warn("Skipping dependencies for %s", dist)778            return  # XXX this is not the distribution we were looking for779        elif requirement is None or dist not in requirement:780            # if we wound up with a different version, resolve what we've got781            distreq = dist.as_requirement()782            requirement = Requirement(str(distreq))783        log.info("Processing dependencies for %s", requirement)784        try:785            distros = WorkingSet([]).resolve(786                [requirement], self.local_index, self.easy_install787            )788        except DistributionNotFound as e:789            raise DistutilsError(str(e)) from e790        except VersionConflict as e:791            raise DistutilsError(e.report()) from e792        if self.always_copy or self.always_copy_from:793            # Force all the relevant distros to be copied or activated794            for dist in distros:795                if dist.key not in self.installed_projects:796                    self.easy_install(dist.as_requirement())797        log.info("Finished processing dependencies for %s", requirement)798 799    def should_unzip(self, dist):800        if self.zip_ok is not None:801            return not self.zip_ok802        if dist.has_metadata('not-zip-safe'):803            return True804        if not dist.has_metadata('zip-safe'):805            return True806        return False807 808    def maybe_move(self, spec, dist_filename, setup_base):809        dst = os.path.join(self.build_directory, spec.key)810        if os.path.exists(dst):811            msg = "%r already exists in %s; build directory %s will not be kept"812            log.warn(msg, spec.key, self.build_directory, setup_base)813            return setup_base814        if os.path.isdir(dist_filename):815            setup_base = dist_filename816        else:817            if os.path.dirname(dist_filename) == setup_base:818                os.unlink(dist_filename)  # get it out of the tmp dir819            contents = os.listdir(setup_base)820            if len(contents) == 1:821                dist_filename = os.path.join(setup_base, contents[0])822                if os.path.isdir(dist_filename):823                    # if the only thing there is a directory, move it instead824                    setup_base = dist_filename825        ensure_directory(dst)826        shutil.move(setup_base, dst)827        return dst828 829    def install_wrapper_scripts(self, dist):830        if self.exclude_scripts:831            return832        for args in ScriptWriter.best().get_args(dist):833            self.write_script(*args)834 835    def install_script(self, dist, script_name, script_text, dev_path=None):836        """Generate a legacy script wrapper and install it"""837        spec = str(dist.as_requirement())838        is_script = is_python_script(script_text, script_name)839 840        if is_script:841            body = self._load_template(dev_path) % locals()842            script_text = ScriptWriter.get_header(script_text) + body843        self.write_script(script_name, _to_bytes(script_text), 'b')844 845    @staticmethod846    def _load_template(dev_path):847        """848        There are a couple of template scripts in the package. This849        function loads one of them and prepares it for use.850        """851        # See https://github.com/pypa/setuptools/issues/134 for info852        # on script file naming and downstream issues with SVR4853        name = 'script.tmpl'854        if dev_path:855            name = name.replace('.tmpl', ' (dev).tmpl')856 857        raw_bytes = resource_string('setuptools', name)858        return raw_bytes.decode('utf-8')859 860    def write_script(self, script_name, contents, mode="t", blockers=()):861        """Write an executable file to the scripts directory"""862        self.delete_blockers(  # clean up old .py/.pyw w/o a script863            [os.path.join(self.script_dir, x) for x in blockers]864        )865        log.info("Installing %s script to %s", script_name, self.script_dir)866        target = os.path.join(self.script_dir, script_name)867        self.add_output(target)868 869        if self.dry_run:870            return871 872        mask = current_umask()873        ensure_directory(target)874        if os.path.exists(target):875            os.unlink(target)876        with open(target, "w" + mode) as f:  # TODO: is it safe to use utf-8?877            f.write(contents)878        chmod(target, 0o777 - mask)879 880    def install_eggs(self, spec, dist_filename, tmpdir):881        # .egg dirs or files are already built, so just return them882        installer_map = {883            '.egg': self.install_egg,884            '.exe': self.install_exe,885            '.whl': self.install_wheel,886        }887        try:888            install_dist = installer_map[dist_filename.lower()[-4:]]889        except KeyError:890            pass891        else:892            return [install_dist(dist_filename, tmpdir)]893 894        # Anything else, try to extract and build895        setup_base = tmpdir896        if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'):897            unpack_archive(dist_filename, tmpdir, self.unpack_progress)898        elif os.path.isdir(dist_filename):899            setup_base = os.path.abspath(dist_filename)900 901        if (902            setup_base.startswith(tmpdir)  # something we downloaded903            and self.build_directory904            and spec is not None905        ):906            setup_base = self.maybe_move(spec, dist_filename, setup_base)907 908        # Find the setup.py file909        setup_script = os.path.join(setup_base, 'setup.py')910 911        if not os.path.exists(setup_script):912            setups = glob(os.path.join(setup_base, '*', 'setup.py'))913            if not setups:914                raise DistutilsError(915                    "Couldn't find a setup script in %s"916                    % os.path.abspath(dist_filename)917                )918            if len(setups) > 1:919                raise DistutilsError(920                    "Multiple setup scripts in %s" % os.path.abspath(dist_filename)921                )922            setup_script = setups[0]923 924        # Now run it, and return the result925        if self.editable:926            log.info(self.report_editable(spec, setup_script))927            return []928        else:929            return self.build_and_install(setup_script, setup_base)930 931    def egg_distribution(self, egg_path):932        if os.path.isdir(egg_path):933            metadata = PathMetadata(egg_path, os.path.join(egg_path, 'EGG-INFO'))934        else:935            metadata = EggMetadata(zipimport.zipimporter(egg_path))936        return Distribution.from_filename(egg_path, metadata=metadata)937 938    # FIXME: 'easy_install.install_egg' is too complex (11)939    def install_egg(self, egg_path, tmpdir):  # noqa: C901940        destination = os.path.join(941            self.install_dir,942            os.path.basename(egg_path),943        )944        destination = os.path.abspath(destination)945        if not self.dry_run:946            ensure_directory(destination)947 948        dist = self.egg_distribution(egg_path)949        if not (950            os.path.exists(destination) and os.path.samefile(egg_path, destination)951        ):952            if os.path.isdir(destination) and not os.path.islink(destination):953                dir_util.remove_tree(destination, dry_run=self.dry_run)954            elif os.path.exists(destination):955                self.execute(956                    os.unlink,957                    (destination,),958                    "Removing " + destination,959                )960            try:961                new_dist_is_zipped = False962                if os.path.isdir(egg_path):963                    if egg_path.startswith(tmpdir):964                        f, m = shutil.move, "Moving"965                    else:966                        f, m = shutil.copytree, "Copying"967                elif self.should_unzip(dist):968                    self.mkpath(destination)969                    f, m = self.unpack_and_compile, "Extracting"970                else:971                    new_dist_is_zipped = True972                    if egg_path.startswith(tmpdir):973                        f, m = shutil.move, "Moving"974                    else:975                        f, m = shutil.copy2, "Copying"976                self.execute(977                    f,978                    (egg_path, destination),979                    (m + " %s to %s")980                    % (os.path.basename(egg_path), os.path.dirname(destination)),981                )982                update_dist_caches(983                    destination,984                    fix_zipimporter_caches=new_dist_is_zipped,985                )986            except Exception:987                update_dist_caches(destination, fix_zipimporter_caches=False)988                raise989 990        self.add_output(destination)991        return self.egg_distribution(destination)992 993    def install_exe(self, dist_filename, tmpdir):994        # See if it's valid, get data995        cfg = extract_wininst_cfg(dist_filename)996        if cfg is None:997            raise DistutilsError(998                "%s is not a valid distutils Windows .exe" % dist_filename999            )1000        # Create a dummy distribution object until we build the real distro1001        dist = Distribution(1002            None,1003            project_name=cfg.get('metadata', 'name'),1004            version=cfg.get('metadata', 'version'),1005            platform=get_platform(),1006        )1007 1008        # Convert the .exe to an unpacked egg1009        egg_path = os.path.join(tmpdir, dist.egg_name() + '.egg')1010        dist.location = egg_path1011        egg_tmp = egg_path + '.tmp'1012        _egg_info = os.path.join(egg_tmp, 'EGG-INFO')1013        pkg_inf = os.path.join(_egg_info, 'PKG-INFO')1014        ensure_directory(pkg_inf)  # make sure EGG-INFO dir exists1015        dist._provider = PathMetadata(egg_tmp, _egg_info)  # XXX1016        self.exe_to_egg(dist_filename, egg_tmp)1017 1018        # Write EGG-INFO/PKG-INFO1019        if not os.path.exists(pkg_inf):1020            f = open(pkg_inf, 'w')  # TODO: probably it is safe to use utf-81021            f.write('Metadata-Version: 1.0\n')1022            for k, v in cfg.items('metadata'):1023                if k != 'target_version':1024                    f.write('%s: %s\n' % (k.replace('_', '-').title(), v))1025            f.close()1026        script_dir = os.path.join(_egg_info, 'scripts')1027        # delete entry-point scripts to avoid duping1028        self.delete_blockers([1029            os.path.join(script_dir, args[0]) for args in ScriptWriter.get_args(dist)1030        ])1031        # Build .egg file from tmpdir1032        bdist_egg.make_zipfile(1033            egg_path,1034            egg_tmp,1035            verbose=self.verbose,1036            dry_run=self.dry_run,1037        )1038        # install the .egg1039        return self.install_egg(egg_path, tmpdir)1040 1041    # FIXME: 'easy_install.exe_to_egg' is too complex (12)1042    def exe_to_egg(self, dist_filename, egg_tmp):  # noqa: C9011043        """Extract a bdist_wininst to the directories an egg would use"""1044        # Check for .pth file and set up prefix translations1045        prefixes = get_exe_prefixes(dist_filename)1046        to_compile = []1047        native_libs = []1048        top_level = {}1049 1050        def process(src, dst):1051            s = src.lower()1052            for old, new in prefixes:1053                if s.startswith(old):1054                    src = new + src[len(old) :]1055                    parts = src.split('/')1056                    dst = os.path.join(egg_tmp, *parts)1057                    dl = dst.lower()1058                    if dl.endswith('.pyd') or dl.endswith('.dll'):1059                        parts[-1] = bdist_egg.strip_module(parts[-1])1060                        top_level[os.path.splitext(parts[0])[0]] = 11061                        native_libs.append(src)1062                    elif dl.endswith('.py') and old != 'SCRIPTS/':1063                        top_level[os.path.splitext(parts[0])[0]] = 11064                        to_compile.append(dst)1065                    return dst1066            if not src.endswith('.pth'):1067                log.warn("WARNING: can't process %s", src)1068            return None1069 1070        # extract, tracking .pyd/.dll->native_libs and .py -> to_compile1071        unpack_archive(dist_filename, egg_tmp, process)1072        stubs = []1073        for res in native_libs:1074            if res.lower().endswith('.pyd'):  # create stubs for .pyd's1075                parts = res.split('/')1076                resource = parts[-1]1077                parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py'1078                pyfile = os.path.join(egg_tmp, *parts)1079                to_compile.append(pyfile)1080                stubs.append(pyfile)1081                bdist_egg.write_stub(resource, pyfile)1082        self.byte_compile(to_compile)  # compile .py's1083        bdist_egg.write_safety_flag(1084            os.path.join(egg_tmp, 'EGG-INFO'), bdist_egg.analyze_egg(egg_tmp, stubs)1085        )  # write zip-safety flag1086 1087        for name in 'top_level', 'native_libs':1088            if locals()[name]:1089                txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt')1090                if not os.path.exists(txt):1091                    f = open(txt, 'w')  # TODO: probably it is safe to use utf-81092                    f.write('\n'.join(locals()[name]) + '\n')1093                    f.close()1094 1095    def install_wheel(self, wheel_path, tmpdir):1096        wheel = Wheel(wheel_path)1097        assert wheel.is_compatible()1098        destination = os.path.join(self.install_dir, wheel.egg_name())1099        destination = os.path.abspath(destination)1100        if not self.dry_run:1101            ensure_directory(destination)1102        if os.path.isdir(destination) and not os.path.islink(destination):1103            dir_util.remove_tree(destination, dry_run=self.dry_run)1104        elif os.path.exists(destination):1105            self.execute(1106                os.unlink,1107                (destination,),1108                "Removing " + destination,1109            )1110        try:1111            self.execute(1112                wheel.install_as_egg,1113                (destination,),1114                ("Installing %s to %s")1115                % (os.path.basename(wheel_path), os.path.dirname(destination)),1116            )1117        finally:1118            update_dist_caches(destination, fix_zipimporter_caches=False)1119        self.add_output(destination)1120        return self.egg_distribution(destination)1121 1122    __mv_warning = textwrap.dedent(1123        """1124        Because this distribution was installed --multi-version, before you can1125        import modules from this package in an application, you will need to1126        'import pkg_resources' and then use a 'require()' call similar to one of1127        these examples, in order to select the desired version:1128 1129            pkg_resources.require("%(name)s")  # latest installed version1130            pkg_resources.require("%(name)s==%(version)s")  # this exact version1131            pkg_resources.require("%(name)s>=%(version)s")  # this version or higher1132        """1133    ).lstrip()  # noqa1134 1135    __id_warning = textwrap.dedent(1136        """1137        Note also that the installation directory must be on sys.path at runtime for1138        this to work.  (e.g. by being the application's script directory, by being on1139        PYTHONPATH, or by being added to sys.path by your code.)1140        """1141    )  # noqa1142 1143    def installation_report(self, req, dist, what="Installed"):1144        """Helpful installation message for display to package users"""1145        msg = "\n%(what)s %(eggloc)s%(extras)s"1146        if self.multi_version and not self.no_report:1147            msg += '\n' + self.__mv_warning1148            if self.install_dir not in map(normalize_path, sys.path):1149                msg += '\n' + self.__id_warning1150 1151        eggloc = dist.location1152        name = dist.project_name1153        version = dist.version1154        extras = ''  # TODO: self.report_extras(req, dist)1155        return msg % locals()1156 1157    __editable_msg = textwrap.dedent(1158        """1159        Extracted editable version of %(spec)s to %(dirname)s1160 1161        If it uses setuptools in its setup script, you can activate it in1162        "development" mode by going to that directory and running::1163 1164            %(python)s setup.py develop1165 1166        See the setuptools documentation for the "develop" command for more info.1167        """1168    ).lstrip()  # noqa1169 1170    def report_editable(self, spec, setup_script):1171        dirname = os.path.dirname(setup_script)1172        python = sys.executable1173        return '\n' + self.__editable_msg % locals()1174 1175    def run_setup(self, setup_script, setup_base, args):1176        sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)1177        sys.modules.setdefault('distutils.command.egg_info', egg_info)1178 1179        args = list(args)1180        if self.verbose > 2:1181            v = 'v' * (self.verbose - 1)1182            args.insert(0, '-' + v)1183        elif self.verbose < 2:1184            args.insert(0, '-q')1185        if self.dry_run:1186            args.insert(0, '-n')1187        log.info("Running %s %s", setup_script[len(setup_base) + 1 :], ' '.join(args))1188        try:1189            run_setup(setup_script, args)1190        except SystemExit as v:1191            raise DistutilsError("Setup script exited with %s" % (v.args[0],)) from v1192 1193    def build_and_install(self, setup_script, setup_base):1194        args = ['bdist_egg', '--dist-dir']1195 1196        dist_dir = tempfile.mkdtemp(1197            prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script)1198        )1199        try:1200            self._set_fetcher_options(os.path.dirname(setup_script))

Showing the first 1,200 of 2362 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai