1# coding: utf-8
2"""
3Package resource API
4--------------------
5
6A resource is a logical file contained within a package, or a logical
7subdirectory thereof.  The package resource API expects resource names
8to have their path parts separated with ``/``, *not* whatever the local
9path separator is.  Do not use os.path operations to manipulate resource
10names being passed into the API.
11
12The package resource API is designed to work with normal filesystem packages,
13.egg files, and unpacked .egg files.  It can also work in a limited way with
14.zip files and with custom PEP 302 loaders that support the ``get_data()``
15method.
16"""
17
18from __future__ import absolute_import
19
20import sys
21import os
22import io
23import time
24import re
25import types
26import zipfile
27import zipimport
28import warnings
29import stat
30import functools
31import pkgutil
32import operator
33import platform
34import collections
35import plistlib
36import email.parser
37import tempfile
38import textwrap
39import itertools
40from pkgutil import get_importer
41
42try:
43    import _imp
44except ImportError:
45    # Python 3.2 compatibility
46    import imp as _imp
47
48import six
49from six.moves import urllib, map, filter
50
51# capture these to bypass sandboxing
52from os import utime
53try:
54    from os import mkdir, rename, unlink
55    WRITE_SUPPORT = True
56except ImportError:
57    # no write support, probably under GAE
58    WRITE_SUPPORT = False
59
60from os import open as os_open
61from os.path import isdir, split
62
63try:
64    import importlib.machinery as importlib_machinery
65    # access attribute to force import under delayed import mechanisms.
66    importlib_machinery.__name__
67except ImportError:
68    importlib_machinery = None
69
70import packaging.version
71import packaging.specifiers
72import packaging.requirements
73import packaging.markers
74import appdirs
75
76if (3, 0) < sys.version_info < (3, 3):
77    raise RuntimeError("Python 3.3 or later is required")
78
79# declare some globals that will be defined later to
80# satisfy the linters.
81require = None
82working_set = None
83
84
85class PEP440Warning(RuntimeWarning):
86    """
87    Used when there is an issue with a version or specifier not complying with
88    PEP 440.
89    """
90
91
92class _SetuptoolsVersionMixin(object):
93    def __hash__(self):
94        return super(_SetuptoolsVersionMixin, self).__hash__()
95
96    def __lt__(self, other):
97        if isinstance(other, tuple):
98            return tuple(self) < other
99        else:
100            return super(_SetuptoolsVersionMixin, self).__lt__(other)
101
102    def __le__(self, other):
103        if isinstance(other, tuple):
104            return tuple(self) <= other
105        else:
106            return super(_SetuptoolsVersionMixin, self).__le__(other)
107
108    def __eq__(self, other):
109        if isinstance(other, tuple):
110            return tuple(self) == other
111        else:
112            return super(_SetuptoolsVersionMixin, self).__eq__(other)
113
114    def __ge__(self, other):
115        if isinstance(other, tuple):
116            return tuple(self) >= other
117        else:
118            return super(_SetuptoolsVersionMixin, self).__ge__(other)
119
120    def __gt__(self, other):
121        if isinstance(other, tuple):
122            return tuple(self) > other
123        else:
124            return super(_SetuptoolsVersionMixin, self).__gt__(other)
125
126    def __ne__(self, other):
127        if isinstance(other, tuple):
128            return tuple(self) != other
129        else:
130            return super(_SetuptoolsVersionMixin, self).__ne__(other)
131
132    def __getitem__(self, key):
133        return tuple(self)[key]
134
135    def __iter__(self):
136        component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
137        replace = {
138            'pre': 'c',
139            'preview': 'c',
140            '-': 'final-',
141            'rc': 'c',
142            'dev': '@',
143        }.get
144
145        def _parse_version_parts(s):
146            for part in component_re.split(s):
147                part = replace(part, part)
148                if not part or part == '.':
149                    continue
150                if part[:1] in '0123456789':
151                    # pad for numeric comparison
152                    yield part.zfill(8)
153                else:
154                    yield '*' + part
155
156            # ensure that alpha/beta/candidate are before final
157            yield '*final'
158
159        def old_parse_version(s):
160            parts = []
161            for part in _parse_version_parts(s.lower()):
162                if part.startswith('*'):
163                    # remove '-' before a prerelease tag
164                    if part < '*final':
165                        while parts and parts[-1] == '*final-':
166                            parts.pop()
167                    # remove trailing zeros from each series of numeric parts
168                    while parts and parts[-1] == '00000000':
169                        parts.pop()
170                parts.append(part)
171            return tuple(parts)
172
173        # Warn for use of this function
174        warnings.warn(
175            "You have iterated over the result of "
176            "pkg_resources.parse_version. This is a legacy behavior which is "
177            "inconsistent with the new version class introduced in setuptools "
178            "8.0. In most cases, conversion to a tuple is unnecessary. For "
179            "comparison of versions, sort the Version instances directly. If "
180            "you have another use case requiring the tuple, please file a "
181            "bug with the setuptools project describing that need.",
182            RuntimeWarning,
183            stacklevel=1,
184        )
185
186        for part in old_parse_version(str(self)):
187            yield part
188
189
190class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version):
191    pass
192
193
194class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin,
195                              packaging.version.LegacyVersion):
196    pass
197
198
199def parse_version(v):
200    try:
201        return SetuptoolsVersion(v)
202    except packaging.version.InvalidVersion:
203        return SetuptoolsLegacyVersion(v)
204
205
206_state_vars = {}
207
208
209def _declare_state(vartype, **kw):
210    globals().update(kw)
211    _state_vars.update(dict.fromkeys(kw, vartype))
212
213
214def __getstate__():
215    state = {}
216    g = globals()
217    for k, v in _state_vars.items():
218        state[k] = g['_sget_' + v](g[k])
219    return state
220
221
222def __setstate__(state):
223    g = globals()
224    for k, v in state.items():
225        g['_sset_' + _state_vars[k]](k, g[k], v)
226    return state
227
228
229def _sget_dict(val):
230    return val.copy()
231
232
233def _sset_dict(key, ob, state):
234    ob.clear()
235    ob.update(state)
236
237
238def _sget_object(val):
239    return val.__getstate__()
240
241
242def _sset_object(key, ob, state):
243    ob.__setstate__(state)
244
245
246_sget_none = _sset_none = lambda *args: None
247
248
249def get_supported_platform():
250    """Return this platform's maximum compatible version.
251
252    distutils.util.get_platform() normally reports the minimum version
253    of Mac OS X that would be required to *use* extensions produced by
254    distutils.  But what we want when checking compatibility is to know the
255    version of Mac OS X that we are *running*.  To allow usage of packages that
256    explicitly require a newer version of Mac OS X, we must also know the
257    current version of the OS.
258
259    If this condition occurs for any other platform with a version in its
260    platform strings, this function should be extended accordingly.
261    """
262    plat = get_build_platform()
263    m = macosVersionString.match(plat)
264    if m is not None and sys.platform == "darwin":
265        try:
266            plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3))
267        except ValueError:
268            # not Mac OS X
269            pass
270    return plat
271
272
273__all__ = [
274    # Basic resource access and distribution/entry point discovery
275    'require', 'run_script', 'get_provider', 'get_distribution',
276    'load_entry_point', 'get_entry_map', 'get_entry_info',
277    'iter_entry_points',
278    'resource_string', 'resource_stream', 'resource_filename',
279    'resource_listdir', 'resource_exists', 'resource_isdir',
280
281    # Environmental control
282    'declare_namespace', 'working_set', 'add_activation_listener',
283    'find_distributions', 'set_extraction_path', 'cleanup_resources',
284    'get_default_cache',
285
286    # Primary implementation classes
287    'Environment', 'WorkingSet', 'ResourceManager',
288    'Distribution', 'Requirement', 'EntryPoint',
289
290    # Exceptions
291    'ResolutionError', 'VersionConflict', 'DistributionNotFound',
292    'UnknownExtra', 'ExtractionError',
293
294    # Warnings
295    'PEP440Warning',
296
297    # Parsing functions and string utilities
298    'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
299    'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
300    'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',
301
302    # filesystem utilities
303    'ensure_directory', 'normalize_path',
304
305    # Distribution "precedence" constants
306    'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST',
307
308    # "Provider" interfaces, implementations, and registration/lookup APIs
309    'IMetadataProvider', 'IResourceProvider', 'FileMetadata',
310    'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider',
311    'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider',
312    'register_finder', 'register_namespace_handler', 'register_loader_type',
313    'fixup_namespace_packages', 'get_importer',
314
315    # Deprecated/backward compatibility only
316    'run_main', 'AvailableDistributions',
317]
318
319
320class ResolutionError(Exception):
321    """Abstract base for dependency resolution errors"""
322
323    def __repr__(self):
324        return self.__class__.__name__ + repr(self.args)
325
326
327class VersionConflict(ResolutionError):
328    """
329    An already-installed version conflicts with the requested version.
330
331    Should be initialized with the installed Distribution and the requested
332    Requirement.
333    """
334
335    _template = "{self.dist} is installed but {self.req} is required"
336
337    @property
338    def dist(self):
339        return self.args[0]
340
341    @property
342    def req(self):
343        return self.args[1]
344
345    def report(self):
346        return self._template.format(**locals())
347
348    def with_context(self, required_by):
349        """
350        If required_by is non-empty, return a version of self that is a
351        ContextualVersionConflict.
352        """
353        if not required_by:
354            return self
355        args = self.args + (required_by,)
356        return ContextualVersionConflict(*args)
357
358
359class ContextualVersionConflict(VersionConflict):
360    """
361    A VersionConflict that accepts a third parameter, the set of the
362    requirements that required the installed Distribution.
363    """
364
365    _template = VersionConflict._template + ' by {self.required_by}'
366
367    @property
368    def required_by(self):
369        return self.args[2]
370
371
372class DistributionNotFound(ResolutionError):
373    """A requested distribution was not found"""
374
375    _template = ("The '{self.req}' distribution was not found "
376                 "and is required by {self.requirers_str}")
377
378    @property
379    def req(self):
380        return self.args[0]
381
382    @property
383    def requirers(self):
384        return self.args[1]
385
386    @property
387    def requirers_str(self):
388        if not self.requirers:
389            return 'the application'
390        return ', '.join(self.requirers)
391
392    def report(self):
393        return self._template.format(**locals())
394
395    def __str__(self):
396        return self.report()
397
398
399class UnknownExtra(ResolutionError):
400    """Distribution doesn't have an "extra feature" of the given name"""
401
402
403_provider_factories = {}
404
405PY_MAJOR = sys.version[:3]
406EGG_DIST = 3
407BINARY_DIST = 2
408SOURCE_DIST = 1
409CHECKOUT_DIST = 0
410DEVELOP_DIST = -1
411
412
413def register_loader_type(loader_type, provider_factory):
414    """Register `provider_factory` to make providers for `loader_type`
415
416    `loader_type` is the type or class of a PEP 302 ``module.__loader__``,
417    and `provider_factory` is a function that, passed a *module* object,
418    returns an ``IResourceProvider`` for that module.
419    """
420    _provider_factories[loader_type] = provider_factory
421
422
423def get_provider(moduleOrReq):
424    """Return an IResourceProvider for the named module or requirement"""
425    if isinstance(moduleOrReq, Requirement):
426        return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
427    try:
428        module = sys.modules[moduleOrReq]
429    except KeyError:
430        __import__(moduleOrReq)
431        module = sys.modules[moduleOrReq]
432    loader = getattr(module, '__loader__', None)
433    return _find_adapter(_provider_factories, loader)(module)
434
435
436def _macosx_vers(_cache=[]):
437    if not _cache:
438        version = platform.mac_ver()[0]
439        # fallback for MacPorts
440        if version == '':
441            plist = '/System/Library/CoreServices/SystemVersion.plist'
442            if os.path.exists(plist):
443                if hasattr(plistlib, 'readPlist'):
444                    plist_content = plistlib.readPlist(plist)
445                    if 'ProductVersion' in plist_content:
446                        version = plist_content['ProductVersion']
447
448        _cache.append(version.split('.'))
449    return _cache[0]
450
451
452def _macosx_arch(machine):
453    return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine)
454
455
456def get_build_platform():
457    """Return this platform's string for platform-specific distributions
458
459    XXX Currently this is the same as ``distutils.util.get_platform()``, but it
460    needs some hacks for Linux and Mac OS X.
461    """
462    try:
463        # Python 2.7 or >=3.2
464        from sysconfig import get_platform
465    except ImportError:
466        from distutils.util import get_platform
467
468    plat = get_platform()
469    if sys.platform == "darwin" and not plat.startswith('macosx-'):
470        try:
471            version = _macosx_vers()
472            machine = os.uname()[4].replace(" ", "_")
473            return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]),
474                _macosx_arch(machine))
475        except ValueError:
476            # if someone is running a non-Mac darwin system, this will fall
477            # through to the default implementation
478            pass
479    return plat
480
481
482macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)")
483darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)")
484# XXX backward compat
485get_platform = get_build_platform
486
487
488def compatible_platforms(provided, required):
489    """Can code for the `provided` platform run on the `required` platform?
490
491    Returns true if either platform is ``None``, or the platforms are equal.
492
493    XXX Needs compatibility checks for Linux and other unixy OSes.
494    """
495    if provided is None or required is None or provided == required:
496        # easy case
497        return True
498
499    # Mac OS X special cases
500    reqMac = macosVersionString.match(required)
501    if reqMac:
502        provMac = macosVersionString.match(provided)
503
504        # is this a Mac package?
505        if not provMac:
506            # this is backwards compatibility for packages built before
507            # setuptools 0.6. All packages built after this point will
508            # use the new macosx designation.
509            provDarwin = darwinVersionString.match(provided)
510            if provDarwin:
511                dversion = int(provDarwin.group(1))
512                macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))
513                if dversion == 7 and macosversion >= "10.3" or \
514                        dversion == 8 and macosversion >= "10.4":
515                    return True
516            # egg isn't macosx or legacy darwin
517            return False
518
519        # are they the same major version and machine type?
520        if provMac.group(1) != reqMac.group(1) or \
521                provMac.group(3) != reqMac.group(3):
522            return False
523
524        # is the required OS major update >= the provided one?
525        if int(provMac.group(2)) > int(reqMac.group(2)):
526            return False
527
528        return True
529
530    # XXX Linux and other platforms' special cases should go here
531    return False
532
533
534def run_script(dist_spec, script_name):
535    """Locate distribution `dist_spec` and run its `script_name` script"""
536    ns = sys._getframe(1).f_globals
537    name = ns['__name__']
538    ns.clear()
539    ns['__name__'] = name
540    require(dist_spec)[0].run_script(script_name, ns)
541
542
543# backward compatibility
544run_main = run_script
545
546
547def get_distribution(dist):
548    """Return a current distribution object for a Requirement or string"""
549    if isinstance(dist, six.string_types):
550        dist = Requirement.parse(dist)
551    if isinstance(dist, Requirement):
552        dist = get_provider(dist)
553    if not isinstance(dist, Distribution):
554        raise TypeError("Expected string, Requirement, or Distribution", dist)
555    return dist
556
557
558def load_entry_point(dist, group, name):
559    """Return `name` entry point of `group` for `dist` or raise ImportError"""
560    return get_distribution(dist).load_entry_point(group, name)
561
562
563def get_entry_map(dist, group=None):
564    """Return the entry point map for `group`, or the full entry map"""
565    return get_distribution(dist).get_entry_map(group)
566
567
568def get_entry_info(dist, group, name):
569    """Return the EntryPoint object for `group`+`name`, or ``None``"""
570    return get_distribution(dist).get_entry_info(group, name)
571
572
573class IMetadataProvider:
574    def has_metadata(name):
575        """Does the package's distribution contain the named metadata?"""
576
577    def get_metadata(name):
578        """The named metadata resource as a string"""
579
580    def get_metadata_lines(name):
581        """Yield named metadata resource as list of non-blank non-comment lines
582
583       Leading and trailing whitespace is stripped from each line, and lines
584       with ``#`` as the first non-blank character are omitted."""
585
586    def metadata_isdir(name):
587        """Is the named metadata a directory?  (like ``os.path.isdir()``)"""
588
589    def metadata_listdir(name):
590        """List of metadata names in the directory (like ``os.listdir()``)"""
591
592    def run_script(script_name, namespace):
593        """Execute the named script in the supplied namespace dictionary"""
594
595
596class IResourceProvider(IMetadataProvider):
597    """An object that provides access to package resources"""
598
599    def get_resource_filename(manager, resource_name):
600        """Return a true filesystem path for `resource_name`
601
602        `manager` must be an ``IResourceManager``"""
603
604    def get_resource_stream(manager, resource_name):
605        """Return a readable file-like object for `resource_name`
606
607        `manager` must be an ``IResourceManager``"""
608
609    def get_resource_string(manager, resource_name):
610        """Return a string containing the contents of `resource_name`
611
612        `manager` must be an ``IResourceManager``"""
613
614    def has_resource(resource_name):
615        """Does the package contain the named resource?"""
616
617    def resource_isdir(resource_name):
618        """Is the named resource a directory?  (like ``os.path.isdir()``)"""
619
620    def resource_listdir(resource_name):
621        """List of resource names in the directory (like ``os.listdir()``)"""
622
623
624class WorkingSet(object):
625    """A collection of active distributions on sys.path (or a similar list)"""
626
627    def __init__(self, entries=None):
628        """Create working set from list of path entries (default=sys.path)"""
629        self.entries = []
630        self.entry_keys = {}
631        self.by_key = {}
632        self.callbacks = []
633
634        if entries is None:
635            entries = sys.path
636
637        for entry in entries:
638            self.add_entry(entry)
639
640    @classmethod
641    def _build_master(cls):
642        """
643        Prepare the master working set.
644        """
645        ws = cls()
646        try:
647            from __main__ import __requires__
648        except ImportError:
649            # The main program does not list any requirements
650            return ws
651
652        # ensure the requirements are met
653        try:
654            ws.require(__requires__)
655        except VersionConflict:
656            return cls._build_from_requirements(__requires__)
657
658        return ws
659
660    @classmethod
661    def _build_from_requirements(cls, req_spec):
662        """
663        Build a working set from a requirement spec. Rewrites sys.path.
664        """
665        # try it without defaults already on sys.path
666        # by starting with an empty path
667        ws = cls([])
668        reqs = parse_requirements(req_spec)
669        dists = ws.resolve(reqs, Environment())
670        for dist in dists:
671            ws.add(dist)
672
673        # add any missing entries from sys.path
674        for entry in sys.path:
675            if entry not in ws.entries:
676                ws.add_entry(entry)
677
678        # then copy back to sys.path
679        sys.path[:] = ws.entries
680        return ws
681
682    def add_entry(self, entry):
683        """Add a path item to ``.entries``, finding any distributions on it
684
685        ``find_distributions(entry, True)`` is used to find distributions
686        corresponding to the path entry, and they are added.  `entry` is
687        always appended to ``.entries``, even if it is already present.
688        (This is because ``sys.path`` can contain the same value more than
689        once, and the ``.entries`` of the ``sys.path`` WorkingSet should always
690        equal ``sys.path``.)
691        """
692        self.entry_keys.setdefault(entry, [])
693        self.entries.append(entry)
694        for dist in find_distributions(entry, True):
695            self.add(dist, entry, False)
696
697    def __contains__(self, dist):
698        """True if `dist` is the active distribution for its project"""
699        return self.by_key.get(dist.key) == dist
700
701    def find(self, req):
702        """Find a distribution matching requirement `req`
703
704        If there is an active distribution for the requested project, this
705        returns it as long as it meets the version requirement specified by
706        `req`.  But, if there is an active distribution for the project and it
707        does *not* meet the `req` requirement, ``VersionConflict`` is raised.
708        If there is no active distribution for the requested project, ``None``
709        is returned.
710        """
711        dist = self.by_key.get(req.key)
712        if dist is not None and dist not in req:
713            # XXX add more info
714            raise VersionConflict(dist, req)
715        return dist
716
717    def iter_entry_points(self, group, name=None):
718        """Yield entry point objects from `group` matching `name`
719
720        If `name` is None, yields all entry points in `group` from all
721        distributions in the working set, otherwise only ones matching
722        both `group` and `name` are yielded (in distribution order).
723        """
724        for dist in self:
725            entries = dist.get_entry_map(group)
726            if name is None:
727                for ep in entries.values():
728                    yield ep
729            elif name in entries:
730                yield entries[name]
731
732    def run_script(self, requires, script_name):
733        """Locate distribution for `requires` and run `script_name` script"""
734        ns = sys._getframe(1).f_globals
735        name = ns['__name__']
736        ns.clear()
737        ns['__name__'] = name
738        self.require(requires)[0].run_script(script_name, ns)
739
740    def __iter__(self):
741        """Yield distributions for non-duplicate projects in the working set
742
743        The yield order is the order in which the items' path entries were
744        added to the working set.
745        """
746        seen = {}
747        for item in self.entries:
748            if item not in self.entry_keys:
749                # workaround a cache issue
750                continue
751
752            for key in self.entry_keys[item]:
753                if key not in seen:
754                    seen[key] = 1
755                    yield self.by_key[key]
756
757    def add(self, dist, entry=None, insert=True, replace=False):
758        """Add `dist` to working set, associated with `entry`
759
760        If `entry` is unspecified, it defaults to the ``.location`` of `dist`.
761        On exit from this routine, `entry` is added to the end of the working
762        set's ``.entries`` (if it wasn't already present).
763
764        `dist` is only added to the working set if it's for a project that
765        doesn't already have a distribution in the set, unless `replace=True`.
766        If it's added, any callbacks registered with the ``subscribe()`` method
767        will be called.
768        """
769        if insert:
770            dist.insert_on(self.entries, entry, replace=replace)
771
772        if entry is None:
773            entry = dist.location
774        keys = self.entry_keys.setdefault(entry, [])
775        keys2 = self.entry_keys.setdefault(dist.location, [])
776        if not replace and dist.key in self.by_key:
777            # ignore hidden distros
778            return
779
780        self.by_key[dist.key] = dist
781        if dist.key not in keys:
782            keys.append(dist.key)
783        if dist.key not in keys2:
784            keys2.append(dist.key)
785        self._added_new(dist)
786
787    def resolve(self, requirements, env=None, installer=None,
788                replace_conflicting=False, extras=None):
789        """List all distributions needed to (recursively) meet `requirements`
790
791        `requirements` must be a sequence of ``Requirement`` objects.  `env`,
792        if supplied, should be an ``Environment`` instance.  If
793        not supplied, it defaults to all distributions available within any
794        entry or distribution in the working set.  `installer`, if supplied,
795        will be invoked with each requirement that cannot be met by an
796        already-installed distribution; it should return a ``Distribution`` or
797        ``None``.
798
799        Unless `replace_conflicting=True`, raises a VersionConflict exception if
800        any requirements are found on the path that have the correct name but
801        the wrong version.  Otherwise, if an `installer` is supplied it will be
802        invoked to obtain the correct version of the requirement and activate
803        it.
804
805        `extras` is a list of the extras to be used with these requirements.
806        This is important because extra requirements may look like `my_req;
807        extra = "my_extra"`, which would otherwise be interpreted as a purely
808        optional requirement.  Instead, we want to be able to assert that these
809        requirements are truly required.
810        """
811
812        # set up the stack
813        requirements = list(requirements)[::-1]
814        # set of processed requirements
815        processed = {}
816        # key -> dist
817        best = {}
818        to_activate = []
819
820        req_extras = _ReqExtras()
821
822        # Mapping of requirement to set of distributions that required it;
823        # useful for reporting info about conflicts.
824        required_by = collections.defaultdict(set)
825
826        while requirements:
827            # process dependencies breadth-first
828            req = requirements.pop(0)
829            if req in processed:
830                # Ignore cyclic or redundant dependencies
831                continue
832
833            if not req_extras.markers_pass(req, extras):
834                continue
835
836            dist = best.get(req.key)
837            if dist is None:
838                # Find the best distribution and add it to the map
839                dist = self.by_key.get(req.key)
840                if dist is None or (dist not in req and replace_conflicting):
841                    ws = self
842                    if env is None:
843                        if dist is None:
844                            env = Environment(self.entries)
845                        else:
846                            # Use an empty environment and workingset to avoid
847                            # any further conflicts with the conflicting
848                            # distribution
849                            env = Environment([])
850                            ws = WorkingSet([])
851                    dist = best[req.key] = env.best_match(req, ws, installer)
852                    if dist is None:
853                        requirers = required_by.get(req, None)
854                        raise DistributionNotFound(req, requirers)
855                to_activate.append(dist)
856            if dist not in req:
857                # Oops, the "best" so far conflicts with a dependency
858                dependent_req = required_by[req]
859                raise VersionConflict(dist, req).with_context(dependent_req)
860
861            # push the new requirements onto the stack
862            new_requirements = dist.requires(req.extras)[::-1]
863            requirements.extend(new_requirements)
864
865            # Register the new requirements needed by req
866            for new_requirement in new_requirements:
867                required_by[new_requirement].add(req.project_name)
868                req_extras[new_requirement] = req.extras
869
870            processed[req] = True
871
872        # return list of distros to activate
873        return to_activate
874
875    def find_plugins(self, plugin_env, full_env=None, installer=None,
876            fallback=True):
877        """Find all activatable distributions in `plugin_env`
878
879        Example usage::
880
881            distributions, errors = working_set.find_plugins(
882                Environment(plugin_dirlist)
883            )
884            # add plugins+libs to sys.path
885            map(working_set.add, distributions)
886            # display errors
887            print('Could not load', errors)
888
889        The `plugin_env` should be an ``Environment`` instance that contains
890        only distributions that are in the project's "plugin directory" or
891        directories. The `full_env`, if supplied, should be an ``Environment``
892        contains all currently-available distributions.  If `full_env` is not
893        supplied, one is created automatically from the ``WorkingSet`` this
894        method is called on, which will typically mean that every directory on
895        ``sys.path`` will be scanned for distributions.
896
897        `installer` is a standard installer callback as used by the
898        ``resolve()`` method. The `fallback` flag indicates whether we should
899        attempt to resolve older versions of a plugin if the newest version
900        cannot be resolved.
901
902        This method returns a 2-tuple: (`distributions`, `error_info`), where
903        `distributions` is a list of the distributions found in `plugin_env`
904        that were loadable, along with any other distributions that are needed
905        to resolve their dependencies.  `error_info` is a dictionary mapping
906        unloadable plugin distributions to an exception instance describing the
907        error that occurred. Usually this will be a ``DistributionNotFound`` or
908        ``VersionConflict`` instance.
909        """
910
911        plugin_projects = list(plugin_env)
912        # scan project names in alphabetic order
913        plugin_projects.sort()
914
915        error_info = {}
916        distributions = {}
917
918        if full_env is None:
919            env = Environment(self.entries)
920            env += plugin_env
921        else:
922            env = full_env + plugin_env
923
924        shadow_set = self.__class__([])
925        # put all our entries in shadow_set
926        list(map(shadow_set.add, self))
927
928        for project_name in plugin_projects:
929
930            for dist in plugin_env[project_name]:
931
932                req = [dist.as_requirement()]
933
934                try:
935                    resolvees = shadow_set.resolve(req, env, installer)
936
937                except ResolutionError as v:
938                    # save error info
939                    error_info[dist] = v
940                    if fallback:
941                        # try the next older version of project
942                        continue
943                    else:
944                        # give up on this project, keep going
945                        break
946
947                else:
948                    list(map(shadow_set.add, resolvees))
949                    distributions.update(dict.fromkeys(resolvees))
950
951                    # success, no need to try any more versions of this project
952                    break
953
954        distributions = list(distributions)
955        distributions.sort()
956
957        return distributions, error_info
958
959    def require(self, *requirements):
960        """Ensure that distributions matching `requirements` are activated
961
962        `requirements` must be a string or a (possibly-nested) sequence
963        thereof, specifying the distributions and versions required.  The
964        return value is a sequence of the distributions that needed to be
965        activated to fulfill the requirements; all relevant distributions are
966        included, even if they were already activated in this working set.
967        """
968        needed = self.resolve(parse_requirements(requirements))
969
970        for dist in needed:
971            self.add(dist)
972
973        return needed
974
975    def subscribe(self, callback, existing=True):
976        """Invoke `callback` for all distributions
977
978        If `existing=True` (default),
979        call on all existing ones, as well.
980        """
981        if callback in self.callbacks:
982            return
983        self.callbacks.append(callback)
984        if not existing:
985            return
986        for dist in self:
987            callback(dist)
988
989    def _added_new(self, dist):
990        for callback in self.callbacks:
991            callback(dist)
992
993    def __getstate__(self):
994        return (
995            self.entries[:], self.entry_keys.copy(), self.by_key.copy(),
996            self.callbacks[:]
997        )
998
999    def __setstate__(self, e_k_b_c):
1000        entries, keys, by_key, callbacks = e_k_b_c
1001        self.entries = entries[:]
1002        self.entry_keys = keys.copy()
1003        self.by_key = by_key.copy()
1004        self.callbacks = callbacks[:]
1005
1006
1007class _ReqExtras(dict):
1008    """
1009    Map each requirement to the extras that demanded it.
1010    """
1011
1012    def markers_pass(self, req, extras=None):
1013        """
1014        Evaluate markers for req against each extra that
1015        demanded it.
1016
1017        Return False if the req has a marker and fails
1018        evaluation. Otherwise, return True.
1019        """
1020        extra_evals = (
1021            req.marker.evaluate({'extra': extra})
1022            for extra in self.get(req, ()) + (extras or (None,))
1023        )
1024        return not req.marker or any(extra_evals)
1025
1026
1027class Environment(object):
1028    """Searchable snapshot of distributions on a search path"""
1029
1030    def __init__(self, search_path=None, platform=get_supported_platform(),
1031            python=PY_MAJOR):
1032        """Snapshot distributions available on a search path
1033
1034        Any distributions found on `search_path` are added to the environment.
1035        `search_path` should be a sequence of ``sys.path`` items.  If not
1036        supplied, ``sys.path`` is used.
1037
1038        `platform` is an optional string specifying the name of the platform
1039        that platform-specific distributions must be compatible with.  If
1040        unspecified, it defaults to the current platform.  `python` is an
1041        optional string naming the desired version of Python (e.g. ``'3.3'``);
1042        it defaults to the current version.
1043
1044        You may explicitly set `platform` (and/or `python`) to ``None`` if you
1045        wish to map *all* distributions, not just those compatible with the
1046        running platform or Python version.
1047        """
1048        self._distmap = {}
1049        self.platform = platform
1050        self.python = python
1051        self.scan(search_path)
1052
1053    def can_add(self, dist):
1054        """Is distribution `dist` acceptable for this environment?
1055
1056        The distribution must match the platform and python version
1057        requirements specified when this environment was created, or False
1058        is returned.
1059        """
1060        return (self.python is None or dist.py_version is None
1061            or dist.py_version == self.python) \
1062            and compatible_platforms(dist.platform, self.platform)
1063
1064    def remove(self, dist):
1065        """Remove `dist` from the environment"""
1066        self._distmap[dist.key].remove(dist)
1067
1068    def scan(self, search_path=None):
1069        """Scan `search_path` for distributions usable in this environment
1070
1071        Any distributions found are added to the environment.
1072        `search_path` should be a sequence of ``sys.path`` items.  If not
1073        supplied, ``sys.path`` is used.  Only distributions conforming to
1074        the platform/python version defined at initialization are added.
1075        """
1076        if search_path is None:
1077            search_path = sys.path
1078
1079        for item in search_path:
1080            for dist in find_distributions(item):
1081                self.add(dist)
1082
1083    def __getitem__(self, project_name):
1084        """Return a newest-to-oldest list of distributions for `project_name`
1085
1086        Uses case-insensitive `project_name` comparison, assuming all the
1087        project's distributions use their project's name converted to all
1088        lowercase as their key.
1089
1090        """
1091        distribution_key = project_name.lower()
1092        return self._distmap.get(distribution_key, [])
1093
1094    def add(self, dist):
1095        """Add `dist` if we ``can_add()`` it and it has not already been added
1096        """
1097        if self.can_add(dist) and dist.has_version():
1098            dists = self._distmap.setdefault(dist.key, [])
1099            if dist not in dists:
1100                dists.append(dist)
1101                dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)
1102
1103    def best_match(self, req, working_set, installer=None):
1104        """Find distribution best matching `req` and usable on `working_set`
1105
1106        This calls the ``find(req)`` method of the `working_set` to see if a
1107        suitable distribution is already active.  (This may raise
1108        ``VersionConflict`` if an unsuitable version of the project is already
1109        active in the specified `working_set`.)  If a suitable distribution
1110        isn't active, this method returns the newest distribution in the
1111        environment that meets the ``Requirement`` in `req`.  If no suitable
1112        distribution is found, and `installer` is supplied, then the result of
1113        calling the environment's ``obtain(req, installer)`` method will be
1114        returned.
1115        """
1116        dist = working_set.find(req)
1117        if dist is not None:
1118            return dist
1119        for dist in self[req.key]:
1120            if dist in req:
1121                return dist
1122        # try to download/install
1123        return self.obtain(req, installer)
1124
1125    def obtain(self, requirement, installer=None):
1126        """Obtain a distribution matching `requirement` (e.g. via download)
1127
1128        Obtain a distro that matches requirement (e.g. via download).  In the
1129        base ``Environment`` class, this routine just returns
1130        ``installer(requirement)``, unless `installer` is None, in which case
1131        None is returned instead.  This method is a hook that allows subclasses
1132        to attempt other ways of obtaining a distribution before falling back
1133        to the `installer` argument."""
1134        if installer is not None:
1135            return installer(requirement)
1136
1137    def __iter__(self):
1138        """Yield the unique project names of the available distributions"""
1139        for key in self._distmap.keys():
1140            if self[key]:
1141                yield key
1142
1143    def __iadd__(self, other):
1144        """In-place addition of a distribution or environment"""
1145        if isinstance(other, Distribution):
1146            self.add(other)
1147        elif isinstance(other, Environment):
1148            for project in other:
1149                for dist in other[project]:
1150                    self.add(dist)
1151        else:
1152            raise TypeError("Can't add %r to environment" % (other,))
1153        return self
1154
1155    def __add__(self, other):
1156        """Add an environment or distribution to an environment"""
1157        new = self.__class__([], platform=None, python=None)
1158        for env in self, other:
1159            new += env
1160        return new
1161
1162
1163# XXX backward compatibility
1164AvailableDistributions = Environment
1165
1166
1167class ExtractionError(RuntimeError):
1168    """An error occurred extracting a resource
1169
1170    The following attributes are available from instances of this exception:
1171
1172    manager
1173        The resource manager that raised this exception
1174
1175    cache_path
1176        The base directory for resource extraction
1177
1178    original_error
1179        The exception instance that caused extraction to fail
1180    """
1181
1182
1183class ResourceManager:
1184    """Manage resource extraction and packages"""
1185    extraction_path = None
1186
1187    def __init__(self):
1188        self.cached_files = {}
1189
1190    def resource_exists(self, package_or_requirement, resource_name):
1191        """Does the named resource exist?"""
1192        return get_provider(package_or_requirement).has_resource(resource_name)
1193
1194    def resource_isdir(self, package_or_requirement, resource_name):
1195        """Is the named resource an existing directory?"""
1196        return get_provider(package_or_requirement).resource_isdir(
1197            resource_name
1198        )
1199
1200    def resource_filename(self, package_or_requirement, resource_name):
1201        """Return a true filesystem path for specified resource"""
1202        return get_provider(package_or_requirement).get_resource_filename(
1203            self, resource_name
1204        )
1205
1206    def resource_stream(self, package_or_requirement, resource_name):
1207        """Return a readable file-like object for specified resource"""
1208        return get_provider(package_or_requirement).get_resource_stream(
1209            self, resource_name
1210        )
1211
1212    def resource_string(self, package_or_requirement, resource_name):
1213        """Return specified resource as a string"""
1214        return get_provider(package_or_requirement).get_resource_string(
1215            self, resource_name
1216        )
1217
1218    def resource_listdir(self, package_or_requirement, resource_name):
1219        """List the contents of the named resource directory"""
1220        return get_provider(package_or_requirement).resource_listdir(
1221            resource_name
1222        )
1223
1224    def extraction_error(self):
1225        """Give an error message for problems extracting file(s)"""
1226
1227        old_exc = sys.exc_info()[1]
1228        cache_path = self.extraction_path or get_default_cache()
1229
1230        tmpl = textwrap.dedent("""
1231            Can't extract file(s) to egg cache
1232
1233            The following error occurred while trying to extract file(s) to the Python egg
1234            cache:
1235
1236              {old_exc}
1237
1238            The Python egg cache directory is currently set to:
1239
1240              {cache_path}
1241
1242            Perhaps your account does not have write access to this directory?  You can
1243            change the cache directory by setting the PYTHON_EGG_CACHE environment
1244            variable to point to an accessible directory.
1245            """).lstrip()
1246        err = ExtractionError(tmpl.format(**locals()))
1247        err.manager = self
1248        err.cache_path = cache_path
1249        err.original_error = old_exc
1250        raise err
1251
1252    def get_cache_path(self, archive_name, names=()):
1253        """Return absolute location in cache for `archive_name` and `names`
1254
1255        The parent directory of the resulting path will be created if it does
1256        not already exist.  `archive_name` should be the base filename of the
1257        enclosing egg (which may not be the name of the enclosing zipfile!),
1258        including its ".egg" extension.  `names`, if provided, should be a
1259        sequence of path name parts "under" the egg's extraction location.
1260
1261        This method should only be called by resource providers that need to
1262        obtain an extraction location, and only for names they intend to
1263        extract, as it tracks the generated names for possible cleanup later.
1264        """
1265        extract_path = self.extraction_path or get_default_cache()
1266        target_path = os.path.join(extract_path, archive_name + '-tmp', *names)
1267        try:
1268            _bypass_ensure_directory(target_path)
1269        except:
1270            self.extraction_error()
1271
1272        self._warn_unsafe_extraction_path(extract_path)
1273
1274        self.cached_files[target_path] = 1
1275        return target_path
1276
1277    @staticmethod
1278    def _warn_unsafe_extraction_path(path):
1279        """
1280        If the default extraction path is overridden and set to an insecure
1281        location, such as /tmp, it opens up an opportunity for an attacker to
1282        replace an extracted file with an unauthorized payload. Warn the user
1283        if a known insecure location is used.
1284
1285        See Distribute #375 for more details.
1286        """
1287        if os.name == 'nt' and not path.startswith(os.environ['windir']):
1288            # On Windows, permissions are generally restrictive by default
1289            #  and temp directories are not writable by other users, so
1290            #  bypass the warning.
1291            return
1292        mode = os.stat(path).st_mode
1293        if mode & stat.S_IWOTH or mode & stat.S_IWGRP:
1294            msg = ("%s is writable by group/others and vulnerable to attack "
1295                "when "
1296                "used with get_resource_filename. Consider a more secure "
1297                "location (set with .set_extraction_path or the "
1298                "PYTHON_EGG_CACHE environment variable)." % path)
1299            warnings.warn(msg, UserWarning)
1300
1301    def postprocess(self, tempname, filename):
1302        """Perform any platform-specific postprocessing of `tempname`
1303
1304        This is where Mac header rewrites should be done; other platforms don't
1305        have anything special they should do.
1306
1307        Resource providers should call this method ONLY after successfully
1308        extracting a compressed resource.  They must NOT call it on resources
1309        that are already in the filesystem.
1310
1311        `tempname` is the current (temporary) name of the file, and `filename`
1312        is the name it will be renamed to by the caller after this routine
1313        returns.
1314        """
1315
1316        if os.name == 'posix':
1317            # Make the resource executable
1318            mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777
1319            os.chmod(tempname, mode)
1320
1321    def set_extraction_path(self, path):
1322        """Set the base path where resources will be extracted to, if needed.
1323
1324        If you do not call this routine before any extractions take place, the
1325        path defaults to the return value of ``get_default_cache()``.  (Which
1326        is based on the ``PYTHON_EGG_CACHE`` environment variable, with various
1327        platform-specific fallbacks.  See that routine's documentation for more
1328        details.)
1329
1330        Resources are extracted to subdirectories of this path based upon
1331        information given by the ``IResourceProvider``.  You may set this to a
1332        temporary directory, but then you must call ``cleanup_resources()`` to
1333        delete the extracted files when done.  There is no guarantee that
1334        ``cleanup_resources()`` will be able to remove all extracted files.
1335
1336        (Note: you may not change the extraction path for a given resource
1337        manager once resources have been extracted, unless you first call
1338        ``cleanup_resources()``.)
1339        """
1340        if self.cached_files:
1341            raise ValueError(
1342                "Can't change extraction path, files already extracted"
1343            )
1344
1345        self.extraction_path = path
1346
1347    def cleanup_resources(self, force=False):
1348        """
1349        Delete all extracted resource files and directories, returning a list
1350        of the file and directory names that could not be successfully removed.
1351        This function does not have any concurrency protection, so it should
1352        generally only be called when the extraction path is a temporary
1353        directory exclusive to a single process.  This method is not
1354        automatically called; you must call it explicitly or register it as an
1355        ``atexit`` function if you wish to ensure cleanup of a temporary
1356        directory used for extractions.
1357        """
1358        # XXX
1359
1360
1361def get_default_cache():
1362    """
1363    Return the ``PYTHON_EGG_CACHE`` environment variable
1364    or a platform-relevant user cache dir for an app
1365    named "Python-Eggs".
1366    """
1367    return (
1368        os.environ.get('PYTHON_EGG_CACHE')
1369        or appdirs.user_cache_dir(appname='Python-Eggs')
1370    )
1371
1372
1373def safe_name(name):
1374    """Convert an arbitrary string to a standard distribution name
1375
1376    Any runs of non-alphanumeric/. characters are replaced with a single '-'.
1377    """
1378    return re.sub('[^A-Za-z0-9.]+', '-', name)
1379
1380
1381def safe_version(version):
1382    """
1383    Convert an arbitrary string to a standard version string
1384    """
1385    try:
1386        # normalize the version
1387        return str(packaging.version.Version(version))
1388    except packaging.version.InvalidVersion:
1389        version = version.replace(' ', '.')
1390        return re.sub('[^A-Za-z0-9.]+', '-', version)
1391
1392
1393def safe_extra(extra):
1394    """Convert an arbitrary string to a standard 'extra' name
1395
1396    Any runs of non-alphanumeric characters are replaced with a single '_',
1397    and the result is always lowercased.
1398    """
1399    return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower()
1400
1401
1402def to_filename(name):
1403    """Convert a project or version name to its filename-escaped form
1404
1405    Any '-' characters are currently replaced with '_'.
1406    """
1407    return name.replace('-', '_')
1408
1409
1410def invalid_marker(text):
1411    """
1412    Validate text as a PEP 508 environment marker; return an exception
1413    if invalid or False otherwise.
1414    """
1415    try:
1416        evaluate_marker(text)
1417    except SyntaxError as e:
1418        e.filename = None
1419        e.lineno = None
1420        return e
1421    return False
1422
1423
1424def evaluate_marker(text, extra=None):
1425    """
1426    Evaluate a PEP 508 environment marker.
1427    Return a boolean indicating the marker result in this environment.
1428    Raise SyntaxError if marker is invalid.
1429
1430    This implementation uses the 'pyparsing' module.
1431    """
1432    try:
1433        marker = packaging.markers.Marker(text)
1434        return marker.evaluate()
1435    except packaging.markers.InvalidMarker as e:
1436        raise SyntaxError(e)
1437
1438
1439class NullProvider:
1440    """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
1441
1442    egg_name = None
1443    egg_info = None
1444    loader = None
1445
1446    def __init__(self, module):
1447        self.loader = getattr(module, '__loader__', None)
1448        self.module_path = os.path.dirname(getattr(module, '__file__', ''))
1449
1450    def get_resource_filename(self, manager, resource_name):
1451        return self._fn(self.module_path, resource_name)
1452
1453    def get_resource_stream(self, manager, resource_name):
1454        return io.BytesIO(self.get_resource_string(manager, resource_name))
1455
1456    def get_resource_string(self, manager, resource_name):
1457        return self._get(self._fn(self.module_path, resource_name))
1458
1459    def has_resource(self, resource_name):
1460        return self._has(self._fn(self.module_path, resource_name))
1461
1462    def has_metadata(self, name):
1463        return self.egg_info and self._has(self._fn(self.egg_info, name))
1464
1465    def get_metadata(self, name):
1466        if not self.egg_info:
1467            return ""
1468        value = self._get(self._fn(self.egg_info, name))
1469        return value.decode('utf-8') if six.PY3 else value
1470
1471    def get_metadata_lines(self, name):
1472        return yield_lines(self.get_metadata(name))
1473
1474    def resource_isdir(self, resource_name):
1475        return self._isdir(self._fn(self.module_path, resource_name))
1476
1477    def metadata_isdir(self, name):
1478        return self.egg_info and self._isdir(self._fn(self.egg_info, name))
1479
1480    def resource_listdir(self, resource_name):
1481        return self._listdir(self._fn(self.module_path, resource_name))
1482
1483    def metadata_listdir(self, name):
1484        if self.egg_info:
1485            return self._listdir(self._fn(self.egg_info, name))
1486        return []
1487
1488    def run_script(self, script_name, namespace):
1489        script = 'scripts/' + script_name
1490        if not self.has_metadata(script):
1491            raise ResolutionError("No script named %r" % script_name)
1492        script_text = self.get_metadata(script).replace('\r\n', '\n')
1493        script_text = script_text.replace('\r', '\n')
1494        script_filename = self._fn(self.egg_info, script)
1495        namespace['__file__'] = script_filename
1496        if os.path.exists(script_filename):
1497            source = open(script_filename).read()
1498            code = compile(source, script_filename, 'exec')
1499            exec(code, namespace, namespace)
1500        else:
1501            from linecache import cache
1502            cache[script_filename] = (
1503                len(script_text), 0, script_text.split('\n'), script_filename
1504            )
1505            script_code = compile(script_text, script_filename, 'exec')
1506            exec(script_code, namespace, namespace)
1507
1508    def _has(self, path):
1509        raise NotImplementedError(
1510            "Can't perform this operation for unregistered loader type"
1511        )
1512
1513    def _isdir(self, path):
1514        raise NotImplementedError(
1515            "Can't perform this operation for unregistered loader type"
1516        )
1517
1518    def _listdir(self, path):
1519        raise NotImplementedError(
1520            "Can't perform this operation for unregistered loader type"
1521        )
1522
1523    def _fn(self, base, resource_name):
1524        if resource_name:
1525            return os.path.join(base, *resource_name.split('/'))
1526        return base
1527
1528    def _get(self, path):
1529        if hasattr(self.loader, 'get_data'):
1530            return self.loader.get_data(path)
1531        raise NotImplementedError(
1532            "Can't perform this operation for loaders without 'get_data()'"
1533        )
1534
1535
1536register_loader_type(object, NullProvider)
1537
1538
1539class EggProvider(NullProvider):
1540    """Provider based on a virtual filesystem"""
1541
1542    def __init__(self, module):
1543        NullProvider.__init__(self, module)
1544        self._setup_prefix()
1545
1546    def _setup_prefix(self):
1547        # we assume here that our metadata may be nested inside a "basket"
1548        # of multiple eggs; that's why we use module_path instead of .archive
1549        path = self.module_path
1550        old = None
1551        while path != old:
1552            if _is_unpacked_egg(path):
1553                self.egg_name = os.path.basename(path)
1554                self.egg_info = os.path.join(path, 'EGG-INFO')
1555                self.egg_root = path
1556                break
1557            old = path
1558            path, base = os.path.split(path)
1559
1560
1561class DefaultProvider(EggProvider):
1562    """Provides access to package resources in the filesystem"""
1563
1564    def _has(self, path):
1565        return os.path.exists(path)
1566
1567    def _isdir(self, path):
1568        return os.path.isdir(path)
1569
1570    def _listdir(self, path):
1571        return os.listdir(path)
1572
1573    def get_resource_stream(self, manager, resource_name):
1574        return open(self._fn(self.module_path, resource_name), 'rb')
1575
1576    def _get(self, path):
1577        with open(path, 'rb') as stream:
1578            return stream.read()
1579
1580    @classmethod
1581    def _register(cls):
1582        loader_cls = getattr(importlib_machinery, 'SourceFileLoader',
1583            type(None))
1584        register_loader_type(loader_cls, cls)
1585
1586
1587DefaultProvider._register()
1588
1589
1590class EmptyProvider(NullProvider):
1591    """Provider that returns nothing for all requests"""
1592
1593    _isdir = _has = lambda self, path: False
1594    _get = lambda self, path: ''
1595    _listdir = lambda self, path: []
1596    module_path = None
1597
1598    def __init__(self):
1599        pass
1600
1601
1602empty_provider = EmptyProvider()
1603
1604
1605class ZipManifests(dict):
1606    """
1607    zip manifest builder
1608    """
1609
1610    @classmethod
1611    def build(cls, path):
1612        """
1613        Build a dictionary similar to the zipimport directory
1614        caches, except instead of tuples, store ZipInfo objects.
1615
1616        Use a platform-specific path separator (os.sep) for the path keys
1617        for compatibility with pypy on Windows.
1618        """
1619        with ContextualZipFile(path) as zfile:
1620            items = (
1621                (
1622                    name.replace('/', os.sep),
1623                    zfile.getinfo(name),
1624                )
1625                for name in zfile.namelist()
1626            )
1627            return dict(items)
1628
1629    load = build
1630
1631
1632class MemoizedZipManifests(ZipManifests):
1633    """
1634    Memoized zipfile manifests.
1635    """
1636    manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime')
1637
1638    def load(self, path):
1639        """
1640        Load a manifest at path or return a suitable manifest already loaded.
1641        """
1642        path = os.path.normpath(path)
1643        mtime = os.stat(path).st_mtime
1644
1645        if path not in self or self[path].mtime != mtime:
1646            manifest = self.build(path)
1647            self[path] = self.manifest_mod(manifest, mtime)
1648
1649        return self[path].manifest
1650
1651
1652class ContextualZipFile(zipfile.ZipFile):
1653    """
1654    Supplement ZipFile class to support context manager for Python 2.6
1655    """
1656
1657    def __enter__(self):
1658        return self
1659
1660    def __exit__(self, type, value, traceback):
1661        self.close()
1662
1663    def __new__(cls, *args, **kwargs):
1664        """
1665        Construct a ZipFile or ContextualZipFile as appropriate
1666        """
1667        if hasattr(zipfile.ZipFile, '__exit__'):
1668            return zipfile.ZipFile(*args, **kwargs)
1669        return super(ContextualZipFile, cls).__new__(cls)
1670
1671
1672class ZipProvider(EggProvider):
1673    """Resource support for zips and eggs"""
1674
1675    eagers = None
1676    _zip_manifests = MemoizedZipManifests()
1677
1678    def __init__(self, module):
1679        EggProvider.__init__(self, module)
1680        self.zip_pre = self.loader.archive + os.sep
1681
1682    def _zipinfo_name(self, fspath):
1683        # Convert a virtual filename (full path to file) into a zipfile subpath
1684        # usable with the zipimport directory cache for our target archive
1685        if fspath.startswith(self.zip_pre):
1686            return fspath[len(self.zip_pre):]
1687        raise AssertionError(
1688            "%s is not a subpath of %s" % (fspath, self.zip_pre)
1689        )
1690
1691    def _parts(self, zip_path):
1692        # Convert a zipfile subpath into an egg-relative path part list.
1693        # pseudo-fs path
1694        fspath = self.zip_pre + zip_path
1695        if fspath.startswith(self.egg_root + os.sep):
1696            return fspath[len(self.egg_root) + 1:].split(os.sep)
1697        raise AssertionError(
1698            "%s is not a subpath of %s" % (fspath, self.egg_root)
1699        )
1700
1701    @property
1702    def zipinfo(self):
1703        return self._zip_manifests.load(self.loader.archive)
1704
1705    def get_resource_filename(self, manager, resource_name):
1706        if not self.egg_name:
1707            raise NotImplementedError(
1708                "resource_filename() only supported for .egg, not .zip"
1709            )
1710        # no need to lock for extraction, since we use temp names
1711        zip_path = self._resource_to_zip(resource_name)
1712        eagers = self._get_eager_resources()
1713        if '/'.join(self._parts(zip_path)) in eagers:
1714            for name in eagers:
1715                self._extract_resource(manager, self._eager_to_zip(name))
1716        return self._extract_resource(manager, zip_path)
1717
1718    @staticmethod
1719    def _get_date_and_size(zip_stat):
1720        size = zip_stat.file_size
1721        # ymdhms+wday, yday, dst
1722        date_time = zip_stat.date_time + (0, 0, -1)
1723        # 1980 offset already done
1724        timestamp = time.mktime(date_time)
1725        return timestamp, size
1726
1727    def _extract_resource(self, manager, zip_path):
1728
1729        if zip_path in self._index():
1730            for name in self._index()[zip_path]:
1731                last = self._extract_resource(
1732                    manager, os.path.join(zip_path, name)
1733                )
1734            # return the extracted directory name
1735            return os.path.dirname(last)
1736
1737        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
1738
1739        if not WRITE_SUPPORT:
1740            raise IOError('"os.rename" and "os.unlink" are not supported '
1741                          'on this platform')
1742        try:
1743
1744            real_path = manager.get_cache_path(
1745                self.egg_name, self._parts(zip_path)
1746            )
1747
1748            if self._is_current(real_path, zip_path):
1749                return real_path
1750
1751            outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
1752            os.write(outf, self.loader.get_data(zip_path))
1753            os.close(outf)
1754            utime(tmpnam, (timestamp, timestamp))
1755            manager.postprocess(tmpnam, real_path)
1756
1757            try:
1758                rename(tmpnam, real_path)
1759
1760            except os.error:
1761                if os.path.isfile(real_path):
1762                    if self._is_current(real_path, zip_path):
1763                        # the file became current since it was checked above,
1764                        #  so proceed.
1765                        return real_path
1766                    # Windows, del old file and retry
1767                    elif os.name == 'nt':
1768                        unlink(real_path)
1769                        rename(tmpnam, real_path)
1770                        return real_path
1771                raise
1772
1773        except os.error:
1774            # report a user-friendly error
1775            manager.extraction_error()
1776
1777        return real_path
1778
1779    def _is_current(self, file_path, zip_path):
1780        """
1781        Return True if the file_path is current for this zip_path
1782        """
1783        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
1784        if not os.path.isfile(file_path):
1785            return False
1786        stat = os.stat(file_path)
1787        if stat.st_size != size or stat.st_mtime != timestamp:
1788            return False
1789        # check that the contents match
1790        zip_contents = self.loader.get_data(zip_path)
1791        with open(file_path, 'rb') as f:
1792            file_contents = f.read()
1793        return zip_contents == file_contents
1794
1795    def _get_eager_resources(self):
1796        if self.eagers is None:
1797            eagers = []
1798            for name in ('native_libs.txt', 'eager_resources.txt'):
1799                if self.has_metadata(name):
1800                    eagers.extend(self.get_metadata_lines(name))
1801            self.eagers = eagers
1802        return self.eagers
1803
1804    def _index(self):
1805        try:
1806            return self._dirindex
1807        except AttributeError:
1808            ind = {}
1809            for path in self.zipinfo:
1810                parts = path.split(os.sep)
1811                while parts:
1812                    parent = os.sep.join(parts[:-1])
1813                    if parent in ind:
1814                        ind[parent].append(parts[-1])
1815                        break
1816                    else:
1817                        ind[parent] = [parts.pop()]
1818            self._dirindex = ind
1819            return ind
1820
1821    def _has(self, fspath):
1822        zip_path = self._zipinfo_name(fspath)
1823        return zip_path in self.zipinfo or zip_path in self._index()
1824
1825    def _isdir(self, fspath):
1826        return self._zipinfo_name(fspath) in self._index()
1827
1828    def _listdir(self, fspath):
1829        return list(self._index().get(self._zipinfo_name(fspath), ()))
1830
1831    def _eager_to_zip(self, resource_name):
1832        return self._zipinfo_name(self._fn(self.egg_root, resource_name))
1833
1834    def _resource_to_zip(self, resource_name):
1835        return self._zipinfo_name(self._fn(self.module_path, resource_name))
1836
1837
1838register_loader_type(zipimport.zipimporter, ZipProvider)
1839
1840
1841class FileMetadata(EmptyProvider):
1842    """Metadata handler for standalone PKG-INFO files
1843
1844    Usage::
1845
1846        metadata = FileMetadata("/path/to/PKG-INFO")
1847
1848    This provider rejects all data and metadata requests except for PKG-INFO,
1849    which is treated as existing, and will be the contents of the file at
1850    the provided location.
1851    """
1852
1853    def __init__(self, path):
1854        self.path = path
1855
1856    def has_metadata(self, name):
1857        return name == 'PKG-INFO' and os.path.isfile(self.path)
1858
1859    def get_metadata(self, name):
1860        if name != 'PKG-INFO':
1861            raise KeyError("No metadata except PKG-INFO is available")
1862
1863        with io.open(self.path, encoding='utf-8', errors="replace") as f:
1864            metadata = f.read()
1865        self._warn_on_replacement(metadata)
1866        return metadata
1867
1868    def _warn_on_replacement(self, metadata):
1869        # Python 2.6 and 3.2 compat for: replacement_char = '�'
1870        replacement_char = b'\xef\xbf\xbd'.decode('utf-8')
1871        if replacement_char in metadata:
1872            tmpl = "{self.path} could not be properly decoded in UTF-8"
1873            msg = tmpl.format(**locals())
1874            warnings.warn(msg)
1875
1876    def get_metadata_lines(self, name):
1877        return yield_lines(self.get_metadata(name))
1878
1879
1880class PathMetadata(DefaultProvider):
1881    """Metadata provider for egg directories
1882
1883    Usage::
1884
1885        # Development eggs:
1886
1887        egg_info = "/path/to/PackageName.egg-info"
1888        base_dir = os.path.dirname(egg_info)
1889        metadata = PathMetadata(base_dir, egg_info)
1890        dist_name = os.path.splitext(os.path.basename(egg_info))[0]
1891        dist = Distribution(basedir, project_name=dist_name, metadata=metadata)
1892
1893        # Unpacked egg directories:
1894
1895        egg_path = "/path/to/PackageName-ver-pyver-etc.egg"
1896        metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO'))
1897        dist = Distribution.from_filename(egg_path, metadata=metadata)
1898    """
1899
1900    def __init__(self, path, egg_info):
1901        self.module_path = path
1902        self.egg_info = egg_info
1903
1904
1905class EggMetadata(ZipProvider):
1906    """Metadata provider for .egg files"""
1907
1908    def __init__(self, importer):
1909        """Create a metadata provider from a zipimporter"""
1910
1911        self.zip_pre = importer.archive + os.sep
1912        self.loader = importer
1913        if importer.prefix:
1914            self.module_path = os.path.join(importer.archive, importer.prefix)
1915        else:
1916            self.module_path = importer.archive
1917        self._setup_prefix()
1918
1919
1920_declare_state('dict', _distribution_finders={})
1921
1922
1923def register_finder(importer_type, distribution_finder):
1924    """Register `distribution_finder` to find distributions in sys.path items
1925
1926    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
1927    handler), and `distribution_finder` is a callable that, passed a path
1928    item and the importer instance, yields ``Distribution`` instances found on
1929    that path item.  See ``pkg_resources.find_on_path`` for an example."""
1930    _distribution_finders[importer_type] = distribution_finder
1931
1932
1933def find_distributions(path_item, only=False):
1934    """Yield distributions accessible via `path_item`"""
1935    importer = get_importer(path_item)
1936    finder = _find_adapter(_distribution_finders, importer)
1937    return finder(importer, path_item, only)
1938
1939
1940def find_eggs_in_zip(importer, path_item, only=False):
1941    """
1942    Find eggs in zip files; possibly multiple nested eggs.
1943    """
1944    if importer.archive.endswith('.whl'):
1945        # wheels are not supported with this finder
1946        # they don't have PKG-INFO metadata, and won't ever contain eggs
1947        return
1948    metadata = EggMetadata(importer)
1949    if metadata.has_metadata('PKG-INFO'):
1950        yield Distribution.from_filename(path_item, metadata=metadata)
1951    if only:
1952        # don't yield nested distros
1953        return
1954    for subitem in metadata.resource_listdir('/'):
1955        if _is_unpacked_egg(subitem):
1956            subpath = os.path.join(path_item, subitem)
1957            for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath):
1958                yield dist
1959
1960
1961register_finder(zipimport.zipimporter, find_eggs_in_zip)
1962
1963
1964def find_nothing(importer, path_item, only=False):
1965    return ()
1966
1967
1968register_finder(object, find_nothing)
1969
1970
1971def _by_version_descending(names):
1972    """
1973    Given a list of filenames, return them in descending order
1974    by version number.
1975
1976    >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg'
1977    >>> _by_version_descending(names)
1978    ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar']
1979    >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg'
1980    >>> _by_version_descending(names)
1981    ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg']
1982    >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg'
1983    >>> _by_version_descending(names)
1984    ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg']
1985    """
1986    def _by_version(name):
1987        """
1988        Parse each component of the filename
1989        """
1990        name, ext = os.path.splitext(name)
1991        parts = itertools.chain(name.split('-'), [ext])
1992        return [packaging.version.parse(part) for part in parts]
1993
1994    return sorted(names, key=_by_version, reverse=True)
1995
1996
1997def find_on_path(importer, path_item, only=False):
1998    """Yield distributions accessible on a sys.path directory"""
1999    path_item = _normalize_cached(path_item)
2000
2001    if os.path.isdir(path_item) and os.access(path_item, os.R_OK):
2002        if _is_unpacked_egg(path_item):
2003            yield Distribution.from_filename(
2004                path_item, metadata=PathMetadata(
2005                    path_item, os.path.join(path_item, 'EGG-INFO')
2006                )
2007            )
2008        else:
2009            # scan for .egg and .egg-info in directory
2010            path_item_entries = _by_version_descending(os.listdir(path_item))
2011            for entry in path_item_entries:
2012                lower = entry.lower()
2013                if lower.endswith('.egg-info') or lower.endswith('.dist-info'):
2014                    fullpath = os.path.join(path_item, entry)
2015                    if os.path.isdir(fullpath):
2016                        # egg-info directory, allow getting metadata
2017                        if len(os.listdir(fullpath)) == 0:
2018                            # Empty egg directory, skip.
2019                            continue
2020                        metadata = PathMetadata(path_item, fullpath)
2021                    else:
2022                        metadata = FileMetadata(fullpath)
2023                    yield Distribution.from_location(
2024                        path_item, entry, metadata, precedence=DEVELOP_DIST
2025                    )
2026                elif not only and _is_unpacked_egg(entry):
2027                    dists = find_distributions(os.path.join(path_item, entry))
2028                    for dist in dists:
2029                        yield dist
2030                elif not only and lower.endswith('.egg-link'):
2031                    with open(os.path.join(path_item, entry)) as entry_file:
2032                        entry_lines = entry_file.readlines()
2033                    for line in entry_lines:
2034                        if not line.strip():
2035                            continue
2036                        path = os.path.join(path_item, line.rstrip())
2037                        dists = find_distributions(path)
2038                        for item in dists:
2039                            yield item
2040                        break
2041
2042
2043register_finder(pkgutil.ImpImporter, find_on_path)
2044
2045if hasattr(importlib_machinery, 'FileFinder'):
2046    register_finder(importlib_machinery.FileFinder, find_on_path)
2047
2048_declare_state('dict', _namespace_handlers={})
2049_declare_state('dict', _namespace_packages={})
2050
2051
2052def register_namespace_handler(importer_type, namespace_handler):
2053    """Register `namespace_handler` to declare namespace packages
2054
2055    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
2056    handler), and `namespace_handler` is a callable like this::
2057
2058        def namespace_handler(importer, path_entry, moduleName, module):
2059            # return a path_entry to use for child packages
2060
2061    Namespace handlers are only called if the importer object has already
2062    agreed that it can handle the relevant path item, and they should only
2063    return a subpath if the module __path__ does not already contain an
2064    equivalent subpath.  For an example namespace handler, see
2065    ``pkg_resources.file_ns_handler``.
2066    """
2067    _namespace_handlers[importer_type] = namespace_handler
2068
2069
2070def _handle_ns(packageName, path_item):
2071    """Ensure that named package includes a subpath of path_item (if needed)"""
2072
2073    importer = get_importer(path_item)
2074    if importer is None:
2075        return None
2076    loader = importer.find_module(packageName)
2077    if loader is None:
2078        return None
2079    module = sys.modules.get(packageName)
2080    if module is None:
2081        module = sys.modules[packageName] = types.ModuleType(packageName)
2082        module.__path__ = []
2083        _set_parent_ns(packageName)
2084    elif not hasattr(module, '__path__'):
2085        raise TypeError("Not a package:", packageName)
2086    handler = _find_adapter(_namespace_handlers, importer)
2087    subpath = handler(importer, path_item, packageName, module)
2088    if subpath is not None:
2089        path = module.__path__
2090        path.append(subpath)
2091        loader.load_module(packageName)
2092        _rebuild_mod_path(path, packageName, module)
2093    return subpath
2094
2095
2096def _rebuild_mod_path(orig_path, package_name, module):
2097    """
2098    Rebuild module.__path__ ensuring that all entries are ordered
2099    corresponding to their sys.path order
2100    """
2101    sys_path = [_normalize_cached(p) for p in sys.path]
2102
2103    def safe_sys_path_index(entry):
2104        """
2105        Workaround for #520 and #513.
2106        """
2107        try:
2108            return sys_path.index(entry)
2109        except ValueError:
2110            return float('inf')
2111
2112    def position_in_sys_path(path):
2113        """
2114        Return the ordinal of the path based on its position in sys.path
2115        """
2116        path_parts = path.split(os.sep)
2117        module_parts = package_name.count('.') + 1
2118        parts = path_parts[:-module_parts]
2119        return safe_sys_path_index(_normalize_cached(os.sep.join(parts)))
2120
2121    if not isinstance(orig_path, list):
2122        # Is this behavior useful when module.__path__ is not a list?
2123        return
2124
2125    orig_path.sort(key=position_in_sys_path)
2126    module.__path__[:] = [_normalize_cached(p) for p in orig_path]
2127
2128
2129def declare_namespace(packageName):
2130    """Declare that package 'packageName' is a namespace package"""
2131
2132    _imp.acquire_lock()
2133    try:
2134        if packageName in _namespace_packages:
2135            return
2136
2137        path, parent = sys.path, None
2138        if '.' in packageName:
2139            parent = '.'.join(packageName.split('.')[:-1])
2140            declare_namespace(parent)
2141            if parent not in _namespace_packages:
2142                __import__(parent)
2143            try:
2144                path = sys.modules[parent].__path__
2145            except AttributeError:
2146                raise TypeError("Not a package:", parent)
2147
2148        # Track what packages are namespaces, so when new path items are added,
2149        # they can be updated
2150        _namespace_packages.setdefault(parent, []).append(packageName)
2151        _namespace_packages.setdefault(packageName, [])
2152
2153        for path_item in path:
2154            # Ensure all the parent's path items are reflected in the child,
2155            # if they apply
2156            _handle_ns(packageName, path_item)
2157
2158    finally:
2159        _imp.release_lock()
2160
2161
2162def fixup_namespace_packages(path_item, parent=None):
2163    """Ensure that previously-declared namespace packages include path_item"""
2164    _imp.acquire_lock()
2165    try:
2166        for package in _namespace_packages.get(parent, ()):
2167            subpath = _handle_ns(package, path_item)
2168            if subpath:
2169                fixup_namespace_packages(subpath, package)
2170    finally:
2171        _imp.release_lock()
2172
2173
2174def file_ns_handler(importer, path_item, packageName, module):
2175    """Compute an ns-package subpath for a filesystem or zipfile importer"""
2176
2177    subpath = os.path.join(path_item, packageName.split('.')[-1])
2178    normalized = _normalize_cached(subpath)
2179    for item in module.__path__:
2180        if _normalize_cached(item) == normalized:
2181            break
2182    else:
2183        # Only return the path if it's not already there
2184        return subpath
2185
2186
2187register_namespace_handler(pkgutil.ImpImporter, file_ns_handler)
2188register_namespace_handler(zipimport.zipimporter, file_ns_handler)
2189
2190if hasattr(importlib_machinery, 'FileFinder'):
2191    register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler)
2192
2193
2194def null_ns_handler(importer, path_item, packageName, module):
2195    return None
2196
2197
2198register_namespace_handler(object, null_ns_handler)
2199
2200
2201def normalize_path(filename):
2202    """Normalize a file/dir name for comparison purposes"""
2203    return os.path.normcase(os.path.realpath(filename))
2204
2205
2206def _normalize_cached(filename, _cache={}):
2207    try:
2208        return _cache[filename]
2209    except KeyError:
2210        _cache[filename] = result = normalize_path(filename)
2211        return result
2212
2213
2214def _is_unpacked_egg(path):
2215    """
2216    Determine if given path appears to be an unpacked egg.
2217    """
2218    return (
2219        path.lower().endswith('.egg')
2220    )
2221
2222
2223def _set_parent_ns(packageName):
2224    parts = packageName.split('.')
2225    name = parts.pop()
2226    if parts:
2227        parent = '.'.join(parts)
2228        setattr(sys.modules[parent], name, sys.modules[packageName])
2229
2230
2231def yield_lines(strs):
2232    """Yield non-empty/non-comment lines of a string or sequence"""
2233    if isinstance(strs, six.string_types):
2234        for s in strs.splitlines():
2235            s = s.strip()
2236            # skip blank lines/comments
2237            if s and not s.startswith('#'):
2238                yield s
2239    else:
2240        for ss in strs:
2241            for s in yield_lines(ss):
2242                yield s
2243
2244
2245MODULE = re.compile(r"\w+(\.\w+)*$").match
2246EGG_NAME = re.compile(
2247    r"""
2248    (?P<name>[^-]+) (
2249        -(?P<ver>[^-]+) (
2250            -py(?P<pyver>[^-]+) (
2251                -(?P<plat>.+)
2252            )?
2253        )?
2254    )?
2255    """,
2256    re.VERBOSE | re.IGNORECASE,
2257).match
2258
2259
2260class EntryPoint(object):
2261    """Object representing an advertised importable object"""
2262
2263    def __init__(self, name, module_name, attrs=(), extras=(), dist=None):
2264        if not MODULE(module_name):
2265            raise ValueError("Invalid module name", module_name)
2266        self.name = name
2267        self.module_name = module_name
2268        self.attrs = tuple(attrs)
2269        self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras
2270        self.dist = dist
2271
2272    def __str__(self):
2273        s = "%s = %s" % (self.name, self.module_name)
2274        if self.attrs:
2275            s += ':' + '.'.join(self.attrs)
2276        if self.extras:
2277            s += ' [%s]' % ','.join(self.extras)
2278        return s
2279
2280    def __repr__(self):
2281        return "EntryPoint.parse(%r)" % str(self)
2282
2283    def load(self, require=True, *args, **kwargs):
2284        """
2285        Require packages for this EntryPoint, then resolve it.
2286        """
2287        if not require or args or kwargs:
2288            warnings.warn(
2289                "Parameters to load are deprecated.  Call .resolve and "
2290                ".require separately.",
2291                DeprecationWarning,
2292                stacklevel=2,
2293            )
2294        if require:
2295            self.require(*args, **kwargs)
2296        return self.resolve()
2297
2298    def resolve(self):
2299        """
2300        Resolve the entry point from its module and attrs.
2301        """
2302        module = __import__(self.module_name, fromlist=['__name__'], level=0)
2303        try:
2304            return functools.reduce(getattr, self.attrs, module)
2305        except AttributeError as exc:
2306            raise ImportError(str(exc))
2307
2308    def require(self, env=None, installer=None):
2309        if self.extras and not self.dist:
2310            raise UnknownExtra("Can't require() without a distribution", self)
2311
2312        # Get the requirements for this entry point with all its extras and
2313        # then resolve them. We have to pass `extras` along when resolving so
2314        # that the working set knows what extras we want. Otherwise, for
2315        # dist-info distributions, the working set will assume that the
2316        # requirements for that extra are purely optional and skip over them.
2317        reqs = self.dist.requires(self.extras)
2318        items = working_set.resolve(reqs, env, installer, extras=self.extras)
2319        list(map(working_set.add, items))
2320
2321    pattern = re.compile(
2322        r'\s*'
2323        r'(?P<name>.+?)\s*'
2324        r'=\s*'
2325        r'(?P<module>[\w.]+)\s*'
2326        r'(:\s*(?P<attr>[\w.]+))?\s*'
2327        r'(?P<extras>\[.*\])?\s*$'
2328    )
2329
2330    @classmethod
2331    def parse(cls, src, dist=None):
2332        """Parse a single entry point from string `src`
2333
2334        Entry point syntax follows the form::
2335
2336            name = some.module:some.attr [extra1, extra2]
2337
2338        The entry name and module name are required, but the ``:attrs`` and
2339        ``[extras]`` parts are optional
2340        """
2341        m = cls.pattern.match(src)
2342        if not m:
2343            msg = "EntryPoint must be in 'name=module:attrs [extras]' format"
2344            raise ValueError(msg, src)
2345        res = m.groupdict()
2346        extras = cls._parse_extras(res['extras'])
2347        attrs = res['attr'].split('.') if res['attr'] else ()
2348        return cls(res['name'], res['module'], attrs, extras, dist)
2349
2350    @classmethod
2351    def _parse_extras(cls, extras_spec):
2352        if not extras_spec:
2353            return ()
2354        req = Requirement.parse('x' + extras_spec)
2355        if req.specs:
2356            raise ValueError()
2357        return req.extras
2358
2359    @classmethod
2360    def parse_group(cls, group, lines, dist=None):
2361        """Parse an entry point group"""
2362        if not MODULE(group):
2363            raise ValueError("Invalid group name", group)
2364        this = {}
2365        for line in yield_lines(lines):
2366            ep = cls.parse(line, dist)
2367            if ep.name in this:
2368                raise ValueError("Duplicate entry point", group, ep.name)
2369            this[ep.name] = ep
2370        return this
2371
2372    @classmethod
2373    def parse_map(cls, data, dist=None):
2374        """Parse a map of entry point groups"""
2375        if isinstance(data, dict):
2376            data = data.items()
2377        else:
2378            data = split_sections(data)
2379        maps = {}
2380        for group, lines in data:
2381            if group is None:
2382                if not lines:
2383                    continue
2384                raise ValueError("Entry points must be listed in groups")
2385            group = group.strip()
2386            if group in maps:
2387                raise ValueError("Duplicate group name", group)
2388            maps[group] = cls.parse_group(group, lines, dist)
2389        return maps
2390
2391
2392def _remove_md5_fragment(location):
2393    if not location:
2394        return ''
2395    parsed = urllib.parse.urlparse(location)
2396    if parsed[-1].startswith('md5='):
2397        return urllib.parse.urlunparse(parsed[:-1] + ('',))
2398    return location
2399
2400
2401def _version_from_file(lines):
2402    """
2403    Given an iterable of lines from a Metadata file, return
2404    the value of the Version field, if present, or None otherwise.
2405    """
2406    is_version_line = lambda line: line.lower().startswith('version:')
2407    version_lines = filter(is_version_line, lines)
2408    line = next(iter(version_lines), '')
2409    _, _, value = line.partition(':')
2410    return safe_version(value.strip()) or None
2411
2412
2413class Distribution(object):
2414    """Wrap an actual or potential sys.path entry w/metadata"""
2415    PKG_INFO = 'PKG-INFO'
2416
2417    def __init__(self, location=None, metadata=None, project_name=None,
2418            version=None, py_version=PY_MAJOR, platform=None,
2419            precedence=EGG_DIST):
2420        self.project_name = safe_name(project_name or 'Unknown')
2421        if version is not None:
2422            self._version = safe_version(version)
2423        self.py_version = py_version
2424        self.platform = platform
2425        self.location = location
2426        self.precedence = precedence
2427        self._provider = metadata or empty_provider
2428
2429    @classmethod
2430    def from_location(cls, location, basename, metadata=None, **kw):
2431        project_name, version, py_version, platform = [None] * 4
2432        basename, ext = os.path.splitext(basename)
2433        if ext.lower() in _distributionImpl:
2434            cls = _distributionImpl[ext.lower()]
2435
2436            match = EGG_NAME(basename)
2437            if match:
2438                project_name, version, py_version, platform = match.group(
2439                    'name', 'ver', 'pyver', 'plat'
2440                )
2441        return cls(
2442            location, metadata, project_name=project_name, version=version,
2443            py_version=py_version, platform=platform, **kw
2444        )._reload_version()
2445
2446    def _reload_version(self):
2447        return self
2448
2449    @property
2450    def hashcmp(self):
2451        return (
2452            self.parsed_version,
2453            self.precedence,
2454            self.key,
2455            _remove_md5_fragment(self.location),
2456            self.py_version or '',
2457            self.platform or '',
2458        )
2459
2460    def __hash__(self):
2461        return hash(self.hashcmp)
2462
2463    def __lt__(self, other):
2464        return self.hashcmp < other.hashcmp
2465
2466    def __le__(self, other):
2467        return self.hashcmp <= other.hashcmp
2468
2469    def __gt__(self, other):
2470        return self.hashcmp > other.hashcmp
2471
2472    def __ge__(self, other):
2473        return self.hashcmp >= other.hashcmp
2474
2475    def __eq__(self, other):
2476        if not isinstance(other, self.__class__):
2477            # It's not a Distribution, so they are not equal
2478            return False
2479        return self.hashcmp == other.hashcmp
2480
2481    def __ne__(self, other):
2482        return not self == other
2483
2484    # These properties have to be lazy so that we don't have to load any
2485    # metadata until/unless it's actually needed.  (i.e., some distributions
2486    # may not know their name or version without loading PKG-INFO)
2487
2488    @property
2489    def key(self):
2490        try:
2491            return self._key
2492        except AttributeError:
2493            self._key = key = self.project_name.lower()
2494            return key
2495
2496    @property
2497    def parsed_version(self):
2498        if not hasattr(self, "_parsed_version"):
2499            self._parsed_version = parse_version(self.version)
2500
2501        return self._parsed_version
2502
2503    def _warn_legacy_version(self):
2504        LV = packaging.version.LegacyVersion
2505        is_legacy = isinstance(self._parsed_version, LV)
2506        if not is_legacy:
2507            return
2508
2509        # While an empty version is technically a legacy version and
2510        # is not a valid PEP 440 version, it's also unlikely to
2511        # actually come from someone and instead it is more likely that
2512        # it comes from setuptools attempting to parse a filename and
2513        # including it in the list. So for that we'll gate this warning
2514        # on if the version is anything at all or not.
2515        if not self.version:
2516            return
2517
2518        tmpl = textwrap.dedent("""
2519            '{project_name} ({version})' is being parsed as a legacy,
2520            non PEP 440,
2521            version. You may find odd behavior and sort order.
2522            In particular it will be sorted as less than 0.0. It
2523            is recommended to migrate to PEP 440 compatible
2524            versions.
2525            """).strip().replace('\n', ' ')
2526
2527        warnings.warn(tmpl.format(**vars(self)), PEP440Warning)
2528
2529    @property
2530    def version(self):
2531        try:
2532            return self._version
2533        except AttributeError:
2534            version = _version_from_file(self._get_metadata(self.PKG_INFO))
2535            if version is None:
2536                tmpl = "Missing 'Version:' header and/or %s file"
2537                raise ValueError(tmpl % self.PKG_INFO, self)
2538            return version
2539
2540    @property
2541    def _dep_map(self):
2542        try:
2543            return self.__dep_map
2544        except AttributeError:
2545            dm = self.__dep_map = {None: []}
2546            for name in 'requires.txt', 'depends.txt':
2547                for extra, reqs in split_sections(self._get_metadata(name)):
2548                    if extra:
2549                        if ':' in extra:
2550                            extra, marker = extra.split(':', 1)
2551                            if invalid_marker(marker):
2552                                # XXX warn
2553                                reqs = []
2554                            elif not evaluate_marker(marker):
2555                                reqs = []
2556                        extra = safe_extra(extra) or None
2557                    dm.setdefault(extra, []).extend(parse_requirements(reqs))
2558            return dm
2559
2560    def requires(self, extras=()):
2561        """List of Requirements needed for this distro if `extras` are used"""
2562        dm = self._dep_map
2563        deps = []
2564        deps.extend(dm.get(None, ()))
2565        for ext in extras:
2566            try:
2567                deps.extend(dm[safe_extra(ext)])
2568            except KeyError:
2569                raise UnknownExtra(
2570                    "%s has no such extra feature %r" % (self, ext)
2571                )
2572        return deps
2573
2574    def _get_metadata(self, name):
2575        if self.has_metadata(name):
2576            for line in self.get_metadata_lines(name):
2577                yield line
2578
2579    def activate(self, path=None, replace=False):
2580        """Ensure distribution is importable on `path` (default=sys.path)"""
2581        if path is None:
2582            path = sys.path
2583        self.insert_on(path, replace=replace)
2584        if path is sys.path:
2585            fixup_namespace_packages(self.location)
2586            for pkg in self._get_metadata('namespace_packages.txt'):
2587                if pkg in sys.modules:
2588                    declare_namespace(pkg)
2589
2590    def egg_name(self):
2591        """Return what this distribution's standard .egg filename should be"""
2592        filename = "%s-%s-py%s" % (
2593            to_filename(self.project_name), to_filename(self.version),
2594            self.py_version or PY_MAJOR
2595        )
2596
2597        if self.platform:
2598            filename += '-' + self.platform
2599        return filename
2600
2601    def __repr__(self):
2602        if self.location:
2603            return "%s (%s)" % (self, self.location)
2604        else:
2605            return str(self)
2606
2607    def __str__(self):
2608        try:
2609            version = getattr(self, 'version', None)
2610        except ValueError:
2611            version = None
2612        version = version or "[unknown version]"
2613        return "%s %s" % (self.project_name, version)
2614
2615    def __getattr__(self, attr):
2616        """Delegate all unrecognized public attributes to .metadata provider"""
2617        if attr.startswith('_'):
2618            raise AttributeError(attr)
2619        return getattr(self._provider, attr)
2620
2621    @classmethod
2622    def from_filename(cls, filename, metadata=None, **kw):
2623        return cls.from_location(
2624            _normalize_cached(filename), os.path.basename(filename), metadata,
2625            **kw
2626        )
2627
2628    def as_requirement(self):
2629        """Return a ``Requirement`` that matches this distribution exactly"""
2630        if isinstance(self.parsed_version, packaging.version.Version):
2631            spec = "%s==%s" % (self.project_name, self.parsed_version)
2632        else:
2633            spec = "%s===%s" % (self.project_name, self.parsed_version)
2634
2635        return Requirement.parse(spec)
2636
2637    def load_entry_point(self, group, name):
2638        """Return the `name` entry point of `group` or raise ImportError"""
2639        ep = self.get_entry_info(group, name)
2640        if ep is None:
2641            raise ImportError("Entry point %r not found" % ((group, name),))
2642        return ep.load()
2643
2644    def get_entry_map(self, group=None):
2645        """Return the entry point map for `group`, or the full entry map"""
2646        try:
2647            ep_map = self._ep_map
2648        except AttributeError:
2649            ep_map = self._ep_map = EntryPoint.parse_map(
2650                self._get_metadata('entry_points.txt'), self
2651            )
2652        if group is not None:
2653            return ep_map.get(group, {})
2654        return ep_map
2655
2656    def get_entry_info(self, group, name):
2657        """Return the EntryPoint object for `group`+`name`, or ``None``"""
2658        return self.get_entry_map(group).get(name)
2659
2660    def insert_on(self, path, loc=None, replace=False):
2661        """Ensure self.location is on path
2662
2663        If replace=False (default):
2664            - If location is already in path anywhere, do nothing.
2665            - Else:
2666              - If it's an egg and its parent directory is on path,
2667                insert just ahead of the parent.
2668              - Else: add to the end of path.
2669        If replace=True:
2670            - If location is already on path anywhere (not eggs)
2671              or higher priority than its parent (eggs)
2672              do nothing.
2673            - Else:
2674              - If it's an egg and its parent directory is on path,
2675                insert just ahead of the parent,
2676                removing any lower-priority entries.
2677              - Else: add it to the front of path.
2678        """
2679
2680        loc = loc or self.location
2681        if not loc:
2682            return
2683
2684        nloc = _normalize_cached(loc)
2685        bdir = os.path.dirname(nloc)
2686        npath = [(p and _normalize_cached(p) or p) for p in path]
2687
2688        for p, item in enumerate(npath):
2689            if item == nloc:
2690                if replace:
2691                    break
2692                else:
2693                    # don't modify path (even removing duplicates) if found and not replace
2694                    return
2695            elif item == bdir and self.precedence == EGG_DIST:
2696                # if it's an .egg, give it precedence over its directory
2697                # UNLESS it's already been added to sys.path and replace=False
2698                if (not replace) and nloc in npath[p:]:
2699                    return
2700                if path is sys.path:
2701                    self.check_version_conflict()
2702                path.insert(p, loc)
2703                npath.insert(p, nloc)
2704                break
2705        else:
2706            if path is sys.path:
2707                self.check_version_conflict()
2708            if replace:
2709                path.insert(0, loc)
2710            else:
2711                path.append(loc)
2712            return
2713
2714        # p is the spot where we found or inserted loc; now remove duplicates
2715        while True:
2716            try:
2717                np = npath.index(nloc, p + 1)
2718            except ValueError:
2719                break
2720            else:
2721                del npath[np], path[np]
2722                # ha!
2723                p = np
2724
2725        return
2726
2727    def check_version_conflict(self):
2728        if self.key == 'setuptools':
2729            # ignore the inevitable setuptools self-conflicts  :(
2730            return
2731
2732        nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
2733        loc = normalize_path(self.location)
2734        for modname in self._get_metadata('top_level.txt'):
2735            if (modname not in sys.modules or modname in nsp
2736                    or modname in _namespace_packages):
2737                continue
2738            if modname in ('pkg_resources', 'setuptools', 'site'):
2739                continue
2740            fn = getattr(sys.modules[modname], '__file__', None)
2741            if fn and (normalize_path(fn).startswith(loc) or
2742                       fn.startswith(self.location)):
2743                continue
2744            issue_warning(
2745                "Module %s was already imported from %s, but %s is being added"
2746                " to sys.path" % (modname, fn, self.location),
2747            )
2748
2749    def has_version(self):
2750        try:
2751            self.version
2752        except ValueError:
2753            issue_warning("Unbuilt egg for " + repr(self))
2754            return False
2755        return True
2756
2757    def clone(self, **kw):
2758        """Copy this distribution, substituting in any changed keyword args"""
2759        names = 'project_name version py_version platform location precedence'
2760        for attr in names.split():
2761            kw.setdefault(attr, getattr(self, attr, None))
2762        kw.setdefault('metadata', self._provider)
2763        return self.__class__(**kw)
2764
2765    @property
2766    def extras(self):
2767        return [dep for dep in self._dep_map if dep]
2768
2769
2770class EggInfoDistribution(Distribution):
2771    def _reload_version(self):
2772        """
2773        Packages installed by distutils (e.g. numpy or scipy),
2774        which uses an old safe_version, and so
2775        their version numbers can get mangled when
2776        converted to filenames (e.g., 1.11.0.dev0+2329eae to
2777        1.11.0.dev0_2329eae). These distributions will not be
2778        parsed properly
2779        downstream by Distribution and safe_version, so
2780        take an extra step and try to get the version number from
2781        the metadata file itself instead of the filename.
2782        """
2783        md_version = _version_from_file(self._get_metadata(self.PKG_INFO))
2784        if md_version:
2785            self._version = md_version
2786        return self
2787
2788
2789class DistInfoDistribution(Distribution):
2790    """Wrap an actual or potential sys.path entry w/metadata, .dist-info style"""
2791    PKG_INFO = 'METADATA'
2792    EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])")
2793
2794    @property
2795    def _parsed_pkg_info(self):
2796        """Parse and cache metadata"""
2797        try:
2798            return self._pkg_info
2799        except AttributeError:
2800            metadata = self.get_metadata(self.PKG_INFO)
2801            self._pkg_info = email.parser.Parser().parsestr(metadata)
2802            return self._pkg_info
2803
2804    @property
2805    def _dep_map(self):
2806        try:
2807            return self.__dep_map
2808        except AttributeError:
2809            self.__dep_map = self._compute_dependencies()
2810            return self.__dep_map
2811
2812    def _compute_dependencies(self):
2813        """Recompute this distribution's dependencies."""
2814        dm = self.__dep_map = {None: []}
2815
2816        reqs = []
2817        # Including any condition expressions
2818        for req in self._parsed_pkg_info.get_all('Requires-Dist') or []:
2819            reqs.extend(parse_requirements(req))
2820
2821        def reqs_for_extra(extra):
2822            for req in reqs:
2823                if not req.marker or req.marker.evaluate({'extra': extra}):
2824                    yield req
2825
2826        common = frozenset(reqs_for_extra(None))
2827        dm[None].extend(common)
2828
2829        for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []:
2830            s_extra = safe_extra(extra.strip())
2831            dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common)
2832
2833        return dm
2834
2835
2836_distributionImpl = {
2837    '.egg': Distribution,
2838    '.egg-info': EggInfoDistribution,
2839    '.dist-info': DistInfoDistribution,
2840    }
2841
2842
2843def issue_warning(*args, **kw):
2844    level = 1
2845    g = globals()
2846    try:
2847        # find the first stack frame that is *not* code in
2848        # the pkg_resources module, to use for the warning
2849        while sys._getframe(level).f_globals is g:
2850            level += 1
2851    except ValueError:
2852        pass
2853    warnings.warn(stacklevel=level + 1, *args, **kw)
2854
2855
2856class RequirementParseError(ValueError):
2857    def __str__(self):
2858        return ' '.join(self.args)
2859
2860
2861def parse_requirements(strs):
2862    """Yield ``Requirement`` objects for each specification in `strs`
2863
2864    `strs` must be a string, or a (possibly-nested) iterable thereof.
2865    """
2866    # create a steppable iterator, so we can handle \-continuations
2867    lines = iter(yield_lines(strs))
2868
2869    for line in lines:
2870        # Drop comments -- a hash without a space may be in a URL.
2871        if ' #' in line:
2872            line = line[:line.find(' #')]
2873        # If there is a line continuation, drop it, and append the next line.
2874        if line.endswith('\\'):
2875            line = line[:-2].strip()
2876            line += next(lines)
2877        yield Requirement(line)
2878
2879
2880class Requirement(packaging.requirements.Requirement):
2881    def __init__(self, requirement_string):
2882        """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
2883        try:
2884            super(Requirement, self).__init__(requirement_string)
2885        except packaging.requirements.InvalidRequirement as e:
2886            raise RequirementParseError(str(e))
2887        self.unsafe_name = self.name
2888        project_name = safe_name(self.name)
2889        self.project_name, self.key = project_name, project_name.lower()
2890        self.specs = [
2891            (spec.operator, spec.version) for spec in self.specifier]
2892        self.extras = tuple(map(safe_extra, self.extras))
2893        self.hashCmp = (
2894            self.key,
2895            self.specifier,
2896            frozenset(self.extras),
2897            str(self.marker) if self.marker else None,
2898        )
2899        self.__hash = hash(self.hashCmp)
2900
2901    def __eq__(self, other):
2902        return (
2903            isinstance(other, Requirement) and
2904            self.hashCmp == other.hashCmp
2905        )
2906
2907    def __ne__(self, other):
2908        return not self == other
2909
2910    def __contains__(self, item):
2911        if isinstance(item, Distribution):
2912            if item.key != self.key:
2913                return False
2914
2915            item = item.version
2916
2917        # Allow prereleases always in order to match the previous behavior of
2918        # this method. In the future this should be smarter and follow PEP 440
2919        # more accurately.
2920        return self.specifier.contains(item, prereleases=True)
2921
2922    def __hash__(self):
2923        return self.__hash
2924
2925    def __repr__(self): return "Requirement.parse(%r)" % str(self)
2926
2927    @staticmethod
2928    def parse(s):
2929        req, = parse_requirements(s)
2930        return req
2931
2932
2933def _get_mro(cls):
2934    """Get an mro for a type or classic class"""
2935    if not isinstance(cls, type):
2936
2937        class cls(cls, object):
2938            pass
2939
2940        return cls.__mro__[1:]
2941    return cls.__mro__
2942
2943
2944def _find_adapter(registry, ob):
2945    """Return an adapter factory for `ob` from `registry`"""
2946    for t in _get_mro(getattr(ob, '__class__', type(ob))):
2947        if t in registry:
2948            return registry[t]
2949
2950
2951def ensure_directory(path):
2952    """Ensure that the parent directory of `path` exists"""
2953    dirname = os.path.dirname(path)
2954    if not os.path.isdir(dirname):
2955        os.makedirs(dirname)
2956
2957
2958def _bypass_ensure_directory(path):
2959    """Sandbox-bypassing version of ensure_directory()"""
2960    if not WRITE_SUPPORT:
2961        raise IOError('"os.mkdir" not supported on this platform.')
2962    dirname, filename = split(path)
2963    if dirname and filename and not isdir(dirname):
2964        _bypass_ensure_directory(dirname)
2965        mkdir(dirname, 0o755)
2966
2967
2968def split_sections(s):
2969    """Split a string or iterable thereof into (section, content) pairs
2970
2971    Each ``section`` is a stripped version of the section header ("[section]")
2972    and each ``content`` is a list of stripped lines excluding blank lines and
2973    comment-only lines.  If there are any such lines before the first section
2974    header, they're returned in a first ``section`` of ``None``.
2975    """
2976    section = None
2977    content = []
2978    for line in yield_lines(s):
2979        if line.startswith("["):
2980            if line.endswith("]"):
2981                if section or content:
2982                    yield section, content
2983                section = line[1:-1].strip()
2984                content = []
2985            else:
2986                raise ValueError("Invalid section heading", line)
2987        else:
2988            content.append(line)
2989
2990    # wrap up last segment
2991    yield section, content
2992
2993
2994def _mkstemp(*args, **kw):
2995    old_open = os.open
2996    try:
2997        # temporarily bypass sandboxing
2998        os.open = os_open
2999        return tempfile.mkstemp(*args, **kw)
3000    finally:
3001        # and then put it back
3002        os.open = old_open
3003
3004
3005# Silence the PEP440Warning by default, so that end users don't get hit by it
3006# randomly just because they use pkg_resources. We want to append the rule
3007# because we want earlier uses of filterwarnings to take precedence over this
3008# one.
3009warnings.filterwarnings("ignore", category=PEP440Warning, append=True)
3010
3011
3012# from jaraco.functools 1.3
3013def _call_aside(f, *args, **kwargs):
3014    f(*args, **kwargs)
3015    return f
3016
3017
3018@_call_aside
3019def _initialize(g=globals()):
3020    "Set up global resource manager (deliberately not state-saved)"
3021    manager = ResourceManager()
3022    g['_manager'] = manager
3023    g.update(
3024        (name, getattr(manager, name))
3025        for name in dir(manager)
3026        if not name.startswith('_')
3027    )
3028
3029
3030@_call_aside
3031def _initialize_master_working_set():
3032    """
3033    Prepare the master working set and make the ``require()``
3034    API available.
3035
3036    This function has explicit effects on the global state
3037    of pkg_resources. It is intended to be invoked once at
3038    the initialization of this module.
3039
3040    Invocation by other packages is unsupported and done
3041    at their own risk.
3042    """
3043    working_set = WorkingSet._build_master()
3044    _declare_state('object', working_set=working_set)
3045
3046    require = working_set.require
3047    iter_entry_points = working_set.iter_entry_points
3048    add_activation_listener = working_set.subscribe
3049    run_script = working_set.run_script
3050    # backward compatibility
3051    run_main = run_script
3052    # Activate all distributions already on sys.path with replace=False and
3053    # ensure that all distributions added to the working set in the future
3054    # (e.g. by calling ``require()``) will get activated as well,
3055    # with higher priority (replace=True).
3056    tuple(
3057        dist.activate(replace=False)
3058        for dist in working_set
3059    )
3060    add_activation_listener(lambda dist: dist.activate(replace=True), existing=False)
3061    working_set.entries = []
3062    # match order
3063    list(map(working_set.add_entry, sys.path))
3064    globals().update(locals())
3065