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