OSDN Git Service

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