Path: blob/master/venv/Lib/site-packages/pkg_resources/__init__.py
811 views
# coding: utf-81"""2Package resource API3--------------------45A resource is a logical file contained within a package, or a logical6subdirectory thereof. The package resource API expects resource names7to have their path parts separated with ``/``, *not* whatever the local8path separator is. Do not use os.path operations to manipulate resource9names being passed into the API.1011The package resource API is designed to work with normal filesystem packages,12.egg files, and unpacked .egg files. It can also work in a limited way with13.zip files and with custom PEP 302 loaders that support the ``get_data()``14method.15"""1617from __future__ import absolute_import1819import sys20import os21import io22import time23import re24import types25import zipfile26import zipimport27import warnings28import stat29import functools30import pkgutil31import operator32import platform33import collections34import plistlib35import email.parser36import errno37import tempfile38import textwrap39import itertools40import inspect41import ntpath42import posixpath43from pkgutil import get_importer4445try:46import _imp47except ImportError:48# Python 3.2 compatibility49import imp as _imp5051try:52FileExistsError53except NameError:54FileExistsError = OSError5556from pkg_resources.extern import six57from pkg_resources.extern.six.moves import map, filter5859# capture these to bypass sandboxing60from os import utime61try:62from os import mkdir, rename, unlink63WRITE_SUPPORT = True64except ImportError:65# no write support, probably under GAE66WRITE_SUPPORT = False6768from os import open as os_open69from os.path import isdir, split7071try:72import importlib.machinery as importlib_machinery73# access attribute to force import under delayed import mechanisms.74importlib_machinery.__name__75except ImportError:76importlib_machinery = None7778from pkg_resources.extern import appdirs79from pkg_resources.extern import packaging80__import__('pkg_resources.extern.packaging.version')81__import__('pkg_resources.extern.packaging.specifiers')82__import__('pkg_resources.extern.packaging.requirements')83__import__('pkg_resources.extern.packaging.markers')84__import__('pkg_resources.py2_warn')858687__metaclass__ = type888990if (3, 0) < sys.version_info < (3, 5):91raise RuntimeError("Python 3.5 or later is required")9293if six.PY2:94# Those builtin exceptions are only defined in Python 395PermissionError = None96NotADirectoryError = None9798# declare some globals that will be defined later to99# satisfy the linters.100require = None101working_set = None102add_activation_listener = None103resources_stream = None104cleanup_resources = None105resource_dir = None106resource_stream = None107set_extraction_path = None108resource_isdir = None109resource_string = None110iter_entry_points = None111resource_listdir = None112resource_filename = None113resource_exists = None114_distribution_finders = None115_namespace_handlers = None116_namespace_packages = None117118119class PEP440Warning(RuntimeWarning):120"""121Used when there is an issue with a version or specifier not complying with122PEP 440.123"""124125126def parse_version(v):127try:128return packaging.version.Version(v)129except packaging.version.InvalidVersion:130return packaging.version.LegacyVersion(v)131132133_state_vars = {}134135136def _declare_state(vartype, **kw):137globals().update(kw)138_state_vars.update(dict.fromkeys(kw, vartype))139140141def __getstate__():142state = {}143g = globals()144for k, v in _state_vars.items():145state[k] = g['_sget_' + v](g[k])146return state147148149def __setstate__(state):150g = globals()151for k, v in state.items():152g['_sset_' + _state_vars[k]](k, g[k], v)153return state154155156def _sget_dict(val):157return val.copy()158159160def _sset_dict(key, ob, state):161ob.clear()162ob.update(state)163164165def _sget_object(val):166return val.__getstate__()167168169def _sset_object(key, ob, state):170ob.__setstate__(state)171172173_sget_none = _sset_none = lambda *args: None174175176def get_supported_platform():177"""Return this platform's maximum compatible version.178179distutils.util.get_platform() normally reports the minimum version180of macOS that would be required to *use* extensions produced by181distutils. But what we want when checking compatibility is to know the182version of macOS that we are *running*. To allow usage of packages that183explicitly require a newer version of macOS, we must also know the184current version of the OS.185186If this condition occurs for any other platform with a version in its187platform strings, this function should be extended accordingly.188"""189plat = get_build_platform()190m = macosVersionString.match(plat)191if m is not None and sys.platform == "darwin":192try:193plat = 'macosx-%s-%s' % ('.'.join(_macos_vers()[:2]), m.group(3))194except ValueError:195# not macOS196pass197return plat198199200__all__ = [201# Basic resource access and distribution/entry point discovery202'require', 'run_script', 'get_provider', 'get_distribution',203'load_entry_point', 'get_entry_map', 'get_entry_info',204'iter_entry_points',205'resource_string', 'resource_stream', 'resource_filename',206'resource_listdir', 'resource_exists', 'resource_isdir',207208# Environmental control209'declare_namespace', 'working_set', 'add_activation_listener',210'find_distributions', 'set_extraction_path', 'cleanup_resources',211'get_default_cache',212213# Primary implementation classes214'Environment', 'WorkingSet', 'ResourceManager',215'Distribution', 'Requirement', 'EntryPoint',216217# Exceptions218'ResolutionError', 'VersionConflict', 'DistributionNotFound',219'UnknownExtra', 'ExtractionError',220221# Warnings222'PEP440Warning',223224# Parsing functions and string utilities225'parse_requirements', 'parse_version', 'safe_name', 'safe_version',226'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',227'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',228229# filesystem utilities230'ensure_directory', 'normalize_path',231232# Distribution "precedence" constants233'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST',234235# "Provider" interfaces, implementations, and registration/lookup APIs236'IMetadataProvider', 'IResourceProvider', 'FileMetadata',237'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider',238'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider',239'register_finder', 'register_namespace_handler', 'register_loader_type',240'fixup_namespace_packages', 'get_importer',241242# Warnings243'PkgResourcesDeprecationWarning',244245# Deprecated/backward compatibility only246'run_main', 'AvailableDistributions',247]248249250class ResolutionError(Exception):251"""Abstract base for dependency resolution errors"""252253def __repr__(self):254return self.__class__.__name__ + repr(self.args)255256257class VersionConflict(ResolutionError):258"""259An already-installed version conflicts with the requested version.260261Should be initialized with the installed Distribution and the requested262Requirement.263"""264265_template = "{self.dist} is installed but {self.req} is required"266267@property268def dist(self):269return self.args[0]270271@property272def req(self):273return self.args[1]274275def report(self):276return self._template.format(**locals())277278def with_context(self, required_by):279"""280If required_by is non-empty, return a version of self that is a281ContextualVersionConflict.282"""283if not required_by:284return self285args = self.args + (required_by,)286return ContextualVersionConflict(*args)287288289class ContextualVersionConflict(VersionConflict):290"""291A VersionConflict that accepts a third parameter, the set of the292requirements that required the installed Distribution.293"""294295_template = VersionConflict._template + ' by {self.required_by}'296297@property298def required_by(self):299return self.args[2]300301302class DistributionNotFound(ResolutionError):303"""A requested distribution was not found"""304305_template = ("The '{self.req}' distribution was not found "306"and is required by {self.requirers_str}")307308@property309def req(self):310return self.args[0]311312@property313def requirers(self):314return self.args[1]315316@property317def requirers_str(self):318if not self.requirers:319return 'the application'320return ', '.join(self.requirers)321322def report(self):323return self._template.format(**locals())324325def __str__(self):326return self.report()327328329class UnknownExtra(ResolutionError):330"""Distribution doesn't have an "extra feature" of the given name"""331332333_provider_factories = {}334335PY_MAJOR = '{}.{}'.format(*sys.version_info)336EGG_DIST = 3337BINARY_DIST = 2338SOURCE_DIST = 1339CHECKOUT_DIST = 0340DEVELOP_DIST = -1341342343def register_loader_type(loader_type, provider_factory):344"""Register `provider_factory` to make providers for `loader_type`345346`loader_type` is the type or class of a PEP 302 ``module.__loader__``,347and `provider_factory` is a function that, passed a *module* object,348returns an ``IResourceProvider`` for that module.349"""350_provider_factories[loader_type] = provider_factory351352353def get_provider(moduleOrReq):354"""Return an IResourceProvider for the named module or requirement"""355if isinstance(moduleOrReq, Requirement):356return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]357try:358module = sys.modules[moduleOrReq]359except KeyError:360__import__(moduleOrReq)361module = sys.modules[moduleOrReq]362loader = getattr(module, '__loader__', None)363return _find_adapter(_provider_factories, loader)(module)364365366def _macos_vers(_cache=[]):367if not _cache:368version = platform.mac_ver()[0]369# fallback for MacPorts370if version == '':371plist = '/System/Library/CoreServices/SystemVersion.plist'372if os.path.exists(plist):373if hasattr(plistlib, 'readPlist'):374plist_content = plistlib.readPlist(plist)375if 'ProductVersion' in plist_content:376version = plist_content['ProductVersion']377378_cache.append(version.split('.'))379return _cache[0]380381382def _macos_arch(machine):383return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine)384385386def get_build_platform():387"""Return this platform's string for platform-specific distributions388389XXX Currently this is the same as ``distutils.util.get_platform()``, but it390needs some hacks for Linux and macOS.391"""392from sysconfig import get_platform393394plat = get_platform()395if sys.platform == "darwin" and not plat.startswith('macosx-'):396try:397version = _macos_vers()398machine = os.uname()[4].replace(" ", "_")399return "macosx-%d.%d-%s" % (400int(version[0]), int(version[1]),401_macos_arch(machine),402)403except ValueError:404# if someone is running a non-Mac darwin system, this will fall405# through to the default implementation406pass407return plat408409410macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)")411darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)")412# XXX backward compat413get_platform = get_build_platform414415416def compatible_platforms(provided, required):417"""Can code for the `provided` platform run on the `required` platform?418419Returns true if either platform is ``None``, or the platforms are equal.420421XXX Needs compatibility checks for Linux and other unixy OSes.422"""423if provided is None or required is None or provided == required:424# easy case425return True426427# macOS special cases428reqMac = macosVersionString.match(required)429if reqMac:430provMac = macosVersionString.match(provided)431432# is this a Mac package?433if not provMac:434# this is backwards compatibility for packages built before435# setuptools 0.6. All packages built after this point will436# use the new macOS designation.437provDarwin = darwinVersionString.match(provided)438if provDarwin:439dversion = int(provDarwin.group(1))440macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))441if dversion == 7 and macosversion >= "10.3" or \442dversion == 8 and macosversion >= "10.4":443return True444# egg isn't macOS or legacy darwin445return False446447# are they the same major version and machine type?448if provMac.group(1) != reqMac.group(1) or \449provMac.group(3) != reqMac.group(3):450return False451452# is the required OS major update >= the provided one?453if int(provMac.group(2)) > int(reqMac.group(2)):454return False455456return True457458# XXX Linux and other platforms' special cases should go here459return False460461462def run_script(dist_spec, script_name):463"""Locate distribution `dist_spec` and run its `script_name` script"""464ns = sys._getframe(1).f_globals465name = ns['__name__']466ns.clear()467ns['__name__'] = name468require(dist_spec)[0].run_script(script_name, ns)469470471# backward compatibility472run_main = run_script473474475def get_distribution(dist):476"""Return a current distribution object for a Requirement or string"""477if isinstance(dist, six.string_types):478dist = Requirement.parse(dist)479if isinstance(dist, Requirement):480dist = get_provider(dist)481if not isinstance(dist, Distribution):482raise TypeError("Expected string, Requirement, or Distribution", dist)483return dist484485486def load_entry_point(dist, group, name):487"""Return `name` entry point of `group` for `dist` or raise ImportError"""488return get_distribution(dist).load_entry_point(group, name)489490491def get_entry_map(dist, group=None):492"""Return the entry point map for `group`, or the full entry map"""493return get_distribution(dist).get_entry_map(group)494495496def get_entry_info(dist, group, name):497"""Return the EntryPoint object for `group`+`name`, or ``None``"""498return get_distribution(dist).get_entry_info(group, name)499500501class IMetadataProvider:502def has_metadata(name):503"""Does the package's distribution contain the named metadata?"""504505def get_metadata(name):506"""The named metadata resource as a string"""507508def get_metadata_lines(name):509"""Yield named metadata resource as list of non-blank non-comment lines510511Leading and trailing whitespace is stripped from each line, and lines512with ``#`` as the first non-blank character are omitted."""513514def metadata_isdir(name):515"""Is the named metadata a directory? (like ``os.path.isdir()``)"""516517def metadata_listdir(name):518"""List of metadata names in the directory (like ``os.listdir()``)"""519520def run_script(script_name, namespace):521"""Execute the named script in the supplied namespace dictionary"""522523524class IResourceProvider(IMetadataProvider):525"""An object that provides access to package resources"""526527def get_resource_filename(manager, resource_name):528"""Return a true filesystem path for `resource_name`529530`manager` must be an ``IResourceManager``"""531532def get_resource_stream(manager, resource_name):533"""Return a readable file-like object for `resource_name`534535`manager` must be an ``IResourceManager``"""536537def get_resource_string(manager, resource_name):538"""Return a string containing the contents of `resource_name`539540`manager` must be an ``IResourceManager``"""541542def has_resource(resource_name):543"""Does the package contain the named resource?"""544545def resource_isdir(resource_name):546"""Is the named resource a directory? (like ``os.path.isdir()``)"""547548def resource_listdir(resource_name):549"""List of resource names in the directory (like ``os.listdir()``)"""550551552class WorkingSet:553"""A collection of active distributions on sys.path (or a similar list)"""554555def __init__(self, entries=None):556"""Create working set from list of path entries (default=sys.path)"""557self.entries = []558self.entry_keys = {}559self.by_key = {}560self.callbacks = []561562if entries is None:563entries = sys.path564565for entry in entries:566self.add_entry(entry)567568@classmethod569def _build_master(cls):570"""571Prepare the master working set.572"""573ws = cls()574try:575from __main__ import __requires__576except ImportError:577# The main program does not list any requirements578return ws579580# ensure the requirements are met581try:582ws.require(__requires__)583except VersionConflict:584return cls._build_from_requirements(__requires__)585586return ws587588@classmethod589def _build_from_requirements(cls, req_spec):590"""591Build a working set from a requirement spec. Rewrites sys.path.592"""593# try it without defaults already on sys.path594# by starting with an empty path595ws = cls([])596reqs = parse_requirements(req_spec)597dists = ws.resolve(reqs, Environment())598for dist in dists:599ws.add(dist)600601# add any missing entries from sys.path602for entry in sys.path:603if entry not in ws.entries:604ws.add_entry(entry)605606# then copy back to sys.path607sys.path[:] = ws.entries608return ws609610def add_entry(self, entry):611"""Add a path item to ``.entries``, finding any distributions on it612613``find_distributions(entry, True)`` is used to find distributions614corresponding to the path entry, and they are added. `entry` is615always appended to ``.entries``, even if it is already present.616(This is because ``sys.path`` can contain the same value more than617once, and the ``.entries`` of the ``sys.path`` WorkingSet should always618equal ``sys.path``.)619"""620self.entry_keys.setdefault(entry, [])621self.entries.append(entry)622for dist in find_distributions(entry, True):623self.add(dist, entry, False)624625def __contains__(self, dist):626"""True if `dist` is the active distribution for its project"""627return self.by_key.get(dist.key) == dist628629def find(self, req):630"""Find a distribution matching requirement `req`631632If there is an active distribution for the requested project, this633returns it as long as it meets the version requirement specified by634`req`. But, if there is an active distribution for the project and it635does *not* meet the `req` requirement, ``VersionConflict`` is raised.636If there is no active distribution for the requested project, ``None``637is returned.638"""639dist = self.by_key.get(req.key)640if dist is not None and dist not in req:641# XXX add more info642raise VersionConflict(dist, req)643return dist644645def iter_entry_points(self, group, name=None):646"""Yield entry point objects from `group` matching `name`647648If `name` is None, yields all entry points in `group` from all649distributions in the working set, otherwise only ones matching650both `group` and `name` are yielded (in distribution order).651"""652return (653entry654for dist in self655for entry in dist.get_entry_map(group).values()656if name is None or name == entry.name657)658659def run_script(self, requires, script_name):660"""Locate distribution for `requires` and run `script_name` script"""661ns = sys._getframe(1).f_globals662name = ns['__name__']663ns.clear()664ns['__name__'] = name665self.require(requires)[0].run_script(script_name, ns)666667def __iter__(self):668"""Yield distributions for non-duplicate projects in the working set669670The yield order is the order in which the items' path entries were671added to the working set.672"""673seen = {}674for item in self.entries:675if item not in self.entry_keys:676# workaround a cache issue677continue678679for key in self.entry_keys[item]:680if key not in seen:681seen[key] = 1682yield self.by_key[key]683684def add(self, dist, entry=None, insert=True, replace=False):685"""Add `dist` to working set, associated with `entry`686687If `entry` is unspecified, it defaults to the ``.location`` of `dist`.688On exit from this routine, `entry` is added to the end of the working689set's ``.entries`` (if it wasn't already present).690691`dist` is only added to the working set if it's for a project that692doesn't already have a distribution in the set, unless `replace=True`.693If it's added, any callbacks registered with the ``subscribe()`` method694will be called.695"""696if insert:697dist.insert_on(self.entries, entry, replace=replace)698699if entry is None:700entry = dist.location701keys = self.entry_keys.setdefault(entry, [])702keys2 = self.entry_keys.setdefault(dist.location, [])703if not replace and dist.key in self.by_key:704# ignore hidden distros705return706707self.by_key[dist.key] = dist708if dist.key not in keys:709keys.append(dist.key)710if dist.key not in keys2:711keys2.append(dist.key)712self._added_new(dist)713714def resolve(self, requirements, env=None, installer=None,715replace_conflicting=False, extras=None):716"""List all distributions needed to (recursively) meet `requirements`717718`requirements` must be a sequence of ``Requirement`` objects. `env`,719if supplied, should be an ``Environment`` instance. If720not supplied, it defaults to all distributions available within any721entry or distribution in the working set. `installer`, if supplied,722will be invoked with each requirement that cannot be met by an723already-installed distribution; it should return a ``Distribution`` or724``None``.725726Unless `replace_conflicting=True`, raises a VersionConflict exception727if728any requirements are found on the path that have the correct name but729the wrong version. Otherwise, if an `installer` is supplied it will be730invoked to obtain the correct version of the requirement and activate731it.732733`extras` is a list of the extras to be used with these requirements.734This is important because extra requirements may look like `my_req;735extra = "my_extra"`, which would otherwise be interpreted as a purely736optional requirement. Instead, we want to be able to assert that these737requirements are truly required.738"""739740# set up the stack741requirements = list(requirements)[::-1]742# set of processed requirements743processed = {}744# key -> dist745best = {}746to_activate = []747748req_extras = _ReqExtras()749750# Mapping of requirement to set of distributions that required it;751# useful for reporting info about conflicts.752required_by = collections.defaultdict(set)753754while requirements:755# process dependencies breadth-first756req = requirements.pop(0)757if req in processed:758# Ignore cyclic or redundant dependencies759continue760761if not req_extras.markers_pass(req, extras):762continue763764dist = best.get(req.key)765if dist is None:766# Find the best distribution and add it to the map767dist = self.by_key.get(req.key)768if dist is None or (dist not in req and replace_conflicting):769ws = self770if env is None:771if dist is None:772env = Environment(self.entries)773else:774# Use an empty environment and workingset to avoid775# any further conflicts with the conflicting776# distribution777env = Environment([])778ws = WorkingSet([])779dist = best[req.key] = env.best_match(780req, ws, installer,781replace_conflicting=replace_conflicting782)783if dist is None:784requirers = required_by.get(req, None)785raise DistributionNotFound(req, requirers)786to_activate.append(dist)787if dist not in req:788# Oops, the "best" so far conflicts with a dependency789dependent_req = required_by[req]790raise VersionConflict(dist, req).with_context(dependent_req)791792# push the new requirements onto the stack793new_requirements = dist.requires(req.extras)[::-1]794requirements.extend(new_requirements)795796# Register the new requirements needed by req797for new_requirement in new_requirements:798required_by[new_requirement].add(req.project_name)799req_extras[new_requirement] = req.extras800801processed[req] = True802803# return list of distros to activate804return to_activate805806def find_plugins(807self, plugin_env, full_env=None, installer=None, fallback=True):808"""Find all activatable distributions in `plugin_env`809810Example usage::811812distributions, errors = working_set.find_plugins(813Environment(plugin_dirlist)814)815# add plugins+libs to sys.path816map(working_set.add, distributions)817# display errors818print('Could not load', errors)819820The `plugin_env` should be an ``Environment`` instance that contains821only distributions that are in the project's "plugin directory" or822directories. The `full_env`, if supplied, should be an ``Environment``823contains all currently-available distributions. If `full_env` is not824supplied, one is created automatically from the ``WorkingSet`` this825method is called on, which will typically mean that every directory on826``sys.path`` will be scanned for distributions.827828`installer` is a standard installer callback as used by the829``resolve()`` method. The `fallback` flag indicates whether we should830attempt to resolve older versions of a plugin if the newest version831cannot be resolved.832833This method returns a 2-tuple: (`distributions`, `error_info`), where834`distributions` is a list of the distributions found in `plugin_env`835that were loadable, along with any other distributions that are needed836to resolve their dependencies. `error_info` is a dictionary mapping837unloadable plugin distributions to an exception instance describing the838error that occurred. Usually this will be a ``DistributionNotFound`` or839``VersionConflict`` instance.840"""841842plugin_projects = list(plugin_env)843# scan project names in alphabetic order844plugin_projects.sort()845846error_info = {}847distributions = {}848849if full_env is None:850env = Environment(self.entries)851env += plugin_env852else:853env = full_env + plugin_env854855shadow_set = self.__class__([])856# put all our entries in shadow_set857list(map(shadow_set.add, self))858859for project_name in plugin_projects:860861for dist in plugin_env[project_name]:862863req = [dist.as_requirement()]864865try:866resolvees = shadow_set.resolve(req, env, installer)867868except ResolutionError as v:869# save error info870error_info[dist] = v871if fallback:872# try the next older version of project873continue874else:875# give up on this project, keep going876break877878else:879list(map(shadow_set.add, resolvees))880distributions.update(dict.fromkeys(resolvees))881882# success, no need to try any more versions of this project883break884885distributions = list(distributions)886distributions.sort()887888return distributions, error_info889890def require(self, *requirements):891"""Ensure that distributions matching `requirements` are activated892893`requirements` must be a string or a (possibly-nested) sequence894thereof, specifying the distributions and versions required. The895return value is a sequence of the distributions that needed to be896activated to fulfill the requirements; all relevant distributions are897included, even if they were already activated in this working set.898"""899needed = self.resolve(parse_requirements(requirements))900901for dist in needed:902self.add(dist)903904return needed905906def subscribe(self, callback, existing=True):907"""Invoke `callback` for all distributions908909If `existing=True` (default),910call on all existing ones, as well.911"""912if callback in self.callbacks:913return914self.callbacks.append(callback)915if not existing:916return917for dist in self:918callback(dist)919920def _added_new(self, dist):921for callback in self.callbacks:922callback(dist)923924def __getstate__(self):925return (926self.entries[:], self.entry_keys.copy(), self.by_key.copy(),927self.callbacks[:]928)929930def __setstate__(self, e_k_b_c):931entries, keys, by_key, callbacks = e_k_b_c932self.entries = entries[:]933self.entry_keys = keys.copy()934self.by_key = by_key.copy()935self.callbacks = callbacks[:]936937938class _ReqExtras(dict):939"""940Map each requirement to the extras that demanded it.941"""942943def markers_pass(self, req, extras=None):944"""945Evaluate markers for req against each extra that946demanded it.947948Return False if the req has a marker and fails949evaluation. Otherwise, return True.950"""951extra_evals = (952req.marker.evaluate({'extra': extra})953for extra in self.get(req, ()) + (extras or (None,))954)955return not req.marker or any(extra_evals)956957958class Environment:959"""Searchable snapshot of distributions on a search path"""960961def __init__(962self, search_path=None, platform=get_supported_platform(),963python=PY_MAJOR):964"""Snapshot distributions available on a search path965966Any distributions found on `search_path` are added to the environment.967`search_path` should be a sequence of ``sys.path`` items. If not968supplied, ``sys.path`` is used.969970`platform` is an optional string specifying the name of the platform971that platform-specific distributions must be compatible with. If972unspecified, it defaults to the current platform. `python` is an973optional string naming the desired version of Python (e.g. ``'3.6'``);974it defaults to the current version.975976You may explicitly set `platform` (and/or `python`) to ``None`` if you977wish to map *all* distributions, not just those compatible with the978running platform or Python version.979"""980self._distmap = {}981self.platform = platform982self.python = python983self.scan(search_path)984985def can_add(self, dist):986"""Is distribution `dist` acceptable for this environment?987988The distribution must match the platform and python version989requirements specified when this environment was created, or False990is returned.991"""992py_compat = (993self.python is None994or dist.py_version is None995or dist.py_version == self.python996)997return py_compat and compatible_platforms(dist.platform, self.platform)998999def remove(self, dist):1000"""Remove `dist` from the environment"""1001self._distmap[dist.key].remove(dist)10021003def scan(self, search_path=None):1004"""Scan `search_path` for distributions usable in this environment10051006Any distributions found are added to the environment.1007`search_path` should be a sequence of ``sys.path`` items. If not1008supplied, ``sys.path`` is used. Only distributions conforming to1009the platform/python version defined at initialization are added.1010"""1011if search_path is None:1012search_path = sys.path10131014for item in search_path:1015for dist in find_distributions(item):1016self.add(dist)10171018def __getitem__(self, project_name):1019"""Return a newest-to-oldest list of distributions for `project_name`10201021Uses case-insensitive `project_name` comparison, assuming all the1022project's distributions use their project's name converted to all1023lowercase as their key.10241025"""1026distribution_key = project_name.lower()1027return self._distmap.get(distribution_key, [])10281029def add(self, dist):1030"""Add `dist` if we ``can_add()`` it and it has not already been added1031"""1032if self.can_add(dist) and dist.has_version():1033dists = self._distmap.setdefault(dist.key, [])1034if dist not in dists:1035dists.append(dist)1036dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)10371038def best_match(1039self, req, working_set, installer=None, replace_conflicting=False):1040"""Find distribution best matching `req` and usable on `working_set`10411042This calls the ``find(req)`` method of the `working_set` to see if a1043suitable distribution is already active. (This may raise1044``VersionConflict`` if an unsuitable version of the project is already1045active in the specified `working_set`.) If a suitable distribution1046isn't active, this method returns the newest distribution in the1047environment that meets the ``Requirement`` in `req`. If no suitable1048distribution is found, and `installer` is supplied, then the result of1049calling the environment's ``obtain(req, installer)`` method will be1050returned.1051"""1052try:1053dist = working_set.find(req)1054except VersionConflict:1055if not replace_conflicting:1056raise1057dist = None1058if dist is not None:1059return dist1060for dist in self[req.key]:1061if dist in req:1062return dist1063# try to download/install1064return self.obtain(req, installer)10651066def obtain(self, requirement, installer=None):1067"""Obtain a distribution matching `requirement` (e.g. via download)10681069Obtain a distro that matches requirement (e.g. via download). In the1070base ``Environment`` class, this routine just returns1071``installer(requirement)``, unless `installer` is None, in which case1072None is returned instead. This method is a hook that allows subclasses1073to attempt other ways of obtaining a distribution before falling back1074to the `installer` argument."""1075if installer is not None:1076return installer(requirement)10771078def __iter__(self):1079"""Yield the unique project names of the available distributions"""1080for key in self._distmap.keys():1081if self[key]:1082yield key10831084def __iadd__(self, other):1085"""In-place addition of a distribution or environment"""1086if isinstance(other, Distribution):1087self.add(other)1088elif isinstance(other, Environment):1089for project in other:1090for dist in other[project]:1091self.add(dist)1092else:1093raise TypeError("Can't add %r to environment" % (other,))1094return self10951096def __add__(self, other):1097"""Add an environment or distribution to an environment"""1098new = self.__class__([], platform=None, python=None)1099for env in self, other:1100new += env1101return new110211031104# XXX backward compatibility1105AvailableDistributions = Environment110611071108class ExtractionError(RuntimeError):1109"""An error occurred extracting a resource11101111The following attributes are available from instances of this exception:11121113manager1114The resource manager that raised this exception11151116cache_path1117The base directory for resource extraction11181119original_error1120The exception instance that caused extraction to fail1121"""112211231124class ResourceManager:1125"""Manage resource extraction and packages"""1126extraction_path = None11271128def __init__(self):1129self.cached_files = {}11301131def resource_exists(self, package_or_requirement, resource_name):1132"""Does the named resource exist?"""1133return get_provider(package_or_requirement).has_resource(resource_name)11341135def resource_isdir(self, package_or_requirement, resource_name):1136"""Is the named resource an existing directory?"""1137return get_provider(package_or_requirement).resource_isdir(1138resource_name1139)11401141def resource_filename(self, package_or_requirement, resource_name):1142"""Return a true filesystem path for specified resource"""1143return get_provider(package_or_requirement).get_resource_filename(1144self, resource_name1145)11461147def resource_stream(self, package_or_requirement, resource_name):1148"""Return a readable file-like object for specified resource"""1149return get_provider(package_or_requirement).get_resource_stream(1150self, resource_name1151)11521153def resource_string(self, package_or_requirement, resource_name):1154"""Return specified resource as a string"""1155return get_provider(package_or_requirement).get_resource_string(1156self, resource_name1157)11581159def resource_listdir(self, package_or_requirement, resource_name):1160"""List the contents of the named resource directory"""1161return get_provider(package_or_requirement).resource_listdir(1162resource_name1163)11641165def extraction_error(self):1166"""Give an error message for problems extracting file(s)"""11671168old_exc = sys.exc_info()[1]1169cache_path = self.extraction_path or get_default_cache()11701171tmpl = textwrap.dedent("""1172Can't extract file(s) to egg cache11731174The following error occurred while trying to extract file(s)1175to the Python egg cache:11761177{old_exc}11781179The Python egg cache directory is currently set to:11801181{cache_path}11821183Perhaps your account does not have write access to this directory?1184You can change the cache directory by setting the PYTHON_EGG_CACHE1185environment variable to point to an accessible directory.1186""").lstrip()1187err = ExtractionError(tmpl.format(**locals()))1188err.manager = self1189err.cache_path = cache_path1190err.original_error = old_exc1191raise err11921193def get_cache_path(self, archive_name, names=()):1194"""Return absolute location in cache for `archive_name` and `names`11951196The parent directory of the resulting path will be created if it does1197not already exist. `archive_name` should be the base filename of the1198enclosing egg (which may not be the name of the enclosing zipfile!),1199including its ".egg" extension. `names`, if provided, should be a1200sequence of path name parts "under" the egg's extraction location.12011202This method should only be called by resource providers that need to1203obtain an extraction location, and only for names they intend to1204extract, as it tracks the generated names for possible cleanup later.1205"""1206extract_path = self.extraction_path or get_default_cache()1207target_path = os.path.join(extract_path, archive_name + '-tmp', *names)1208try:1209_bypass_ensure_directory(target_path)1210except Exception:1211self.extraction_error()12121213self._warn_unsafe_extraction_path(extract_path)12141215self.cached_files[target_path] = 11216return target_path12171218@staticmethod1219def _warn_unsafe_extraction_path(path):1220"""1221If the default extraction path is overridden and set to an insecure1222location, such as /tmp, it opens up an opportunity for an attacker to1223replace an extracted file with an unauthorized payload. Warn the user1224if a known insecure location is used.12251226See Distribute #375 for more details.1227"""1228if os.name == 'nt' and not path.startswith(os.environ['windir']):1229# On Windows, permissions are generally restrictive by default1230# and temp directories are not writable by other users, so1231# bypass the warning.1232return1233mode = os.stat(path).st_mode1234if mode & stat.S_IWOTH or mode & stat.S_IWGRP:1235msg = (1236"Extraction path is writable by group/others "1237"and vulnerable to attack when "1238"used with get_resource_filename ({path}). "1239"Consider a more secure "1240"location (set with .set_extraction_path or the "1241"PYTHON_EGG_CACHE environment variable)."1242).format(**locals())1243warnings.warn(msg, UserWarning)12441245def postprocess(self, tempname, filename):1246"""Perform any platform-specific postprocessing of `tempname`12471248This is where Mac header rewrites should be done; other platforms don't1249have anything special they should do.12501251Resource providers should call this method ONLY after successfully1252extracting a compressed resource. They must NOT call it on resources1253that are already in the filesystem.12541255`tempname` is the current (temporary) name of the file, and `filename`1256is the name it will be renamed to by the caller after this routine1257returns.1258"""12591260if os.name == 'posix':1261# Make the resource executable1262mode = ((os.stat(tempname).st_mode) | 0o555) & 0o77771263os.chmod(tempname, mode)12641265def set_extraction_path(self, path):1266"""Set the base path where resources will be extracted to, if needed.12671268If you do not call this routine before any extractions take place, the1269path defaults to the return value of ``get_default_cache()``. (Which1270is based on the ``PYTHON_EGG_CACHE`` environment variable, with various1271platform-specific fallbacks. See that routine's documentation for more1272details.)12731274Resources are extracted to subdirectories of this path based upon1275information given by the ``IResourceProvider``. You may set this to a1276temporary directory, but then you must call ``cleanup_resources()`` to1277delete the extracted files when done. There is no guarantee that1278``cleanup_resources()`` will be able to remove all extracted files.12791280(Note: you may not change the extraction path for a given resource1281manager once resources have been extracted, unless you first call1282``cleanup_resources()``.)1283"""1284if self.cached_files:1285raise ValueError(1286"Can't change extraction path, files already extracted"1287)12881289self.extraction_path = path12901291def cleanup_resources(self, force=False):1292"""1293Delete all extracted resource files and directories, returning a list1294of the file and directory names that could not be successfully removed.1295This function does not have any concurrency protection, so it should1296generally only be called when the extraction path is a temporary1297directory exclusive to a single process. This method is not1298automatically called; you must call it explicitly or register it as an1299``atexit`` function if you wish to ensure cleanup of a temporary1300directory used for extractions.1301"""1302# XXX130313041305def get_default_cache():1306"""1307Return the ``PYTHON_EGG_CACHE`` environment variable1308or a platform-relevant user cache dir for an app1309named "Python-Eggs".1310"""1311return (1312os.environ.get('PYTHON_EGG_CACHE')1313or appdirs.user_cache_dir(appname='Python-Eggs')1314)131513161317def safe_name(name):1318"""Convert an arbitrary string to a standard distribution name13191320Any runs of non-alphanumeric/. characters are replaced with a single '-'.1321"""1322return re.sub('[^A-Za-z0-9.]+', '-', name)132313241325def safe_version(version):1326"""1327Convert an arbitrary string to a standard version string1328"""1329try:1330# normalize the version1331return str(packaging.version.Version(version))1332except packaging.version.InvalidVersion:1333version = version.replace(' ', '.')1334return re.sub('[^A-Za-z0-9.]+', '-', version)133513361337def safe_extra(extra):1338"""Convert an arbitrary string to a standard 'extra' name13391340Any runs of non-alphanumeric characters are replaced with a single '_',1341and the result is always lowercased.1342"""1343return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower()134413451346def to_filename(name):1347"""Convert a project or version name to its filename-escaped form13481349Any '-' characters are currently replaced with '_'.1350"""1351return name.replace('-', '_')135213531354def invalid_marker(text):1355"""1356Validate text as a PEP 508 environment marker; return an exception1357if invalid or False otherwise.1358"""1359try:1360evaluate_marker(text)1361except SyntaxError as e:1362e.filename = None1363e.lineno = None1364return e1365return False136613671368def evaluate_marker(text, extra=None):1369"""1370Evaluate a PEP 508 environment marker.1371Return a boolean indicating the marker result in this environment.1372Raise SyntaxError if marker is invalid.13731374This implementation uses the 'pyparsing' module.1375"""1376try:1377marker = packaging.markers.Marker(text)1378return marker.evaluate()1379except packaging.markers.InvalidMarker as e:1380raise SyntaxError(e) from e138113821383class NullProvider:1384"""Try to implement resources and metadata for arbitrary PEP 302 loaders"""13851386egg_name = None1387egg_info = None1388loader = None13891390def __init__(self, module):1391self.loader = getattr(module, '__loader__', None)1392self.module_path = os.path.dirname(getattr(module, '__file__', ''))13931394def get_resource_filename(self, manager, resource_name):1395return self._fn(self.module_path, resource_name)13961397def get_resource_stream(self, manager, resource_name):1398return io.BytesIO(self.get_resource_string(manager, resource_name))13991400def get_resource_string(self, manager, resource_name):1401return self._get(self._fn(self.module_path, resource_name))14021403def has_resource(self, resource_name):1404return self._has(self._fn(self.module_path, resource_name))14051406def _get_metadata_path(self, name):1407return self._fn(self.egg_info, name)14081409def has_metadata(self, name):1410if not self.egg_info:1411return self.egg_info14121413path = self._get_metadata_path(name)1414return self._has(path)14151416def get_metadata(self, name):1417if not self.egg_info:1418return ""1419path = self._get_metadata_path(name)1420value = self._get(path)1421if six.PY2:1422return value1423try:1424return value.decode('utf-8')1425except UnicodeDecodeError as exc:1426# Include the path in the error message to simplify1427# troubleshooting, and without changing the exception type.1428exc.reason += ' in {} file at path: {}'.format(name, path)1429raise14301431def get_metadata_lines(self, name):1432return yield_lines(self.get_metadata(name))14331434def resource_isdir(self, resource_name):1435return self._isdir(self._fn(self.module_path, resource_name))14361437def metadata_isdir(self, name):1438return self.egg_info and self._isdir(self._fn(self.egg_info, name))14391440def resource_listdir(self, resource_name):1441return self._listdir(self._fn(self.module_path, resource_name))14421443def metadata_listdir(self, name):1444if self.egg_info:1445return self._listdir(self._fn(self.egg_info, name))1446return []14471448def run_script(self, script_name, namespace):1449script = 'scripts/' + script_name1450if not self.has_metadata(script):1451raise ResolutionError(1452"Script {script!r} not found in metadata at {self.egg_info!r}"1453.format(**locals()),1454)1455script_text = self.get_metadata(script).replace('\r\n', '\n')1456script_text = script_text.replace('\r', '\n')1457script_filename = self._fn(self.egg_info, script)1458namespace['__file__'] = script_filename1459if os.path.exists(script_filename):1460with open(script_filename) as fid:1461source = fid.read()1462code = compile(source, script_filename, 'exec')1463exec(code, namespace, namespace)1464else:1465from linecache import cache1466cache[script_filename] = (1467len(script_text), 0, script_text.split('\n'), script_filename1468)1469script_code = compile(script_text, script_filename, 'exec')1470exec(script_code, namespace, namespace)14711472def _has(self, path):1473raise NotImplementedError(1474"Can't perform this operation for unregistered loader type"1475)14761477def _isdir(self, path):1478raise NotImplementedError(1479"Can't perform this operation for unregistered loader type"1480)14811482def _listdir(self, path):1483raise NotImplementedError(1484"Can't perform this operation for unregistered loader type"1485)14861487def _fn(self, base, resource_name):1488self._validate_resource_path(resource_name)1489if resource_name:1490return os.path.join(base, *resource_name.split('/'))1491return base14921493@staticmethod1494def _validate_resource_path(path):1495"""1496Validate the resource paths according to the docs.1497https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access14981499>>> warned = getfixture('recwarn')1500>>> warnings.simplefilter('always')1501>>> vrp = NullProvider._validate_resource_path1502>>> vrp('foo/bar.txt')1503>>> bool(warned)1504False1505>>> vrp('../foo/bar.txt')1506>>> bool(warned)1507True1508>>> warned.clear()1509>>> vrp('/foo/bar.txt')1510>>> bool(warned)1511True1512>>> vrp('foo/../../bar.txt')1513>>> bool(warned)1514True1515>>> warned.clear()1516>>> vrp('foo/f../bar.txt')1517>>> bool(warned)1518False15191520Windows path separators are straight-up disallowed.1521>>> vrp(r'\\foo/bar.txt')1522Traceback (most recent call last):1523...1524ValueError: Use of .. or absolute path in a resource path \1525is not allowed.15261527>>> vrp(r'C:\\foo/bar.txt')1528Traceback (most recent call last):1529...1530ValueError: Use of .. or absolute path in a resource path \1531is not allowed.15321533Blank values are allowed15341535>>> vrp('')1536>>> bool(warned)1537False15381539Non-string values are not.15401541>>> vrp(None)1542Traceback (most recent call last):1543...1544AttributeError: ...1545"""1546invalid = (1547os.path.pardir in path.split(posixpath.sep) or1548posixpath.isabs(path) or1549ntpath.isabs(path)1550)1551if not invalid:1552return15531554msg = "Use of .. or absolute path in a resource path is not allowed."15551556# Aggressively disallow Windows absolute paths1557if ntpath.isabs(path) and not posixpath.isabs(path):1558raise ValueError(msg)15591560# for compatibility, warn; in future1561# raise ValueError(msg)1562warnings.warn(1563msg[:-1] + " and will raise exceptions in a future release.",1564DeprecationWarning,1565stacklevel=4,1566)15671568def _get(self, path):1569if hasattr(self.loader, 'get_data'):1570return self.loader.get_data(path)1571raise NotImplementedError(1572"Can't perform this operation for loaders without 'get_data()'"1573)157415751576register_loader_type(object, NullProvider)157715781579def _parents(path):1580"""1581yield all parents of path including path1582"""1583last = None1584while path != last:1585yield path1586last = path1587path, _ = os.path.split(path)158815891590class EggProvider(NullProvider):1591"""Provider based on a virtual filesystem"""15921593def __init__(self, module):1594NullProvider.__init__(self, module)1595self._setup_prefix()15961597def _setup_prefix(self):1598# Assume that metadata may be nested inside a "basket"1599# of multiple eggs and use module_path instead of .archive.1600eggs = filter(_is_egg_path, _parents(self.module_path))1601egg = next(eggs, None)1602egg and self._set_egg(egg)16031604def _set_egg(self, path):1605self.egg_name = os.path.basename(path)1606self.egg_info = os.path.join(path, 'EGG-INFO')1607self.egg_root = path160816091610class DefaultProvider(EggProvider):1611"""Provides access to package resources in the filesystem"""16121613def _has(self, path):1614return os.path.exists(path)16151616def _isdir(self, path):1617return os.path.isdir(path)16181619def _listdir(self, path):1620return os.listdir(path)16211622def get_resource_stream(self, manager, resource_name):1623return open(self._fn(self.module_path, resource_name), 'rb')16241625def _get(self, path):1626with open(path, 'rb') as stream:1627return stream.read()16281629@classmethod1630def _register(cls):1631loader_names = 'SourceFileLoader', 'SourcelessFileLoader',1632for name in loader_names:1633loader_cls = getattr(importlib_machinery, name, type(None))1634register_loader_type(loader_cls, cls)163516361637DefaultProvider._register()163816391640class EmptyProvider(NullProvider):1641"""Provider that returns nothing for all requests"""16421643module_path = None16441645_isdir = _has = lambda self, path: False16461647def _get(self, path):1648return ''16491650def _listdir(self, path):1651return []16521653def __init__(self):1654pass165516561657empty_provider = EmptyProvider()165816591660class ZipManifests(dict):1661"""1662zip manifest builder1663"""16641665@classmethod1666def build(cls, path):1667"""1668Build a dictionary similar to the zipimport directory1669caches, except instead of tuples, store ZipInfo objects.16701671Use a platform-specific path separator (os.sep) for the path keys1672for compatibility with pypy on Windows.1673"""1674with zipfile.ZipFile(path) as zfile:1675items = (1676(1677name.replace('/', os.sep),1678zfile.getinfo(name),1679)1680for name in zfile.namelist()1681)1682return dict(items)16831684load = build168516861687class MemoizedZipManifests(ZipManifests):1688"""1689Memoized zipfile manifests.1690"""1691manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime')16921693def load(self, path):1694"""1695Load a manifest at path or return a suitable manifest already loaded.1696"""1697path = os.path.normpath(path)1698mtime = os.stat(path).st_mtime16991700if path not in self or self[path].mtime != mtime:1701manifest = self.build(path)1702self[path] = self.manifest_mod(manifest, mtime)17031704return self[path].manifest170517061707class ZipProvider(EggProvider):1708"""Resource support for zips and eggs"""17091710eagers = None1711_zip_manifests = MemoizedZipManifests()17121713def __init__(self, module):1714EggProvider.__init__(self, module)1715self.zip_pre = self.loader.archive + os.sep17161717def _zipinfo_name(self, fspath):1718# Convert a virtual filename (full path to file) into a zipfile subpath1719# usable with the zipimport directory cache for our target archive1720fspath = fspath.rstrip(os.sep)1721if fspath == self.loader.archive:1722return ''1723if fspath.startswith(self.zip_pre):1724return fspath[len(self.zip_pre):]1725raise AssertionError(1726"%s is not a subpath of %s" % (fspath, self.zip_pre)1727)17281729def _parts(self, zip_path):1730# Convert a zipfile subpath into an egg-relative path part list.1731# pseudo-fs path1732fspath = self.zip_pre + zip_path1733if fspath.startswith(self.egg_root + os.sep):1734return fspath[len(self.egg_root) + 1:].split(os.sep)1735raise AssertionError(1736"%s is not a subpath of %s" % (fspath, self.egg_root)1737)17381739@property1740def zipinfo(self):1741return self._zip_manifests.load(self.loader.archive)17421743def get_resource_filename(self, manager, resource_name):1744if not self.egg_name:1745raise NotImplementedError(1746"resource_filename() only supported for .egg, not .zip"1747)1748# no need to lock for extraction, since we use temp names1749zip_path = self._resource_to_zip(resource_name)1750eagers = self._get_eager_resources()1751if '/'.join(self._parts(zip_path)) in eagers:1752for name in eagers:1753self._extract_resource(manager, self._eager_to_zip(name))1754return self._extract_resource(manager, zip_path)17551756@staticmethod1757def _get_date_and_size(zip_stat):1758size = zip_stat.file_size1759# ymdhms+wday, yday, dst1760date_time = zip_stat.date_time + (0, 0, -1)1761# 1980 offset already done1762timestamp = time.mktime(date_time)1763return timestamp, size17641765def _extract_resource(self, manager, zip_path):17661767if zip_path in self._index():1768for name in self._index()[zip_path]:1769last = self._extract_resource(1770manager, os.path.join(zip_path, name)1771)1772# return the extracted directory name1773return os.path.dirname(last)17741775timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])17761777if not WRITE_SUPPORT:1778raise IOError('"os.rename" and "os.unlink" are not supported '1779'on this platform')1780try:17811782real_path = manager.get_cache_path(1783self.egg_name, self._parts(zip_path)1784)17851786if self._is_current(real_path, zip_path):1787return real_path17881789outf, tmpnam = _mkstemp(1790".$extract",1791dir=os.path.dirname(real_path),1792)1793os.write(outf, self.loader.get_data(zip_path))1794os.close(outf)1795utime(tmpnam, (timestamp, timestamp))1796manager.postprocess(tmpnam, real_path)17971798try:1799rename(tmpnam, real_path)18001801except os.error:1802if os.path.isfile(real_path):1803if self._is_current(real_path, zip_path):1804# the file became current since it was checked above,1805# so proceed.1806return real_path1807# Windows, del old file and retry1808elif os.name == 'nt':1809unlink(real_path)1810rename(tmpnam, real_path)1811return real_path1812raise18131814except os.error:1815# report a user-friendly error1816manager.extraction_error()18171818return real_path18191820def _is_current(self, file_path, zip_path):1821"""1822Return True if the file_path is current for this zip_path1823"""1824timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])1825if not os.path.isfile(file_path):1826return False1827stat = os.stat(file_path)1828if stat.st_size != size or stat.st_mtime != timestamp:1829return False1830# check that the contents match1831zip_contents = self.loader.get_data(zip_path)1832with open(file_path, 'rb') as f:1833file_contents = f.read()1834return zip_contents == file_contents18351836def _get_eager_resources(self):1837if self.eagers is None:1838eagers = []1839for name in ('native_libs.txt', 'eager_resources.txt'):1840if self.has_metadata(name):1841eagers.extend(self.get_metadata_lines(name))1842self.eagers = eagers1843return self.eagers18441845def _index(self):1846try:1847return self._dirindex1848except AttributeError:1849ind = {}1850for path in self.zipinfo:1851parts = path.split(os.sep)1852while parts:1853parent = os.sep.join(parts[:-1])1854if parent in ind:1855ind[parent].append(parts[-1])1856break1857else:1858ind[parent] = [parts.pop()]1859self._dirindex = ind1860return ind18611862def _has(self, fspath):1863zip_path = self._zipinfo_name(fspath)1864return zip_path in self.zipinfo or zip_path in self._index()18651866def _isdir(self, fspath):1867return self._zipinfo_name(fspath) in self._index()18681869def _listdir(self, fspath):1870return list(self._index().get(self._zipinfo_name(fspath), ()))18711872def _eager_to_zip(self, resource_name):1873return self._zipinfo_name(self._fn(self.egg_root, resource_name))18741875def _resource_to_zip(self, resource_name):1876return self._zipinfo_name(self._fn(self.module_path, resource_name))187718781879register_loader_type(zipimport.zipimporter, ZipProvider)188018811882class FileMetadata(EmptyProvider):1883"""Metadata handler for standalone PKG-INFO files18841885Usage::18861887metadata = FileMetadata("/path/to/PKG-INFO")18881889This provider rejects all data and metadata requests except for PKG-INFO,1890which is treated as existing, and will be the contents of the file at1891the provided location.1892"""18931894def __init__(self, path):1895self.path = path18961897def _get_metadata_path(self, name):1898return self.path18991900def has_metadata(self, name):1901return name == 'PKG-INFO' and os.path.isfile(self.path)19021903def get_metadata(self, name):1904if name != 'PKG-INFO':1905raise KeyError("No metadata except PKG-INFO is available")19061907with io.open(self.path, encoding='utf-8', errors="replace") as f:1908metadata = f.read()1909self._warn_on_replacement(metadata)1910return metadata19111912def _warn_on_replacement(self, metadata):1913# Python 2.7 compat for: replacement_char = '�'1914replacement_char = b'\xef\xbf\xbd'.decode('utf-8')1915if replacement_char in metadata:1916tmpl = "{self.path} could not be properly decoded in UTF-8"1917msg = tmpl.format(**locals())1918warnings.warn(msg)19191920def get_metadata_lines(self, name):1921return yield_lines(self.get_metadata(name))192219231924class PathMetadata(DefaultProvider):1925"""Metadata provider for egg directories19261927Usage::19281929# Development eggs:19301931egg_info = "/path/to/PackageName.egg-info"1932base_dir = os.path.dirname(egg_info)1933metadata = PathMetadata(base_dir, egg_info)1934dist_name = os.path.splitext(os.path.basename(egg_info))[0]1935dist = Distribution(basedir, project_name=dist_name, metadata=metadata)19361937# Unpacked egg directories:19381939egg_path = "/path/to/PackageName-ver-pyver-etc.egg"1940metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO'))1941dist = Distribution.from_filename(egg_path, metadata=metadata)1942"""19431944def __init__(self, path, egg_info):1945self.module_path = path1946self.egg_info = egg_info194719481949class EggMetadata(ZipProvider):1950"""Metadata provider for .egg files"""19511952def __init__(self, importer):1953"""Create a metadata provider from a zipimporter"""19541955self.zip_pre = importer.archive + os.sep1956self.loader = importer1957if importer.prefix:1958self.module_path = os.path.join(importer.archive, importer.prefix)1959else:1960self.module_path = importer.archive1961self._setup_prefix()196219631964_declare_state('dict', _distribution_finders={})196519661967def register_finder(importer_type, distribution_finder):1968"""Register `distribution_finder` to find distributions in sys.path items19691970`importer_type` is the type or class of a PEP 302 "Importer" (sys.path item1971handler), and `distribution_finder` is a callable that, passed a path1972item and the importer instance, yields ``Distribution`` instances found on1973that path item. See ``pkg_resources.find_on_path`` for an example."""1974_distribution_finders[importer_type] = distribution_finder197519761977def find_distributions(path_item, only=False):1978"""Yield distributions accessible via `path_item`"""1979importer = get_importer(path_item)1980finder = _find_adapter(_distribution_finders, importer)1981return finder(importer, path_item, only)198219831984def find_eggs_in_zip(importer, path_item, only=False):1985"""1986Find eggs in zip files; possibly multiple nested eggs.1987"""1988if importer.archive.endswith('.whl'):1989# wheels are not supported with this finder1990# they don't have PKG-INFO metadata, and won't ever contain eggs1991return1992metadata = EggMetadata(importer)1993if metadata.has_metadata('PKG-INFO'):1994yield Distribution.from_filename(path_item, metadata=metadata)1995if only:1996# don't yield nested distros1997return1998for subitem in metadata.resource_listdir(''):1999if _is_egg_path(subitem):2000subpath = os.path.join(path_item, subitem)2001dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath)2002for dist in dists:2003yield dist2004elif subitem.lower().endswith('.dist-info'):2005subpath = os.path.join(path_item, subitem)2006submeta = EggMetadata(zipimport.zipimporter(subpath))2007submeta.egg_info = subpath2008yield Distribution.from_location(path_item, subitem, submeta)200920102011register_finder(zipimport.zipimporter, find_eggs_in_zip)201220132014def find_nothing(importer, path_item, only=False):2015return ()201620172018register_finder(object, find_nothing)201920202021def _by_version_descending(names):2022"""2023Given a list of filenames, return them in descending order2024by version number.20252026>>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg'2027>>> _by_version_descending(names)2028['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar']2029>>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg'2030>>> _by_version_descending(names)2031['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg']2032>>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg'2033>>> _by_version_descending(names)2034['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg']2035"""2036def _by_version(name):2037"""2038Parse each component of the filename2039"""2040name, ext = os.path.splitext(name)2041parts = itertools.chain(name.split('-'), [ext])2042return [packaging.version.parse(part) for part in parts]20432044return sorted(names, key=_by_version, reverse=True)204520462047def find_on_path(importer, path_item, only=False):2048"""Yield distributions accessible on a sys.path directory"""2049path_item = _normalize_cached(path_item)20502051if _is_unpacked_egg(path_item):2052yield Distribution.from_filename(2053path_item, metadata=PathMetadata(2054path_item, os.path.join(path_item, 'EGG-INFO')2055)2056)2057return20582059entries = safe_listdir(path_item)20602061# for performance, before sorting by version,2062# screen entries for only those that will yield2063# distributions2064filtered = (2065entry2066for entry in entries2067if dist_factory(path_item, entry, only)2068)20692070# scan for .egg and .egg-info in directory2071path_item_entries = _by_version_descending(filtered)2072for entry in path_item_entries:2073fullpath = os.path.join(path_item, entry)2074factory = dist_factory(path_item, entry, only)2075for dist in factory(fullpath):2076yield dist207720782079def dist_factory(path_item, entry, only):2080"""Return a dist_factory for the given entry."""2081lower = entry.lower()2082is_egg_info = lower.endswith('.egg-info')2083is_dist_info = (2084lower.endswith('.dist-info') and2085os.path.isdir(os.path.join(path_item, entry))2086)2087is_meta = is_egg_info or is_dist_info2088return (2089distributions_from_metadata2090if is_meta else2091find_distributions2092if not only and _is_egg_path(entry) else2093resolve_egg_link2094if not only and lower.endswith('.egg-link') else2095NoDists()2096)209720982099class NoDists:2100"""2101>>> bool(NoDists())2102False21032104>>> list(NoDists()('anything'))2105[]2106"""2107def __bool__(self):2108return False2109if six.PY2:2110__nonzero__ = __bool__21112112def __call__(self, fullpath):2113return iter(())211421152116def safe_listdir(path):2117"""2118Attempt to list contents of path, but suppress some exceptions.2119"""2120try:2121return os.listdir(path)2122except (PermissionError, NotADirectoryError):2123pass2124except OSError as e:2125# Ignore the directory if does not exist, not a directory or2126# permission denied2127ignorable = (2128e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT)2129# Python 2 on Windows needs to be handled this way :(2130or getattr(e, "winerror", None) == 2672131)2132if not ignorable:2133raise2134return ()213521362137def distributions_from_metadata(path):2138root = os.path.dirname(path)2139if os.path.isdir(path):2140if len(os.listdir(path)) == 0:2141# empty metadata dir; skip2142return2143metadata = PathMetadata(root, path)2144else:2145metadata = FileMetadata(path)2146entry = os.path.basename(path)2147yield Distribution.from_location(2148root, entry, metadata, precedence=DEVELOP_DIST,2149)215021512152def non_empty_lines(path):2153"""2154Yield non-empty lines from file at path2155"""2156with open(path) as f:2157for line in f:2158line = line.strip()2159if line:2160yield line216121622163def resolve_egg_link(path):2164"""2165Given a path to an .egg-link, resolve distributions2166present in the referenced path.2167"""2168referenced_paths = non_empty_lines(path)2169resolved_paths = (2170os.path.join(os.path.dirname(path), ref)2171for ref in referenced_paths2172)2173dist_groups = map(find_distributions, resolved_paths)2174return next(dist_groups, ())217521762177register_finder(pkgutil.ImpImporter, find_on_path)21782179if hasattr(importlib_machinery, 'FileFinder'):2180register_finder(importlib_machinery.FileFinder, find_on_path)21812182_declare_state('dict', _namespace_handlers={})2183_declare_state('dict', _namespace_packages={})218421852186def register_namespace_handler(importer_type, namespace_handler):2187"""Register `namespace_handler` to declare namespace packages21882189`importer_type` is the type or class of a PEP 302 "Importer" (sys.path item2190handler), and `namespace_handler` is a callable like this::21912192def namespace_handler(importer, path_entry, moduleName, module):2193# return a path_entry to use for child packages21942195Namespace handlers are only called if the importer object has already2196agreed that it can handle the relevant path item, and they should only2197return a subpath if the module __path__ does not already contain an2198equivalent subpath. For an example namespace handler, see2199``pkg_resources.file_ns_handler``.2200"""2201_namespace_handlers[importer_type] = namespace_handler220222032204def _handle_ns(packageName, path_item):2205"""Ensure that named package includes a subpath of path_item (if needed)"""22062207importer = get_importer(path_item)2208if importer is None:2209return None22102211# use find_spec (PEP 451) and fall-back to find_module (PEP 302)2212try:2213loader = importer.find_spec(packageName).loader2214except AttributeError:2215# capture warnings due to #11112216with warnings.catch_warnings():2217warnings.simplefilter("ignore")2218loader = importer.find_module(packageName)22192220if loader is None:2221return None2222module = sys.modules.get(packageName)2223if module is None:2224module = sys.modules[packageName] = types.ModuleType(packageName)2225module.__path__ = []2226_set_parent_ns(packageName)2227elif not hasattr(module, '__path__'):2228raise TypeError("Not a package:", packageName)2229handler = _find_adapter(_namespace_handlers, importer)2230subpath = handler(importer, path_item, packageName, module)2231if subpath is not None:2232path = module.__path__2233path.append(subpath)2234loader.load_module(packageName)2235_rebuild_mod_path(path, packageName, module)2236return subpath223722382239def _rebuild_mod_path(orig_path, package_name, module):2240"""2241Rebuild module.__path__ ensuring that all entries are ordered2242corresponding to their sys.path order2243"""2244sys_path = [_normalize_cached(p) for p in sys.path]22452246def safe_sys_path_index(entry):2247"""2248Workaround for #520 and #513.2249"""2250try:2251return sys_path.index(entry)2252except ValueError:2253return float('inf')22542255def position_in_sys_path(path):2256"""2257Return the ordinal of the path based on its position in sys.path2258"""2259path_parts = path.split(os.sep)2260module_parts = package_name.count('.') + 12261parts = path_parts[:-module_parts]2262return safe_sys_path_index(_normalize_cached(os.sep.join(parts)))22632264new_path = sorted(orig_path, key=position_in_sys_path)2265new_path = [_normalize_cached(p) for p in new_path]22662267if isinstance(module.__path__, list):2268module.__path__[:] = new_path2269else:2270module.__path__ = new_path227122722273def declare_namespace(packageName):2274"""Declare that package 'packageName' is a namespace package"""22752276_imp.acquire_lock()2277try:2278if packageName in _namespace_packages:2279return22802281path = sys.path2282parent, _, _ = packageName.rpartition('.')22832284if parent:2285declare_namespace(parent)2286if parent not in _namespace_packages:2287__import__(parent)2288try:2289path = sys.modules[parent].__path__2290except AttributeError as e:2291raise TypeError("Not a package:", parent) from e22922293# Track what packages are namespaces, so when new path items are added,2294# they can be updated2295_namespace_packages.setdefault(parent or None, []).append(packageName)2296_namespace_packages.setdefault(packageName, [])22972298for path_item in path:2299# Ensure all the parent's path items are reflected in the child,2300# if they apply2301_handle_ns(packageName, path_item)23022303finally:2304_imp.release_lock()230523062307def fixup_namespace_packages(path_item, parent=None):2308"""Ensure that previously-declared namespace packages include path_item"""2309_imp.acquire_lock()2310try:2311for package in _namespace_packages.get(parent, ()):2312subpath = _handle_ns(package, path_item)2313if subpath:2314fixup_namespace_packages(subpath, package)2315finally:2316_imp.release_lock()231723182319def file_ns_handler(importer, path_item, packageName, module):2320"""Compute an ns-package subpath for a filesystem or zipfile importer"""23212322subpath = os.path.join(path_item, packageName.split('.')[-1])2323normalized = _normalize_cached(subpath)2324for item in module.__path__:2325if _normalize_cached(item) == normalized:2326break2327else:2328# Only return the path if it's not already there2329return subpath233023312332register_namespace_handler(pkgutil.ImpImporter, file_ns_handler)2333register_namespace_handler(zipimport.zipimporter, file_ns_handler)23342335if hasattr(importlib_machinery, 'FileFinder'):2336register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler)233723382339def null_ns_handler(importer, path_item, packageName, module):2340return None234123422343register_namespace_handler(object, null_ns_handler)234423452346def normalize_path(filename):2347"""Normalize a file/dir name for comparison purposes"""2348return os.path.normcase(os.path.realpath(os.path.normpath(2349_cygwin_patch(filename))))235023512352def _cygwin_patch(filename): # pragma: nocover2353"""2354Contrary to POSIX 2008, on Cygwin, getcwd (3) contains2355symlink components. Using2356os.path.abspath() works around this limitation. A fix in os.getcwd()2357would probably better, in Cygwin even more so, except2358that this seems to be by design...2359"""2360return os.path.abspath(filename) if sys.platform == 'cygwin' else filename236123622363def _normalize_cached(filename, _cache={}):2364try:2365return _cache[filename]2366except KeyError:2367_cache[filename] = result = normalize_path(filename)2368return result236923702371def _is_egg_path(path):2372"""2373Determine if given path appears to be an egg.2374"""2375return path.lower().endswith('.egg')237623772378def _is_unpacked_egg(path):2379"""2380Determine if given path appears to be an unpacked egg.2381"""2382return (2383_is_egg_path(path) and2384os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO'))2385)238623872388def _set_parent_ns(packageName):2389parts = packageName.split('.')2390name = parts.pop()2391if parts:2392parent = '.'.join(parts)2393setattr(sys.modules[parent], name, sys.modules[packageName])239423952396def yield_lines(strs):2397"""Yield non-empty/non-comment lines of a string or sequence"""2398if isinstance(strs, six.string_types):2399for s in strs.splitlines():2400s = s.strip()2401# skip blank lines/comments2402if s and not s.startswith('#'):2403yield s2404else:2405for ss in strs:2406for s in yield_lines(ss):2407yield s240824092410MODULE = re.compile(r"\w+(\.\w+)*$").match2411EGG_NAME = re.compile(2412r"""2413(?P<name>[^-]+) (2414-(?P<ver>[^-]+) (2415-py(?P<pyver>[^-]+) (2416-(?P<plat>.+)2417)?2418)?2419)?2420""",2421re.VERBOSE | re.IGNORECASE,2422).match242324242425class EntryPoint:2426"""Object representing an advertised importable object"""24272428def __init__(self, name, module_name, attrs=(), extras=(), dist=None):2429if not MODULE(module_name):2430raise ValueError("Invalid module name", module_name)2431self.name = name2432self.module_name = module_name2433self.attrs = tuple(attrs)2434self.extras = tuple(extras)2435self.dist = dist24362437def __str__(self):2438s = "%s = %s" % (self.name, self.module_name)2439if self.attrs:2440s += ':' + '.'.join(self.attrs)2441if self.extras:2442s += ' [%s]' % ','.join(self.extras)2443return s24442445def __repr__(self):2446return "EntryPoint.parse(%r)" % str(self)24472448def load(self, require=True, *args, **kwargs):2449"""2450Require packages for this EntryPoint, then resolve it.2451"""2452if not require or args or kwargs:2453warnings.warn(2454"Parameters to load are deprecated. Call .resolve and "2455".require separately.",2456PkgResourcesDeprecationWarning,2457stacklevel=2,2458)2459if require:2460self.require(*args, **kwargs)2461return self.resolve()24622463def resolve(self):2464"""2465Resolve the entry point from its module and attrs.2466"""2467module = __import__(self.module_name, fromlist=['__name__'], level=0)2468try:2469return functools.reduce(getattr, self.attrs, module)2470except AttributeError as exc:2471raise ImportError(str(exc)) from exc24722473def require(self, env=None, installer=None):2474if self.extras and not self.dist:2475raise UnknownExtra("Can't require() without a distribution", self)24762477# Get the requirements for this entry point with all its extras and2478# then resolve them. We have to pass `extras` along when resolving so2479# that the working set knows what extras we want. Otherwise, for2480# dist-info distributions, the working set will assume that the2481# requirements for that extra are purely optional and skip over them.2482reqs = self.dist.requires(self.extras)2483items = working_set.resolve(reqs, env, installer, extras=self.extras)2484list(map(working_set.add, items))24852486pattern = re.compile(2487r'\s*'2488r'(?P<name>.+?)\s*'2489r'=\s*'2490r'(?P<module>[\w.]+)\s*'2491r'(:\s*(?P<attr>[\w.]+))?\s*'2492r'(?P<extras>\[.*\])?\s*$'2493)24942495@classmethod2496def parse(cls, src, dist=None):2497"""Parse a single entry point from string `src`24982499Entry point syntax follows the form::25002501name = some.module:some.attr [extra1, extra2]25022503The entry name and module name are required, but the ``:attrs`` and2504``[extras]`` parts are optional2505"""2506m = cls.pattern.match(src)2507if not m:2508msg = "EntryPoint must be in 'name=module:attrs [extras]' format"2509raise ValueError(msg, src)2510res = m.groupdict()2511extras = cls._parse_extras(res['extras'])2512attrs = res['attr'].split('.') if res['attr'] else ()2513return cls(res['name'], res['module'], attrs, extras, dist)25142515@classmethod2516def _parse_extras(cls, extras_spec):2517if not extras_spec:2518return ()2519req = Requirement.parse('x' + extras_spec)2520if req.specs:2521raise ValueError()2522return req.extras25232524@classmethod2525def parse_group(cls, group, lines, dist=None):2526"""Parse an entry point group"""2527if not MODULE(group):2528raise ValueError("Invalid group name", group)2529this = {}2530for line in yield_lines(lines):2531ep = cls.parse(line, dist)2532if ep.name in this:2533raise ValueError("Duplicate entry point", group, ep.name)2534this[ep.name] = ep2535return this25362537@classmethod2538def parse_map(cls, data, dist=None):2539"""Parse a map of entry point groups"""2540if isinstance(data, dict):2541data = data.items()2542else:2543data = split_sections(data)2544maps = {}2545for group, lines in data:2546if group is None:2547if not lines:2548continue2549raise ValueError("Entry points must be listed in groups")2550group = group.strip()2551if group in maps:2552raise ValueError("Duplicate group name", group)2553maps[group] = cls.parse_group(group, lines, dist)2554return maps255525562557def _version_from_file(lines):2558"""2559Given an iterable of lines from a Metadata file, return2560the value of the Version field, if present, or None otherwise.2561"""2562def is_version_line(line):2563return line.lower().startswith('version:')2564version_lines = filter(is_version_line, lines)2565line = next(iter(version_lines), '')2566_, _, value = line.partition(':')2567return safe_version(value.strip()) or None256825692570class Distribution:2571"""Wrap an actual or potential sys.path entry w/metadata"""2572PKG_INFO = 'PKG-INFO'25732574def __init__(2575self, location=None, metadata=None, project_name=None,2576version=None, py_version=PY_MAJOR, platform=None,2577precedence=EGG_DIST):2578self.project_name = safe_name(project_name or 'Unknown')2579if version is not None:2580self._version = safe_version(version)2581self.py_version = py_version2582self.platform = platform2583self.location = location2584self.precedence = precedence2585self._provider = metadata or empty_provider25862587@classmethod2588def from_location(cls, location, basename, metadata=None, **kw):2589project_name, version, py_version, platform = [None] * 42590basename, ext = os.path.splitext(basename)2591if ext.lower() in _distributionImpl:2592cls = _distributionImpl[ext.lower()]25932594match = EGG_NAME(basename)2595if match:2596project_name, version, py_version, platform = match.group(2597'name', 'ver', 'pyver', 'plat'2598)2599return cls(2600location, metadata, project_name=project_name, version=version,2601py_version=py_version, platform=platform, **kw2602)._reload_version()26032604def _reload_version(self):2605return self26062607@property2608def hashcmp(self):2609return (2610self.parsed_version,2611self.precedence,2612self.key,2613self.location,2614self.py_version or '',2615self.platform or '',2616)26172618def __hash__(self):2619return hash(self.hashcmp)26202621def __lt__(self, other):2622return self.hashcmp < other.hashcmp26232624def __le__(self, other):2625return self.hashcmp <= other.hashcmp26262627def __gt__(self, other):2628return self.hashcmp > other.hashcmp26292630def __ge__(self, other):2631return self.hashcmp >= other.hashcmp26322633def __eq__(self, other):2634if not isinstance(other, self.__class__):2635# It's not a Distribution, so they are not equal2636return False2637return self.hashcmp == other.hashcmp26382639def __ne__(self, other):2640return not self == other26412642# These properties have to be lazy so that we don't have to load any2643# metadata until/unless it's actually needed. (i.e., some distributions2644# may not know their name or version without loading PKG-INFO)26452646@property2647def key(self):2648try:2649return self._key2650except AttributeError:2651self._key = key = self.project_name.lower()2652return key26532654@property2655def parsed_version(self):2656if not hasattr(self, "_parsed_version"):2657self._parsed_version = parse_version(self.version)26582659return self._parsed_version26602661def _warn_legacy_version(self):2662LV = packaging.version.LegacyVersion2663is_legacy = isinstance(self._parsed_version, LV)2664if not is_legacy:2665return26662667# While an empty version is technically a legacy version and2668# is not a valid PEP 440 version, it's also unlikely to2669# actually come from someone and instead it is more likely that2670# it comes from setuptools attempting to parse a filename and2671# including it in the list. So for that we'll gate this warning2672# on if the version is anything at all or not.2673if not self.version:2674return26752676tmpl = textwrap.dedent("""2677'{project_name} ({version})' is being parsed as a legacy,2678non PEP 440,2679version. You may find odd behavior and sort order.2680In particular it will be sorted as less than 0.0. It2681is recommended to migrate to PEP 440 compatible2682versions.2683""").strip().replace('\n', ' ')26842685warnings.warn(tmpl.format(**vars(self)), PEP440Warning)26862687@property2688def version(self):2689try:2690return self._version2691except AttributeError as e:2692version = self._get_version()2693if version is None:2694path = self._get_metadata_path_for_display(self.PKG_INFO)2695msg = (2696"Missing 'Version:' header and/or {} file at path: {}"2697).format(self.PKG_INFO, path)2698raise ValueError(msg, self) from e26992700return version27012702@property2703def _dep_map(self):2704"""2705A map of extra to its list of (direct) requirements2706for this distribution, including the null extra.2707"""2708try:2709return self.__dep_map2710except AttributeError:2711self.__dep_map = self._filter_extras(self._build_dep_map())2712return self.__dep_map27132714@staticmethod2715def _filter_extras(dm):2716"""2717Given a mapping of extras to dependencies, strip off2718environment markers and filter out any dependencies2719not matching the markers.2720"""2721for extra in list(filter(None, dm)):2722new_extra = extra2723reqs = dm.pop(extra)2724new_extra, _, marker = extra.partition(':')2725fails_marker = marker and (2726invalid_marker(marker)2727or not evaluate_marker(marker)2728)2729if fails_marker:2730reqs = []2731new_extra = safe_extra(new_extra) or None27322733dm.setdefault(new_extra, []).extend(reqs)2734return dm27352736def _build_dep_map(self):2737dm = {}2738for name in 'requires.txt', 'depends.txt':2739for extra, reqs in split_sections(self._get_metadata(name)):2740dm.setdefault(extra, []).extend(parse_requirements(reqs))2741return dm27422743def requires(self, extras=()):2744"""List of Requirements needed for this distro if `extras` are used"""2745dm = self._dep_map2746deps = []2747deps.extend(dm.get(None, ()))2748for ext in extras:2749try:2750deps.extend(dm[safe_extra(ext)])2751except KeyError as e:2752raise UnknownExtra(2753"%s has no such extra feature %r" % (self, ext)2754) from e2755return deps27562757def _get_metadata_path_for_display(self, name):2758"""2759Return the path to the given metadata file, if available.2760"""2761try:2762# We need to access _get_metadata_path() on the provider object2763# directly rather than through this class's __getattr__()2764# since _get_metadata_path() is marked private.2765path = self._provider._get_metadata_path(name)27662767# Handle exceptions e.g. in case the distribution's metadata2768# provider doesn't support _get_metadata_path().2769except Exception:2770return '[could not detect]'27712772return path27732774def _get_metadata(self, name):2775if self.has_metadata(name):2776for line in self.get_metadata_lines(name):2777yield line27782779def _get_version(self):2780lines = self._get_metadata(self.PKG_INFO)2781version = _version_from_file(lines)27822783return version27842785def activate(self, path=None, replace=False):2786"""Ensure distribution is importable on `path` (default=sys.path)"""2787if path is None:2788path = sys.path2789self.insert_on(path, replace=replace)2790if path is sys.path:2791fixup_namespace_packages(self.location)2792for pkg in self._get_metadata('namespace_packages.txt'):2793if pkg in sys.modules:2794declare_namespace(pkg)27952796def egg_name(self):2797"""Return what this distribution's standard .egg filename should be"""2798filename = "%s-%s-py%s" % (2799to_filename(self.project_name), to_filename(self.version),2800self.py_version or PY_MAJOR2801)28022803if self.platform:2804filename += '-' + self.platform2805return filename28062807def __repr__(self):2808if self.location:2809return "%s (%s)" % (self, self.location)2810else:2811return str(self)28122813def __str__(self):2814try:2815version = getattr(self, 'version', None)2816except ValueError:2817version = None2818version = version or "[unknown version]"2819return "%s %s" % (self.project_name, version)28202821def __getattr__(self, attr):2822"""Delegate all unrecognized public attributes to .metadata provider"""2823if attr.startswith('_'):2824raise AttributeError(attr)2825return getattr(self._provider, attr)28262827def __dir__(self):2828return list(2829set(super(Distribution, self).__dir__())2830| set(2831attr for attr in self._provider.__dir__()2832if not attr.startswith('_')2833)2834)28352836if not hasattr(object, '__dir__'):2837# python 2.7 not supported2838del __dir__28392840@classmethod2841def from_filename(cls, filename, metadata=None, **kw):2842return cls.from_location(2843_normalize_cached(filename), os.path.basename(filename), metadata,2844**kw2845)28462847def as_requirement(self):2848"""Return a ``Requirement`` that matches this distribution exactly"""2849if isinstance(self.parsed_version, packaging.version.Version):2850spec = "%s==%s" % (self.project_name, self.parsed_version)2851else:2852spec = "%s===%s" % (self.project_name, self.parsed_version)28532854return Requirement.parse(spec)28552856def load_entry_point(self, group, name):2857"""Return the `name` entry point of `group` or raise ImportError"""2858ep = self.get_entry_info(group, name)2859if ep is None:2860raise ImportError("Entry point %r not found" % ((group, name),))2861return ep.load()28622863def get_entry_map(self, group=None):2864"""Return the entry point map for `group`, or the full entry map"""2865try:2866ep_map = self._ep_map2867except AttributeError:2868ep_map = self._ep_map = EntryPoint.parse_map(2869self._get_metadata('entry_points.txt'), self2870)2871if group is not None:2872return ep_map.get(group, {})2873return ep_map28742875def get_entry_info(self, group, name):2876"""Return the EntryPoint object for `group`+`name`, or ``None``"""2877return self.get_entry_map(group).get(name)28782879def insert_on(self, path, loc=None, replace=False):2880"""Ensure self.location is on path28812882If replace=False (default):2883- If location is already in path anywhere, do nothing.2884- Else:2885- If it's an egg and its parent directory is on path,2886insert just ahead of the parent.2887- Else: add to the end of path.2888If replace=True:2889- If location is already on path anywhere (not eggs)2890or higher priority than its parent (eggs)2891do nothing.2892- Else:2893- If it's an egg and its parent directory is on path,2894insert just ahead of the parent,2895removing any lower-priority entries.2896- Else: add it to the front of path.2897"""28982899loc = loc or self.location2900if not loc:2901return29022903nloc = _normalize_cached(loc)2904bdir = os.path.dirname(nloc)2905npath = [(p and _normalize_cached(p) or p) for p in path]29062907for p, item in enumerate(npath):2908if item == nloc:2909if replace:2910break2911else:2912# don't modify path (even removing duplicates) if2913# found and not replace2914return2915elif item == bdir and self.precedence == EGG_DIST:2916# if it's an .egg, give it precedence over its directory2917# UNLESS it's already been added to sys.path and replace=False2918if (not replace) and nloc in npath[p:]:2919return2920if path is sys.path:2921self.check_version_conflict()2922path.insert(p, loc)2923npath.insert(p, nloc)2924break2925else:2926if path is sys.path:2927self.check_version_conflict()2928if replace:2929path.insert(0, loc)2930else:2931path.append(loc)2932return29332934# p is the spot where we found or inserted loc; now remove duplicates2935while True:2936try:2937np = npath.index(nloc, p + 1)2938except ValueError:2939break2940else:2941del npath[np], path[np]2942# ha!2943p = np29442945return29462947def check_version_conflict(self):2948if self.key == 'setuptools':2949# ignore the inevitable setuptools self-conflicts :(2950return29512952nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))2953loc = normalize_path(self.location)2954for modname in self._get_metadata('top_level.txt'):2955if (modname not in sys.modules or modname in nsp2956or modname in _namespace_packages):2957continue2958if modname in ('pkg_resources', 'setuptools', 'site'):2959continue2960fn = getattr(sys.modules[modname], '__file__', None)2961if fn and (normalize_path(fn).startswith(loc) or2962fn.startswith(self.location)):2963continue2964issue_warning(2965"Module %s was already imported from %s, but %s is being added"2966" to sys.path" % (modname, fn, self.location),2967)29682969def has_version(self):2970try:2971self.version2972except ValueError:2973issue_warning("Unbuilt egg for " + repr(self))2974return False2975return True29762977def clone(self, **kw):2978"""Copy this distribution, substituting in any changed keyword args"""2979names = 'project_name version py_version platform location precedence'2980for attr in names.split():2981kw.setdefault(attr, getattr(self, attr, None))2982kw.setdefault('metadata', self._provider)2983return self.__class__(**kw)29842985@property2986def extras(self):2987return [dep for dep in self._dep_map if dep]298829892990class EggInfoDistribution(Distribution):2991def _reload_version(self):2992"""2993Packages installed by distutils (e.g. numpy or scipy),2994which uses an old safe_version, and so2995their version numbers can get mangled when2996converted to filenames (e.g., 1.11.0.dev0+2329eae to29971.11.0.dev0_2329eae). These distributions will not be2998parsed properly2999downstream by Distribution and safe_version, so3000take an extra step and try to get the version number from3001the metadata file itself instead of the filename.3002"""3003md_version = self._get_version()3004if md_version:3005self._version = md_version3006return self300730083009class DistInfoDistribution(Distribution):3010"""3011Wrap an actual or potential sys.path entry3012w/metadata, .dist-info style.3013"""3014PKG_INFO = 'METADATA'3015EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])")30163017@property3018def _parsed_pkg_info(self):3019"""Parse and cache metadata"""3020try:3021return self._pkg_info3022except AttributeError:3023metadata = self.get_metadata(self.PKG_INFO)3024self._pkg_info = email.parser.Parser().parsestr(metadata)3025return self._pkg_info30263027@property3028def _dep_map(self):3029try:3030return self.__dep_map3031except AttributeError:3032self.__dep_map = self._compute_dependencies()3033return self.__dep_map30343035def _compute_dependencies(self):3036"""Recompute this distribution's dependencies."""3037dm = self.__dep_map = {None: []}30383039reqs = []3040# Including any condition expressions3041for req in self._parsed_pkg_info.get_all('Requires-Dist') or []:3042reqs.extend(parse_requirements(req))30433044def reqs_for_extra(extra):3045for req in reqs:3046if not req.marker or req.marker.evaluate({'extra': extra}):3047yield req30483049common = frozenset(reqs_for_extra(None))3050dm[None].extend(common)30513052for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []:3053s_extra = safe_extra(extra.strip())3054dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common)30553056return dm305730583059_distributionImpl = {3060'.egg': Distribution,3061'.egg-info': EggInfoDistribution,3062'.dist-info': DistInfoDistribution,3063}306430653066def issue_warning(*args, **kw):3067level = 13068g = globals()3069try:3070# find the first stack frame that is *not* code in3071# the pkg_resources module, to use for the warning3072while sys._getframe(level).f_globals is g:3073level += 13074except ValueError:3075pass3076warnings.warn(stacklevel=level + 1, *args, **kw)307730783079def parse_requirements(strs):3080"""Yield ``Requirement`` objects for each specification in `strs`30813082`strs` must be a string, or a (possibly-nested) iterable thereof.3083"""3084# create a steppable iterator, so we can handle \-continuations3085lines = iter(yield_lines(strs))30863087for line in lines:3088# Drop comments -- a hash without a space may be in a URL.3089if ' #' in line:3090line = line[:line.find(' #')]3091# If there is a line continuation, drop it, and append the next line.3092if line.endswith('\\'):3093line = line[:-2].strip()3094try:3095line += next(lines)3096except StopIteration:3097return3098yield Requirement(line)309931003101class RequirementParseError(packaging.requirements.InvalidRequirement):3102"Compatibility wrapper for InvalidRequirement"310331043105class Requirement(packaging.requirements.Requirement):3106def __init__(self, requirement_string):3107"""DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""3108super(Requirement, self).__init__(requirement_string)3109self.unsafe_name = self.name3110project_name = safe_name(self.name)3111self.project_name, self.key = project_name, project_name.lower()3112self.specs = [3113(spec.operator, spec.version) for spec in self.specifier]3114self.extras = tuple(map(safe_extra, self.extras))3115self.hashCmp = (3116self.key,3117self.url,3118self.specifier,3119frozenset(self.extras),3120str(self.marker) if self.marker else None,3121)3122self.__hash = hash(self.hashCmp)31233124def __eq__(self, other):3125return (3126isinstance(other, Requirement) and3127self.hashCmp == other.hashCmp3128)31293130def __ne__(self, other):3131return not self == other31323133def __contains__(self, item):3134if isinstance(item, Distribution):3135if item.key != self.key:3136return False31373138item = item.version31393140# Allow prereleases always in order to match the previous behavior of3141# this method. In the future this should be smarter and follow PEP 4403142# more accurately.3143return self.specifier.contains(item, prereleases=True)31443145def __hash__(self):3146return self.__hash31473148def __repr__(self):3149return "Requirement.parse(%r)" % str(self)31503151@staticmethod3152def parse(s):3153req, = parse_requirements(s)3154return req315531563157def _always_object(classes):3158"""3159Ensure object appears in the mro even3160for old-style classes.3161"""3162if object not in classes:3163return classes + (object,)3164return classes316531663167def _find_adapter(registry, ob):3168"""Return an adapter factory for `ob` from `registry`"""3169types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob))))3170for t in types:3171if t in registry:3172return registry[t]317331743175def ensure_directory(path):3176"""Ensure that the parent directory of `path` exists"""3177dirname = os.path.dirname(path)3178os.makedirs(dirname, exist_ok=True)317931803181def _bypass_ensure_directory(path):3182"""Sandbox-bypassing version of ensure_directory()"""3183if not WRITE_SUPPORT:3184raise IOError('"os.mkdir" not supported on this platform.')3185dirname, filename = split(path)3186if dirname and filename and not isdir(dirname):3187_bypass_ensure_directory(dirname)3188try:3189mkdir(dirname, 0o755)3190except FileExistsError:3191pass319231933194def split_sections(s):3195"""Split a string or iterable thereof into (section, content) pairs31963197Each ``section`` is a stripped version of the section header ("[section]")3198and each ``content`` is a list of stripped lines excluding blank lines and3199comment-only lines. If there are any such lines before the first section3200header, they're returned in a first ``section`` of ``None``.3201"""3202section = None3203content = []3204for line in yield_lines(s):3205if line.startswith("["):3206if line.endswith("]"):3207if section or content:3208yield section, content3209section = line[1:-1].strip()3210content = []3211else:3212raise ValueError("Invalid section heading", line)3213else:3214content.append(line)32153216# wrap up last segment3217yield section, content321832193220def _mkstemp(*args, **kw):3221old_open = os.open3222try:3223# temporarily bypass sandboxing3224os.open = os_open3225return tempfile.mkstemp(*args, **kw)3226finally:3227# and then put it back3228os.open = old_open322932303231# Silence the PEP440Warning by default, so that end users don't get hit by it3232# randomly just because they use pkg_resources. We want to append the rule3233# because we want earlier uses of filterwarnings to take precedence over this3234# one.3235warnings.filterwarnings("ignore", category=PEP440Warning, append=True)323632373238# from jaraco.functools 1.33239def _call_aside(f, *args, **kwargs):3240f(*args, **kwargs)3241return f324232433244@_call_aside3245def _initialize(g=globals()):3246"Set up global resource manager (deliberately not state-saved)"3247manager = ResourceManager()3248g['_manager'] = manager3249g.update(3250(name, getattr(manager, name))3251for name in dir(manager)3252if not name.startswith('_')3253)325432553256@_call_aside3257def _initialize_master_working_set():3258"""3259Prepare the master working set and make the ``require()``3260API available.32613262This function has explicit effects on the global state3263of pkg_resources. It is intended to be invoked once at3264the initialization of this module.32653266Invocation by other packages is unsupported and done3267at their own risk.3268"""3269working_set = WorkingSet._build_master()3270_declare_state('object', working_set=working_set)32713272require = working_set.require3273iter_entry_points = working_set.iter_entry_points3274add_activation_listener = working_set.subscribe3275run_script = working_set.run_script3276# backward compatibility3277run_main = run_script3278# Activate all distributions already on sys.path with replace=False and3279# ensure that all distributions added to the working set in the future3280# (e.g. by calling ``require()``) will get activated as well,3281# with higher priority (replace=True).3282tuple(3283dist.activate(replace=False)3284for dist in working_set3285)3286add_activation_listener(3287lambda dist: dist.activate(replace=True),3288existing=False,3289)3290working_set.entries = []3291# match order3292list(map(working_set.add_entry, sys.path))3293globals().update(locals())329432953296class PkgResourcesDeprecationWarning(Warning):3297"""3298Base class for warning about deprecations in ``pkg_resources``32993300This class is not derived from ``DeprecationWarning``, and as such is3301visible by default.3302"""330333043305