fix
This commit is contained in:
138
.CondaPkg/env/Lib/importlib/__init__.py
vendored
Normal file
138
.CondaPkg/env/Lib/importlib/__init__.py
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
"""A pure Python implementation of import."""
|
||||
__all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload']
|
||||
|
||||
# Bootstrap help #####################################################
|
||||
|
||||
# Until bootstrapping is complete, DO NOT import any modules that attempt
|
||||
# to import importlib._bootstrap (directly or indirectly). Since this
|
||||
# partially initialised package would be present in sys.modules, those
|
||||
# modules would get an uninitialised copy of the source version, instead
|
||||
# of a fully initialised version (either the frozen one or the one
|
||||
# initialised below if the frozen one is not available).
|
||||
import _imp # Just the builtin component, NOT the full Python module
|
||||
import sys
|
||||
|
||||
try:
|
||||
import _frozen_importlib as _bootstrap
|
||||
except ImportError:
|
||||
from . import _bootstrap
|
||||
_bootstrap._setup(sys, _imp)
|
||||
else:
|
||||
# importlib._bootstrap is the built-in import, ensure we don't create
|
||||
# a second copy of the module.
|
||||
_bootstrap.__name__ = 'importlib._bootstrap'
|
||||
_bootstrap.__package__ = 'importlib'
|
||||
try:
|
||||
_bootstrap.__file__ = __file__.replace('__init__.py', '_bootstrap.py')
|
||||
except NameError:
|
||||
# __file__ is not guaranteed to be defined, e.g. if this code gets
|
||||
# frozen by a tool like cx_Freeze.
|
||||
pass
|
||||
sys.modules['importlib._bootstrap'] = _bootstrap
|
||||
|
||||
try:
|
||||
import _frozen_importlib_external as _bootstrap_external
|
||||
except ImportError:
|
||||
from . import _bootstrap_external
|
||||
_bootstrap_external._set_bootstrap_module(_bootstrap)
|
||||
_bootstrap._bootstrap_external = _bootstrap_external
|
||||
else:
|
||||
_bootstrap_external.__name__ = 'importlib._bootstrap_external'
|
||||
_bootstrap_external.__package__ = 'importlib'
|
||||
try:
|
||||
_bootstrap_external.__file__ = __file__.replace('__init__.py', '_bootstrap_external.py')
|
||||
except NameError:
|
||||
# __file__ is not guaranteed to be defined, e.g. if this code gets
|
||||
# frozen by a tool like cx_Freeze.
|
||||
pass
|
||||
sys.modules['importlib._bootstrap_external'] = _bootstrap_external
|
||||
|
||||
# To simplify imports in test code
|
||||
_pack_uint32 = _bootstrap_external._pack_uint32
|
||||
_unpack_uint32 = _bootstrap_external._unpack_uint32
|
||||
|
||||
# Fully bootstrapped at this point, import whatever you like, circular
|
||||
# dependencies and startup overhead minimisation permitting :)
|
||||
|
||||
import warnings
|
||||
|
||||
|
||||
# Public API #########################################################
|
||||
|
||||
from ._bootstrap import __import__
|
||||
|
||||
|
||||
def invalidate_caches():
|
||||
"""Call the invalidate_caches() method on all meta path finders stored in
|
||||
sys.meta_path (where implemented)."""
|
||||
for finder in sys.meta_path:
|
||||
if hasattr(finder, 'invalidate_caches'):
|
||||
finder.invalidate_caches()
|
||||
|
||||
|
||||
def import_module(name, package=None):
|
||||
"""Import a module.
|
||||
|
||||
The 'package' argument is required when performing a relative import. It
|
||||
specifies the package to use as the anchor point from which to resolve the
|
||||
relative import to an absolute import.
|
||||
|
||||
"""
|
||||
level = 0
|
||||
if name.startswith('.'):
|
||||
if not package:
|
||||
raise TypeError("the 'package' argument is required to perform a "
|
||||
f"relative import for {name!r}")
|
||||
for character in name:
|
||||
if character != '.':
|
||||
break
|
||||
level += 1
|
||||
return _bootstrap._gcd_import(name[level:], package, level)
|
||||
|
||||
|
||||
_RELOADING = {}
|
||||
|
||||
|
||||
def reload(module):
|
||||
"""Reload the module and return it.
|
||||
|
||||
The module must have been successfully imported before.
|
||||
|
||||
"""
|
||||
try:
|
||||
name = module.__spec__.name
|
||||
except AttributeError:
|
||||
try:
|
||||
name = module.__name__
|
||||
except AttributeError:
|
||||
raise TypeError("reload() argument must be a module")
|
||||
|
||||
if sys.modules.get(name) is not module:
|
||||
raise ImportError(f"module {name} not in sys.modules", name=name)
|
||||
if name in _RELOADING:
|
||||
return _RELOADING[name]
|
||||
_RELOADING[name] = module
|
||||
try:
|
||||
parent_name = name.rpartition('.')[0]
|
||||
if parent_name:
|
||||
try:
|
||||
parent = sys.modules[parent_name]
|
||||
except KeyError:
|
||||
raise ImportError(f"parent {parent_name!r} not in sys.modules",
|
||||
name=parent_name) from None
|
||||
else:
|
||||
pkgpath = parent.__path__
|
||||
else:
|
||||
pkgpath = None
|
||||
target = module
|
||||
spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
|
||||
if spec is None:
|
||||
raise ModuleNotFoundError(f"spec not found for the module {name!r}", name=name)
|
||||
_bootstrap._exec(spec, module)
|
||||
# The module may have replaced itself in sys.modules!
|
||||
return sys.modules[name]
|
||||
finally:
|
||||
try:
|
||||
del _RELOADING[name]
|
||||
except KeyError:
|
||||
pass
|
||||
BIN
.CondaPkg/env/Lib/importlib/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/_abc.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/_abc.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/_bootstrap.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/_bootstrap.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/_bootstrap_external.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/_bootstrap_external.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/abc.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/abc.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/machinery.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/machinery.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/readers.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/readers.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/simple.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/simple.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/__pycache__/util.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/__pycache__/util.cpython-312.pyc
vendored
Normal file
Binary file not shown.
39
.CondaPkg/env/Lib/importlib/_abc.py
vendored
Normal file
39
.CondaPkg/env/Lib/importlib/_abc.py
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
"""Subset of importlib.abc used to reduce importlib.util imports."""
|
||||
from . import _bootstrap
|
||||
import abc
|
||||
|
||||
|
||||
class Loader(metaclass=abc.ABCMeta):
|
||||
|
||||
"""Abstract base class for import loaders."""
|
||||
|
||||
def create_module(self, spec):
|
||||
"""Return a module to initialize and into which to load.
|
||||
|
||||
This method should raise ImportError if anything prevents it
|
||||
from creating a new module. It may return None to indicate
|
||||
that the spec should create the new module.
|
||||
"""
|
||||
# By default, defer to default semantics for the new module.
|
||||
return None
|
||||
|
||||
# We don't define exec_module() here since that would break
|
||||
# hasattr checks we do to support backward compatibility.
|
||||
|
||||
def load_module(self, fullname):
|
||||
"""Return the loaded module.
|
||||
|
||||
The module must be added to sys.modules and have import-related
|
||||
attributes set properly. The fullname is a str.
|
||||
|
||||
ImportError is raised on failure.
|
||||
|
||||
This method is deprecated in favor of loader.exec_module(). If
|
||||
exec_module() exists then it is used to provide a backwards-compatible
|
||||
functionality for this method.
|
||||
|
||||
"""
|
||||
if not hasattr(self, 'exec_module'):
|
||||
raise ImportError
|
||||
# Warning implemented in _load_module_shim().
|
||||
return _bootstrap._load_module_shim(self, fullname)
|
||||
1545
.CondaPkg/env/Lib/importlib/_bootstrap.py
vendored
Normal file
1545
.CondaPkg/env/Lib/importlib/_bootstrap.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1741
.CondaPkg/env/Lib/importlib/_bootstrap_external.py
vendored
Normal file
1741
.CondaPkg/env/Lib/importlib/_bootstrap_external.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
239
.CondaPkg/env/Lib/importlib/abc.py
vendored
Normal file
239
.CondaPkg/env/Lib/importlib/abc.py
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
"""Abstract base classes related to import."""
|
||||
from . import _bootstrap_external
|
||||
from . import machinery
|
||||
try:
|
||||
import _frozen_importlib
|
||||
except ImportError as exc:
|
||||
if exc.name != '_frozen_importlib':
|
||||
raise
|
||||
_frozen_importlib = None
|
||||
try:
|
||||
import _frozen_importlib_external
|
||||
except ImportError:
|
||||
_frozen_importlib_external = _bootstrap_external
|
||||
from ._abc import Loader
|
||||
import abc
|
||||
import warnings
|
||||
|
||||
from .resources import abc as _resources_abc
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Loader', 'MetaPathFinder', 'PathEntryFinder',
|
||||
'ResourceLoader', 'InspectLoader', 'ExecutionLoader',
|
||||
'FileLoader', 'SourceLoader',
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
"""
|
||||
For backwards compatibility, continue to make names
|
||||
from _resources_abc available through this module. #93963
|
||||
"""
|
||||
if name in _resources_abc.__all__:
|
||||
obj = getattr(_resources_abc, name)
|
||||
warnings._deprecated(f"{__name__}.{name}", remove=(3, 14))
|
||||
globals()[name] = obj
|
||||
return obj
|
||||
raise AttributeError(f'module {__name__!r} has no attribute {name!r}')
|
||||
|
||||
|
||||
def _register(abstract_cls, *classes):
|
||||
for cls in classes:
|
||||
abstract_cls.register(cls)
|
||||
if _frozen_importlib is not None:
|
||||
try:
|
||||
frozen_cls = getattr(_frozen_importlib, cls.__name__)
|
||||
except AttributeError:
|
||||
frozen_cls = getattr(_frozen_importlib_external, cls.__name__)
|
||||
abstract_cls.register(frozen_cls)
|
||||
|
||||
|
||||
class MetaPathFinder(metaclass=abc.ABCMeta):
|
||||
|
||||
"""Abstract base class for import finders on sys.meta_path."""
|
||||
|
||||
# We don't define find_spec() here since that would break
|
||||
# hasattr checks we do to support backward compatibility.
|
||||
|
||||
def invalidate_caches(self):
|
||||
"""An optional method for clearing the finder's cache, if any.
|
||||
This method is used by importlib.invalidate_caches().
|
||||
"""
|
||||
|
||||
_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter,
|
||||
machinery.PathFinder, machinery.WindowsRegistryFinder)
|
||||
|
||||
|
||||
class PathEntryFinder(metaclass=abc.ABCMeta):
|
||||
|
||||
"""Abstract base class for path entry finders used by PathFinder."""
|
||||
|
||||
def invalidate_caches(self):
|
||||
"""An optional method for clearing the finder's cache, if any.
|
||||
This method is used by PathFinder.invalidate_caches().
|
||||
"""
|
||||
|
||||
_register(PathEntryFinder, machinery.FileFinder)
|
||||
|
||||
|
||||
class ResourceLoader(Loader):
|
||||
|
||||
"""Abstract base class for loaders which can return data from their
|
||||
back-end storage.
|
||||
|
||||
This ABC represents one of the optional protocols specified by PEP 302.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_data(self, path):
|
||||
"""Abstract method which when implemented should return the bytes for
|
||||
the specified path. The path must be a str."""
|
||||
raise OSError
|
||||
|
||||
|
||||
class InspectLoader(Loader):
|
||||
|
||||
"""Abstract base class for loaders which support inspection about the
|
||||
modules they can load.
|
||||
|
||||
This ABC represents one of the optional protocols specified by PEP 302.
|
||||
|
||||
"""
|
||||
|
||||
def is_package(self, fullname):
|
||||
"""Optional method which when implemented should return whether the
|
||||
module is a package. The fullname is a str. Returns a bool.
|
||||
|
||||
Raises ImportError if the module cannot be found.
|
||||
"""
|
||||
raise ImportError
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Method which returns the code object for the module.
|
||||
|
||||
The fullname is a str. Returns a types.CodeType if possible, else
|
||||
returns None if a code object does not make sense
|
||||
(e.g. built-in module). Raises ImportError if the module cannot be
|
||||
found.
|
||||
"""
|
||||
source = self.get_source(fullname)
|
||||
if source is None:
|
||||
return None
|
||||
return self.source_to_code(source)
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_source(self, fullname):
|
||||
"""Abstract method which should return the source code for the
|
||||
module. The fullname is a str. Returns a str.
|
||||
|
||||
Raises ImportError if the module cannot be found.
|
||||
"""
|
||||
raise ImportError
|
||||
|
||||
@staticmethod
|
||||
def source_to_code(data, path='<string>'):
|
||||
"""Compile 'data' into a code object.
|
||||
|
||||
The 'data' argument can be anything that compile() can handle. The'path'
|
||||
argument should be where the data was retrieved (when applicable)."""
|
||||
return compile(data, path, 'exec', dont_inherit=True)
|
||||
|
||||
exec_module = _bootstrap_external._LoaderBasics.exec_module
|
||||
load_module = _bootstrap_external._LoaderBasics.load_module
|
||||
|
||||
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.NamespaceLoader)
|
||||
|
||||
|
||||
class ExecutionLoader(InspectLoader):
|
||||
|
||||
"""Abstract base class for loaders that wish to support the execution of
|
||||
modules as scripts.
|
||||
|
||||
This ABC represents one of the optional protocols specified in PEP 302.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_filename(self, fullname):
|
||||
"""Abstract method which should return the value that __file__ is to be
|
||||
set to.
|
||||
|
||||
Raises ImportError if the module cannot be found.
|
||||
"""
|
||||
raise ImportError
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Method to return the code object for fullname.
|
||||
|
||||
Should return None if not applicable (e.g. built-in module).
|
||||
Raise ImportError if the module cannot be found.
|
||||
"""
|
||||
source = self.get_source(fullname)
|
||||
if source is None:
|
||||
return None
|
||||
try:
|
||||
path = self.get_filename(fullname)
|
||||
except ImportError:
|
||||
return self.source_to_code(source)
|
||||
else:
|
||||
return self.source_to_code(source, path)
|
||||
|
||||
_register(ExecutionLoader, machinery.ExtensionFileLoader)
|
||||
|
||||
|
||||
class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader):
|
||||
|
||||
"""Abstract base class partially implementing the ResourceLoader and
|
||||
ExecutionLoader ABCs."""
|
||||
|
||||
_register(FileLoader, machinery.SourceFileLoader,
|
||||
machinery.SourcelessFileLoader)
|
||||
|
||||
|
||||
class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader):
|
||||
|
||||
"""Abstract base class for loading source code (and optionally any
|
||||
corresponding bytecode).
|
||||
|
||||
To support loading from source code, the abstractmethods inherited from
|
||||
ResourceLoader and ExecutionLoader need to be implemented. To also support
|
||||
loading from bytecode, the optional methods specified directly by this ABC
|
||||
is required.
|
||||
|
||||
Inherited abstractmethods not implemented in this ABC:
|
||||
|
||||
* ResourceLoader.get_data
|
||||
* ExecutionLoader.get_filename
|
||||
|
||||
"""
|
||||
|
||||
def path_mtime(self, path):
|
||||
"""Return the (int) modification time for the path (str)."""
|
||||
if self.path_stats.__func__ is SourceLoader.path_stats:
|
||||
raise OSError
|
||||
return int(self.path_stats(path)['mtime'])
|
||||
|
||||
def path_stats(self, path):
|
||||
"""Return a metadata dict for the source pointed to by the path (str).
|
||||
Possible keys:
|
||||
- 'mtime' (mandatory) is the numeric timestamp of last source
|
||||
code modification;
|
||||
- 'size' (optional) is the size in bytes of the source code.
|
||||
"""
|
||||
if self.path_mtime.__func__ is SourceLoader.path_mtime:
|
||||
raise OSError
|
||||
return {'mtime': self.path_mtime(path)}
|
||||
|
||||
def set_data(self, path, data):
|
||||
"""Write the bytes to the path (if possible).
|
||||
|
||||
Accepts a str path and data as bytes.
|
||||
|
||||
Any needed intermediary directories are to be created. If for some
|
||||
reason the file cannot be written because of permissions, fail
|
||||
silently.
|
||||
"""
|
||||
|
||||
_register(SourceLoader, machinery.SourceFileLoader)
|
||||
20
.CondaPkg/env/Lib/importlib/machinery.py
vendored
Normal file
20
.CondaPkg/env/Lib/importlib/machinery.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
"""The machinery of importlib: finders, loaders, hooks, etc."""
|
||||
|
||||
from ._bootstrap import ModuleSpec
|
||||
from ._bootstrap import BuiltinImporter
|
||||
from ._bootstrap import FrozenImporter
|
||||
from ._bootstrap_external import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
|
||||
OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
|
||||
EXTENSION_SUFFIXES)
|
||||
from ._bootstrap_external import WindowsRegistryFinder
|
||||
from ._bootstrap_external import PathFinder
|
||||
from ._bootstrap_external import FileFinder
|
||||
from ._bootstrap_external import SourceFileLoader
|
||||
from ._bootstrap_external import SourcelessFileLoader
|
||||
from ._bootstrap_external import ExtensionFileLoader
|
||||
from ._bootstrap_external import NamespaceLoader
|
||||
|
||||
|
||||
def all_suffixes():
|
||||
"""Returns a list of all recognized module suffixes for this process"""
|
||||
return SOURCE_SUFFIXES + BYTECODE_SUFFIXES + EXTENSION_SUFFIXES
|
||||
965
.CondaPkg/env/Lib/importlib/metadata/__init__.py
vendored
Normal file
965
.CondaPkg/env/Lib/importlib/metadata/__init__.py
vendored
Normal file
@@ -0,0 +1,965 @@
|
||||
import os
|
||||
import re
|
||||
import abc
|
||||
import csv
|
||||
import sys
|
||||
import email
|
||||
import pathlib
|
||||
import zipfile
|
||||
import operator
|
||||
import textwrap
|
||||
import warnings
|
||||
import functools
|
||||
import itertools
|
||||
import posixpath
|
||||
import contextlib
|
||||
import collections
|
||||
import inspect
|
||||
|
||||
from . import _adapters, _meta
|
||||
from ._collections import FreezableDefaultDict, Pair
|
||||
from ._functools import method_cache, pass_none
|
||||
from ._itertools import always_iterable, unique_everseen
|
||||
from ._meta import PackageMetadata, SimplePath
|
||||
|
||||
from contextlib import suppress
|
||||
from importlib import import_module
|
||||
from importlib.abc import MetaPathFinder
|
||||
from itertools import starmap
|
||||
from typing import List, Mapping, Optional, cast
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Distribution',
|
||||
'DistributionFinder',
|
||||
'PackageMetadata',
|
||||
'PackageNotFoundError',
|
||||
'distribution',
|
||||
'distributions',
|
||||
'entry_points',
|
||||
'files',
|
||||
'metadata',
|
||||
'packages_distributions',
|
||||
'requires',
|
||||
'version',
|
||||
]
|
||||
|
||||
|
||||
class PackageNotFoundError(ModuleNotFoundError):
|
||||
"""The package was not found."""
|
||||
|
||||
def __str__(self):
|
||||
return f"No package metadata was found for {self.name}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
(name,) = self.args
|
||||
return name
|
||||
|
||||
|
||||
class Sectioned:
|
||||
"""
|
||||
A simple entry point config parser for performance
|
||||
|
||||
>>> for item in Sectioned.read(Sectioned._sample):
|
||||
... print(item)
|
||||
Pair(name='sec1', value='# comments ignored')
|
||||
Pair(name='sec1', value='a = 1')
|
||||
Pair(name='sec1', value='b = 2')
|
||||
Pair(name='sec2', value='a = 2')
|
||||
|
||||
>>> res = Sectioned.section_pairs(Sectioned._sample)
|
||||
>>> item = next(res)
|
||||
>>> item.name
|
||||
'sec1'
|
||||
>>> item.value
|
||||
Pair(name='a', value='1')
|
||||
>>> item = next(res)
|
||||
>>> item.value
|
||||
Pair(name='b', value='2')
|
||||
>>> item = next(res)
|
||||
>>> item.name
|
||||
'sec2'
|
||||
>>> item.value
|
||||
Pair(name='a', value='2')
|
||||
>>> list(res)
|
||||
[]
|
||||
"""
|
||||
|
||||
_sample = textwrap.dedent(
|
||||
"""
|
||||
[sec1]
|
||||
# comments ignored
|
||||
a = 1
|
||||
b = 2
|
||||
|
||||
[sec2]
|
||||
a = 2
|
||||
"""
|
||||
).lstrip()
|
||||
|
||||
@classmethod
|
||||
def section_pairs(cls, text):
|
||||
return (
|
||||
section._replace(value=Pair.parse(section.value))
|
||||
for section in cls.read(text, filter_=cls.valid)
|
||||
if section.name is not None
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def read(text, filter_=None):
|
||||
lines = filter(filter_, map(str.strip, text.splitlines()))
|
||||
name = None
|
||||
for value in lines:
|
||||
section_match = value.startswith('[') and value.endswith(']')
|
||||
if section_match:
|
||||
name = value.strip('[]')
|
||||
continue
|
||||
yield Pair(name, value)
|
||||
|
||||
@staticmethod
|
||||
def valid(line):
|
||||
return line and not line.startswith('#')
|
||||
|
||||
|
||||
class DeprecatedTuple:
|
||||
"""
|
||||
Provide subscript item access for backward compatibility.
|
||||
|
||||
>>> recwarn = getfixture('recwarn')
|
||||
>>> ep = EntryPoint(name='name', value='value', group='group')
|
||||
>>> ep[:]
|
||||
('name', 'value', 'group')
|
||||
>>> ep[0]
|
||||
'name'
|
||||
>>> len(recwarn)
|
||||
1
|
||||
"""
|
||||
|
||||
# Do not remove prior to 2023-05-01 or Python 3.13
|
||||
_warn = functools.partial(
|
||||
warnings.warn,
|
||||
"EntryPoint tuple interface is deprecated. Access members by name.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
def __getitem__(self, item):
|
||||
self._warn()
|
||||
return self._key()[item]
|
||||
|
||||
|
||||
class EntryPoint(DeprecatedTuple):
|
||||
"""An entry point as defined by Python packaging conventions.
|
||||
|
||||
See `the packaging docs on entry points
|
||||
<https://packaging.python.org/specifications/entry-points/>`_
|
||||
for more information.
|
||||
|
||||
>>> ep = EntryPoint(
|
||||
... name=None, group=None, value='package.module:attr [extra1, extra2]')
|
||||
>>> ep.module
|
||||
'package.module'
|
||||
>>> ep.attr
|
||||
'attr'
|
||||
>>> ep.extras
|
||||
['extra1', 'extra2']
|
||||
"""
|
||||
|
||||
pattern = re.compile(
|
||||
r'(?P<module>[\w.]+)\s*'
|
||||
r'(:\s*(?P<attr>[\w.]+)\s*)?'
|
||||
r'((?P<extras>\[.*\])\s*)?$'
|
||||
)
|
||||
"""
|
||||
A regular expression describing the syntax for an entry point,
|
||||
which might look like:
|
||||
|
||||
- module
|
||||
- package.module
|
||||
- package.module:attribute
|
||||
- package.module:object.attribute
|
||||
- package.module:attr [extra1, extra2]
|
||||
|
||||
Other combinations are possible as well.
|
||||
|
||||
The expression is lenient about whitespace around the ':',
|
||||
following the attr, and following any extras.
|
||||
"""
|
||||
|
||||
name: str
|
||||
value: str
|
||||
group: str
|
||||
|
||||
dist: Optional['Distribution'] = None
|
||||
|
||||
def __init__(self, name, value, group):
|
||||
vars(self).update(name=name, value=value, group=group)
|
||||
|
||||
def load(self):
|
||||
"""Load the entry point from its definition. If only a module
|
||||
is indicated by the value, return that module. Otherwise,
|
||||
return the named object.
|
||||
"""
|
||||
match = self.pattern.match(self.value)
|
||||
module = import_module(match.group('module'))
|
||||
attrs = filter(None, (match.group('attr') or '').split('.'))
|
||||
return functools.reduce(getattr, attrs, module)
|
||||
|
||||
@property
|
||||
def module(self):
|
||||
match = self.pattern.match(self.value)
|
||||
return match.group('module')
|
||||
|
||||
@property
|
||||
def attr(self):
|
||||
match = self.pattern.match(self.value)
|
||||
return match.group('attr')
|
||||
|
||||
@property
|
||||
def extras(self):
|
||||
match = self.pattern.match(self.value)
|
||||
return re.findall(r'\w+', match.group('extras') or '')
|
||||
|
||||
def _for(self, dist):
|
||||
vars(self).update(dist=dist)
|
||||
return self
|
||||
|
||||
def matches(self, **params):
|
||||
"""
|
||||
EntryPoint matches the given parameters.
|
||||
|
||||
>>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]')
|
||||
>>> ep.matches(group='foo')
|
||||
True
|
||||
>>> ep.matches(name='bar', value='bing:bong [extra1, extra2]')
|
||||
True
|
||||
>>> ep.matches(group='foo', name='other')
|
||||
False
|
||||
>>> ep.matches()
|
||||
True
|
||||
>>> ep.matches(extras=['extra1', 'extra2'])
|
||||
True
|
||||
>>> ep.matches(module='bing')
|
||||
True
|
||||
>>> ep.matches(attr='bong')
|
||||
True
|
||||
"""
|
||||
attrs = (getattr(self, param) for param in params)
|
||||
return all(map(operator.eq, params.values(), attrs))
|
||||
|
||||
def _key(self):
|
||||
return self.name, self.value, self.group
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._key() < other._key()
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._key() == other._key()
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
raise AttributeError("EntryPoint objects are immutable.")
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f'EntryPoint(name={self.name!r}, value={self.value!r}, '
|
||||
f'group={self.group!r})'
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key())
|
||||
|
||||
|
||||
class EntryPoints(tuple):
|
||||
"""
|
||||
An immutable collection of selectable EntryPoint objects.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __getitem__(self, name): # -> EntryPoint:
|
||||
"""
|
||||
Get the EntryPoint in self matching name.
|
||||
"""
|
||||
try:
|
||||
return next(iter(self.select(name=name)))
|
||||
except StopIteration:
|
||||
raise KeyError(name)
|
||||
|
||||
def select(self, **params):
|
||||
"""
|
||||
Select entry points from self that match the
|
||||
given parameters (typically group and/or name).
|
||||
"""
|
||||
return EntryPoints(ep for ep in self if ep.matches(**params))
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
"""
|
||||
Return the set of all names of all entry points.
|
||||
"""
|
||||
return {ep.name for ep in self}
|
||||
|
||||
@property
|
||||
def groups(self):
|
||||
"""
|
||||
Return the set of all groups of all entry points.
|
||||
"""
|
||||
return {ep.group for ep in self}
|
||||
|
||||
@classmethod
|
||||
def _from_text_for(cls, text, dist):
|
||||
return cls(ep._for(dist) for ep in cls._from_text(text))
|
||||
|
||||
@staticmethod
|
||||
def _from_text(text):
|
||||
return (
|
||||
EntryPoint(name=item.value.name, value=item.value.value, group=item.name)
|
||||
for item in Sectioned.section_pairs(text or '')
|
||||
)
|
||||
|
||||
|
||||
class PackagePath(pathlib.PurePosixPath):
|
||||
"""A reference to a path in a package"""
|
||||
|
||||
def read_text(self, encoding='utf-8'):
|
||||
with self.locate().open(encoding=encoding) as stream:
|
||||
return stream.read()
|
||||
|
||||
def read_binary(self):
|
||||
with self.locate().open('rb') as stream:
|
||||
return stream.read()
|
||||
|
||||
def locate(self):
|
||||
"""Return a path-like object for this path"""
|
||||
return self.dist.locate_file(self)
|
||||
|
||||
|
||||
class FileHash:
|
||||
def __init__(self, spec):
|
||||
self.mode, _, self.value = spec.partition('=')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<FileHash mode: {self.mode} value: {self.value}>'
|
||||
|
||||
|
||||
class DeprecatedNonAbstract:
|
||||
def __new__(cls, *args, **kwargs):
|
||||
all_names = {
|
||||
name for subclass in inspect.getmro(cls) for name in vars(subclass)
|
||||
}
|
||||
abstract = {
|
||||
name
|
||||
for name in all_names
|
||||
if getattr(getattr(cls, name), '__isabstractmethod__', False)
|
||||
}
|
||||
if abstract:
|
||||
warnings.warn(
|
||||
f"Unimplemented abstract methods {abstract}",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return super().__new__(cls)
|
||||
|
||||
|
||||
class Distribution(DeprecatedNonAbstract):
|
||||
"""A Python distribution package."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def read_text(self, filename) -> Optional[str]:
|
||||
"""Attempt to load metadata file given by the name.
|
||||
|
||||
:param filename: The name of the file in the distribution info.
|
||||
:return: The text if found, otherwise None.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def locate_file(self, path):
|
||||
"""
|
||||
Given a path to a file in this distribution, return a path
|
||||
to it.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_name(cls, name: str):
|
||||
"""Return the Distribution for the given package name.
|
||||
|
||||
:param name: The name of the distribution package to search for.
|
||||
:return: The Distribution instance (or subclass thereof) for the named
|
||||
package, if found.
|
||||
:raises PackageNotFoundError: When the named package's distribution
|
||||
metadata cannot be found.
|
||||
:raises ValueError: When an invalid value is supplied for name.
|
||||
"""
|
||||
if not name:
|
||||
raise ValueError("A distribution name is required.")
|
||||
try:
|
||||
return next(cls.discover(name=name))
|
||||
except StopIteration:
|
||||
raise PackageNotFoundError(name)
|
||||
|
||||
@classmethod
|
||||
def discover(cls, **kwargs):
|
||||
"""Return an iterable of Distribution objects for all packages.
|
||||
|
||||
Pass a ``context`` or pass keyword arguments for constructing
|
||||
a context.
|
||||
|
||||
:context: A ``DistributionFinder.Context`` object.
|
||||
:return: Iterable of Distribution objects for all packages.
|
||||
"""
|
||||
context = kwargs.pop('context', None)
|
||||
if context and kwargs:
|
||||
raise ValueError("cannot accept context and kwargs")
|
||||
context = context or DistributionFinder.Context(**kwargs)
|
||||
return itertools.chain.from_iterable(
|
||||
resolver(context) for resolver in cls._discover_resolvers()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def at(path):
|
||||
"""Return a Distribution for the indicated metadata path
|
||||
|
||||
:param path: a string or path-like object
|
||||
:return: a concrete Distribution instance for the path
|
||||
"""
|
||||
return PathDistribution(pathlib.Path(path))
|
||||
|
||||
@staticmethod
|
||||
def _discover_resolvers():
|
||||
"""Search the meta_path for resolvers."""
|
||||
declared = (
|
||||
getattr(finder, 'find_distributions', None) for finder in sys.meta_path
|
||||
)
|
||||
return filter(None, declared)
|
||||
|
||||
@property
|
||||
def metadata(self) -> _meta.PackageMetadata:
|
||||
"""Return the parsed metadata for this Distribution.
|
||||
|
||||
The returned object will have keys that name the various bits of
|
||||
metadata. See PEP 566 for details.
|
||||
"""
|
||||
opt_text = (
|
||||
self.read_text('METADATA')
|
||||
or self.read_text('PKG-INFO')
|
||||
# This last clause is here to support old egg-info files. Its
|
||||
# effect is to just end up using the PathDistribution's self._path
|
||||
# (which points to the egg-info file) attribute unchanged.
|
||||
or self.read_text('')
|
||||
)
|
||||
text = cast(str, opt_text)
|
||||
return _adapters.Message(email.message_from_string(text))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the 'Name' metadata for the distribution package."""
|
||||
return self.metadata['Name']
|
||||
|
||||
@property
|
||||
def _normalized_name(self):
|
||||
"""Return a normalized version of the name."""
|
||||
return Prepared.normalize(self.name)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""Return the 'Version' metadata for the distribution package."""
|
||||
return self.metadata['Version']
|
||||
|
||||
@property
|
||||
def entry_points(self):
|
||||
return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
|
||||
|
||||
@property
|
||||
def files(self):
|
||||
"""Files in this distribution.
|
||||
|
||||
:return: List of PackagePath for this distribution or None
|
||||
|
||||
Result is `None` if the metadata file that enumerates files
|
||||
(i.e. RECORD for dist-info, or installed-files.txt or
|
||||
SOURCES.txt for egg-info) is missing.
|
||||
Result may be empty if the metadata exists but is empty.
|
||||
"""
|
||||
|
||||
def make_file(name, hash=None, size_str=None):
|
||||
result = PackagePath(name)
|
||||
result.hash = FileHash(hash) if hash else None
|
||||
result.size = int(size_str) if size_str else None
|
||||
result.dist = self
|
||||
return result
|
||||
|
||||
@pass_none
|
||||
def make_files(lines):
|
||||
return starmap(make_file, csv.reader(lines))
|
||||
|
||||
@pass_none
|
||||
def skip_missing_files(package_paths):
|
||||
return list(filter(lambda path: path.locate().exists(), package_paths))
|
||||
|
||||
return skip_missing_files(
|
||||
make_files(
|
||||
self._read_files_distinfo()
|
||||
or self._read_files_egginfo_installed()
|
||||
or self._read_files_egginfo_sources()
|
||||
)
|
||||
)
|
||||
|
||||
def _read_files_distinfo(self):
|
||||
"""
|
||||
Read the lines of RECORD
|
||||
"""
|
||||
text = self.read_text('RECORD')
|
||||
return text and text.splitlines()
|
||||
|
||||
def _read_files_egginfo_installed(self):
|
||||
"""
|
||||
Read installed-files.txt and return lines in a similar
|
||||
CSV-parsable format as RECORD: each file must be placed
|
||||
relative to the site-packages directory and must also be
|
||||
quoted (since file names can contain literal commas).
|
||||
|
||||
This file is written when the package is installed by pip,
|
||||
but it might not be written for other installation methods.
|
||||
Assume the file is accurate if it exists.
|
||||
"""
|
||||
text = self.read_text('installed-files.txt')
|
||||
# Prepend the .egg-info/ subdir to the lines in this file.
|
||||
# But this subdir is only available from PathDistribution's
|
||||
# self._path.
|
||||
subdir = getattr(self, '_path', None)
|
||||
if not text or not subdir:
|
||||
return
|
||||
|
||||
paths = (
|
||||
(subdir / name)
|
||||
.resolve()
|
||||
.relative_to(self.locate_file('').resolve())
|
||||
.as_posix()
|
||||
for name in text.splitlines()
|
||||
)
|
||||
return map('"{}"'.format, paths)
|
||||
|
||||
def _read_files_egginfo_sources(self):
|
||||
"""
|
||||
Read SOURCES.txt and return lines in a similar CSV-parsable
|
||||
format as RECORD: each file name must be quoted (since it
|
||||
might contain literal commas).
|
||||
|
||||
Note that SOURCES.txt is not a reliable source for what
|
||||
files are installed by a package. This file is generated
|
||||
for a source archive, and the files that are present
|
||||
there (e.g. setup.py) may not correctly reflect the files
|
||||
that are present after the package has been installed.
|
||||
"""
|
||||
text = self.read_text('SOURCES.txt')
|
||||
return text and map('"{}"'.format, text.splitlines())
|
||||
|
||||
@property
|
||||
def requires(self):
|
||||
"""Generated requirements specified for this Distribution"""
|
||||
reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
|
||||
return reqs and list(reqs)
|
||||
|
||||
def _read_dist_info_reqs(self):
|
||||
return self.metadata.get_all('Requires-Dist')
|
||||
|
||||
def _read_egg_info_reqs(self):
|
||||
source = self.read_text('requires.txt')
|
||||
return pass_none(self._deps_from_requires_text)(source)
|
||||
|
||||
@classmethod
|
||||
def _deps_from_requires_text(cls, source):
|
||||
return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source))
|
||||
|
||||
@staticmethod
|
||||
def _convert_egg_info_reqs_to_simple_reqs(sections):
|
||||
"""
|
||||
Historically, setuptools would solicit and store 'extra'
|
||||
requirements, including those with environment markers,
|
||||
in separate sections. More modern tools expect each
|
||||
dependency to be defined separately, with any relevant
|
||||
extras and environment markers attached directly to that
|
||||
requirement. This method converts the former to the
|
||||
latter. See _test_deps_from_requires_text for an example.
|
||||
"""
|
||||
|
||||
def make_condition(name):
|
||||
return name and f'extra == "{name}"'
|
||||
|
||||
def quoted_marker(section):
|
||||
section = section or ''
|
||||
extra, sep, markers = section.partition(':')
|
||||
if extra and markers:
|
||||
markers = f'({markers})'
|
||||
conditions = list(filter(None, [markers, make_condition(extra)]))
|
||||
return '; ' + ' and '.join(conditions) if conditions else ''
|
||||
|
||||
def url_req_space(req):
|
||||
"""
|
||||
PEP 508 requires a space between the url_spec and the quoted_marker.
|
||||
Ref python/importlib_metadata#357.
|
||||
"""
|
||||
# '@' is uniquely indicative of a url_req.
|
||||
return ' ' * ('@' in req)
|
||||
|
||||
for section in sections:
|
||||
space = url_req_space(section.value)
|
||||
yield section.value + space + quoted_marker(section.name)
|
||||
|
||||
|
||||
class DistributionFinder(MetaPathFinder):
|
||||
"""
|
||||
A MetaPathFinder capable of discovering installed distributions.
|
||||
"""
|
||||
|
||||
class Context:
|
||||
"""
|
||||
Keyword arguments presented by the caller to
|
||||
``distributions()`` or ``Distribution.discover()``
|
||||
to narrow the scope of a search for distributions
|
||||
in all DistributionFinders.
|
||||
|
||||
Each DistributionFinder may expect any parameters
|
||||
and should attempt to honor the canonical
|
||||
parameters defined below when appropriate.
|
||||
"""
|
||||
|
||||
name = None
|
||||
"""
|
||||
Specific name for which a distribution finder should match.
|
||||
A name of ``None`` matches all distributions.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
vars(self).update(kwargs)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""
|
||||
The sequence of directory path that a distribution finder
|
||||
should search.
|
||||
|
||||
Typically refers to Python installed package paths such as
|
||||
"site-packages" directories and defaults to ``sys.path``.
|
||||
"""
|
||||
return vars(self).get('path', sys.path)
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_distributions(self, context=Context()):
|
||||
"""
|
||||
Find distributions.
|
||||
|
||||
Return an iterable of all Distribution instances capable of
|
||||
loading the metadata for packages matching the ``context``,
|
||||
a DistributionFinder.Context instance.
|
||||
"""
|
||||
|
||||
|
||||
class FastPath:
|
||||
"""
|
||||
Micro-optimized class for searching a path for
|
||||
children.
|
||||
|
||||
>>> FastPath('').children()
|
||||
['...']
|
||||
"""
|
||||
|
||||
@functools.lru_cache() # type: ignore
|
||||
def __new__(cls, root):
|
||||
return super().__new__(cls)
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
|
||||
def joinpath(self, child):
|
||||
return pathlib.Path(self.root, child)
|
||||
|
||||
def children(self):
|
||||
with suppress(Exception):
|
||||
return os.listdir(self.root or '.')
|
||||
with suppress(Exception):
|
||||
return self.zip_children()
|
||||
return []
|
||||
|
||||
def zip_children(self):
|
||||
zip_path = zipfile.Path(self.root)
|
||||
names = zip_path.root.namelist()
|
||||
self.joinpath = zip_path.joinpath
|
||||
|
||||
return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)
|
||||
|
||||
def search(self, name):
|
||||
return self.lookup(self.mtime).search(name)
|
||||
|
||||
@property
|
||||
def mtime(self):
|
||||
with suppress(OSError):
|
||||
return os.stat(self.root).st_mtime
|
||||
self.lookup.cache_clear()
|
||||
|
||||
@method_cache
|
||||
def lookup(self, mtime):
|
||||
return Lookup(self)
|
||||
|
||||
|
||||
class Lookup:
|
||||
def __init__(self, path: FastPath):
|
||||
base = os.path.basename(path.root).lower()
|
||||
base_is_egg = base.endswith(".egg")
|
||||
self.infos = FreezableDefaultDict(list)
|
||||
self.eggs = FreezableDefaultDict(list)
|
||||
|
||||
for child in path.children():
|
||||
low = child.lower()
|
||||
if low.endswith((".dist-info", ".egg-info")):
|
||||
# rpartition is faster than splitext and suitable for this purpose.
|
||||
name = low.rpartition(".")[0].partition("-")[0]
|
||||
normalized = Prepared.normalize(name)
|
||||
self.infos[normalized].append(path.joinpath(child))
|
||||
elif base_is_egg and low == "egg-info":
|
||||
name = base.rpartition(".")[0].partition("-")[0]
|
||||
legacy_normalized = Prepared.legacy_normalize(name)
|
||||
self.eggs[legacy_normalized].append(path.joinpath(child))
|
||||
|
||||
self.infos.freeze()
|
||||
self.eggs.freeze()
|
||||
|
||||
def search(self, prepared):
|
||||
infos = (
|
||||
self.infos[prepared.normalized]
|
||||
if prepared
|
||||
else itertools.chain.from_iterable(self.infos.values())
|
||||
)
|
||||
eggs = (
|
||||
self.eggs[prepared.legacy_normalized]
|
||||
if prepared
|
||||
else itertools.chain.from_iterable(self.eggs.values())
|
||||
)
|
||||
return itertools.chain(infos, eggs)
|
||||
|
||||
|
||||
class Prepared:
|
||||
"""
|
||||
A prepared search for metadata on a possibly-named package.
|
||||
"""
|
||||
|
||||
normalized = None
|
||||
legacy_normalized = None
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
if name is None:
|
||||
return
|
||||
self.normalized = self.normalize(name)
|
||||
self.legacy_normalized = self.legacy_normalize(name)
|
||||
|
||||
@staticmethod
|
||||
def normalize(name):
|
||||
"""
|
||||
PEP 503 normalization plus dashes as underscores.
|
||||
"""
|
||||
return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
|
||||
|
||||
@staticmethod
|
||||
def legacy_normalize(name):
|
||||
"""
|
||||
Normalize the package name as found in the convention in
|
||||
older packaging tools versions and specs.
|
||||
"""
|
||||
return name.lower().replace('-', '_')
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.name)
|
||||
|
||||
|
||||
class MetadataPathFinder(DistributionFinder):
|
||||
@classmethod
|
||||
def find_distributions(cls, context=DistributionFinder.Context()):
|
||||
"""
|
||||
Find distributions.
|
||||
|
||||
Return an iterable of all Distribution instances capable of
|
||||
loading the metadata for packages matching ``context.name``
|
||||
(or all names if ``None`` indicated) along the paths in the list
|
||||
of directories ``context.path``.
|
||||
"""
|
||||
found = cls._search_paths(context.name, context.path)
|
||||
return map(PathDistribution, found)
|
||||
|
||||
@classmethod
|
||||
def _search_paths(cls, name, paths):
|
||||
"""Find metadata directories in paths heuristically."""
|
||||
prepared = Prepared(name)
|
||||
return itertools.chain.from_iterable(
|
||||
path.search(prepared) for path in map(FastPath, paths)
|
||||
)
|
||||
|
||||
def invalidate_caches(cls):
|
||||
FastPath.__new__.cache_clear()
|
||||
|
||||
|
||||
class PathDistribution(Distribution):
|
||||
def __init__(self, path: SimplePath):
|
||||
"""Construct a distribution.
|
||||
|
||||
:param path: SimplePath indicating the metadata directory.
|
||||
"""
|
||||
self._path = path
|
||||
|
||||
def read_text(self, filename):
|
||||
with suppress(
|
||||
FileNotFoundError,
|
||||
IsADirectoryError,
|
||||
KeyError,
|
||||
NotADirectoryError,
|
||||
PermissionError,
|
||||
):
|
||||
return self._path.joinpath(filename).read_text(encoding='utf-8')
|
||||
|
||||
read_text.__doc__ = Distribution.read_text.__doc__
|
||||
|
||||
def locate_file(self, path):
|
||||
return self._path.parent / path
|
||||
|
||||
@property
|
||||
def _normalized_name(self):
|
||||
"""
|
||||
Performance optimization: where possible, resolve the
|
||||
normalized name from the file system path.
|
||||
"""
|
||||
stem = os.path.basename(str(self._path))
|
||||
return (
|
||||
pass_none(Prepared.normalize)(self._name_from_stem(stem))
|
||||
or super()._normalized_name
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _name_from_stem(stem):
|
||||
"""
|
||||
>>> PathDistribution._name_from_stem('foo-3.0.egg-info')
|
||||
'foo'
|
||||
>>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info')
|
||||
'CherryPy'
|
||||
>>> PathDistribution._name_from_stem('face.egg-info')
|
||||
'face'
|
||||
>>> PathDistribution._name_from_stem('foo.bar')
|
||||
"""
|
||||
filename, ext = os.path.splitext(stem)
|
||||
if ext not in ('.dist-info', '.egg-info'):
|
||||
return
|
||||
name, sep, rest = filename.partition('-')
|
||||
return name
|
||||
|
||||
|
||||
def distribution(distribution_name):
|
||||
"""Get the ``Distribution`` instance for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package as a string.
|
||||
:return: A ``Distribution`` instance (or subclass thereof).
|
||||
"""
|
||||
return Distribution.from_name(distribution_name)
|
||||
|
||||
|
||||
def distributions(**kwargs):
|
||||
"""Get all ``Distribution`` instances in the current environment.
|
||||
|
||||
:return: An iterable of ``Distribution`` instances.
|
||||
"""
|
||||
return Distribution.discover(**kwargs)
|
||||
|
||||
|
||||
def metadata(distribution_name) -> _meta.PackageMetadata:
|
||||
"""Get the metadata for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package to query.
|
||||
:return: A PackageMetadata containing the parsed metadata.
|
||||
"""
|
||||
return Distribution.from_name(distribution_name).metadata
|
||||
|
||||
|
||||
def version(distribution_name):
|
||||
"""Get the version string for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package to query.
|
||||
:return: The version string for the package as defined in the package's
|
||||
"Version" metadata key.
|
||||
"""
|
||||
return distribution(distribution_name).version
|
||||
|
||||
|
||||
_unique = functools.partial(
|
||||
unique_everseen,
|
||||
key=operator.attrgetter('_normalized_name'),
|
||||
)
|
||||
"""
|
||||
Wrapper for ``distributions`` to return unique distributions by name.
|
||||
"""
|
||||
|
||||
|
||||
def entry_points(**params) -> EntryPoints:
|
||||
"""Return EntryPoint objects for all installed packages.
|
||||
|
||||
Pass selection parameters (group or name) to filter the
|
||||
result to entry points matching those properties (see
|
||||
EntryPoints.select()).
|
||||
|
||||
:return: EntryPoints for all installed packages.
|
||||
"""
|
||||
eps = itertools.chain.from_iterable(
|
||||
dist.entry_points for dist in _unique(distributions())
|
||||
)
|
||||
return EntryPoints(eps).select(**params)
|
||||
|
||||
|
||||
def files(distribution_name):
|
||||
"""Return a list of files for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package to query.
|
||||
:return: List of files composing the distribution.
|
||||
"""
|
||||
return distribution(distribution_name).files
|
||||
|
||||
|
||||
def requires(distribution_name):
|
||||
"""
|
||||
Return a list of requirements for the named package.
|
||||
|
||||
:return: An iterator of requirements, suitable for
|
||||
packaging.requirement.Requirement.
|
||||
"""
|
||||
return distribution(distribution_name).requires
|
||||
|
||||
|
||||
def packages_distributions() -> Mapping[str, List[str]]:
|
||||
"""
|
||||
Return a mapping of top-level packages to their
|
||||
distributions.
|
||||
|
||||
>>> import collections.abc
|
||||
>>> pkgs = packages_distributions()
|
||||
>>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values())
|
||||
True
|
||||
"""
|
||||
pkg_to_dist = collections.defaultdict(list)
|
||||
for dist in distributions():
|
||||
for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
|
||||
pkg_to_dist[pkg].append(dist.metadata['Name'])
|
||||
return dict(pkg_to_dist)
|
||||
|
||||
|
||||
def _top_level_declared(dist):
|
||||
return (dist.read_text('top_level.txt') or '').split()
|
||||
|
||||
|
||||
def _top_level_inferred(dist):
|
||||
opt_names = {
|
||||
f.parts[0] if len(f.parts) > 1 else inspect.getmodulename(f)
|
||||
for f in always_iterable(dist.files)
|
||||
}
|
||||
|
||||
@pass_none
|
||||
def importable_name(name):
|
||||
return '.' not in name
|
||||
|
||||
return filter(importable_name, opt_names)
|
||||
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_adapters.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_adapters.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_collections.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_collections.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_functools.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_functools.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_itertools.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_itertools.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_meta.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_meta.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_text.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/metadata/__pycache__/_text.cpython-312.pyc
vendored
Normal file
Binary file not shown.
89
.CondaPkg/env/Lib/importlib/metadata/_adapters.py
vendored
Normal file
89
.CondaPkg/env/Lib/importlib/metadata/_adapters.py
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
import functools
|
||||
import warnings
|
||||
import re
|
||||
import textwrap
|
||||
import email.message
|
||||
|
||||
from ._text import FoldedCase
|
||||
|
||||
|
||||
# Do not remove prior to 2024-01-01 or Python 3.14
|
||||
_warn = functools.partial(
|
||||
warnings.warn,
|
||||
"Implicit None on return values is deprecated and will raise KeyErrors.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
class Message(email.message.Message):
|
||||
multiple_use_keys = set(
|
||||
map(
|
||||
FoldedCase,
|
||||
[
|
||||
'Classifier',
|
||||
'Obsoletes-Dist',
|
||||
'Platform',
|
||||
'Project-URL',
|
||||
'Provides-Dist',
|
||||
'Provides-Extra',
|
||||
'Requires-Dist',
|
||||
'Requires-External',
|
||||
'Supported-Platform',
|
||||
'Dynamic',
|
||||
],
|
||||
)
|
||||
)
|
||||
"""
|
||||
Keys that may be indicated multiple times per PEP 566.
|
||||
"""
|
||||
|
||||
def __new__(cls, orig: email.message.Message):
|
||||
res = super().__new__(cls)
|
||||
vars(res).update(vars(orig))
|
||||
return res
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._headers = self._repair_headers()
|
||||
|
||||
# suppress spurious error from mypy
|
||||
def __iter__(self):
|
||||
return super().__iter__()
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""
|
||||
Warn users that a ``KeyError`` can be expected when a
|
||||
mising key is supplied. Ref python/importlib_metadata#371.
|
||||
"""
|
||||
res = super().__getitem__(item)
|
||||
if res is None:
|
||||
_warn()
|
||||
return res
|
||||
|
||||
def _repair_headers(self):
|
||||
def redent(value):
|
||||
"Correct for RFC822 indentation"
|
||||
if not value or '\n' not in value:
|
||||
return value
|
||||
return textwrap.dedent(' ' * 8 + value)
|
||||
|
||||
headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
|
||||
if self._payload:
|
||||
headers.append(('Description', self.get_payload()))
|
||||
return headers
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
Convert PackageMetadata to a JSON-compatible format
|
||||
per PEP 0566.
|
||||
"""
|
||||
|
||||
def transform(key):
|
||||
value = self.get_all(key) if key in self.multiple_use_keys else self[key]
|
||||
if key == 'Keywords':
|
||||
value = re.split(r'\s+', value)
|
||||
tk = key.lower().replace('-', '_')
|
||||
return tk, value
|
||||
|
||||
return dict(map(transform, map(FoldedCase, self)))
|
||||
30
.CondaPkg/env/Lib/importlib/metadata/_collections.py
vendored
Normal file
30
.CondaPkg/env/Lib/importlib/metadata/_collections.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import collections
|
||||
|
||||
|
||||
# from jaraco.collections 3.3
|
||||
class FreezableDefaultDict(collections.defaultdict):
|
||||
"""
|
||||
Often it is desirable to prevent the mutation of
|
||||
a default dict after its initial construction, such
|
||||
as to prevent mutation during iteration.
|
||||
|
||||
>>> dd = FreezableDefaultDict(list)
|
||||
>>> dd[0].append('1')
|
||||
>>> dd.freeze()
|
||||
>>> dd[1]
|
||||
[]
|
||||
>>> len(dd)
|
||||
1
|
||||
"""
|
||||
|
||||
def __missing__(self, key):
|
||||
return getattr(self, '_frozen', super().__missing__)(key)
|
||||
|
||||
def freeze(self):
|
||||
self._frozen = lambda key: self.default_factory()
|
||||
|
||||
|
||||
class Pair(collections.namedtuple('Pair', 'name value')):
|
||||
@classmethod
|
||||
def parse(cls, text):
|
||||
return cls(*map(str.strip, text.split("=", 1)))
|
||||
104
.CondaPkg/env/Lib/importlib/metadata/_functools.py
vendored
Normal file
104
.CondaPkg/env/Lib/importlib/metadata/_functools.py
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
import types
|
||||
import functools
|
||||
|
||||
|
||||
# from jaraco.functools 3.3
|
||||
def method_cache(method, cache_wrapper=None):
|
||||
"""
|
||||
Wrap lru_cache to support storing the cache data in the object instances.
|
||||
|
||||
Abstracts the common paradigm where the method explicitly saves an
|
||||
underscore-prefixed protected property on first call and returns that
|
||||
subsequently.
|
||||
|
||||
>>> class MyClass:
|
||||
... calls = 0
|
||||
...
|
||||
... @method_cache
|
||||
... def method(self, value):
|
||||
... self.calls += 1
|
||||
... return value
|
||||
|
||||
>>> a = MyClass()
|
||||
>>> a.method(3)
|
||||
3
|
||||
>>> for x in range(75):
|
||||
... res = a.method(x)
|
||||
>>> a.calls
|
||||
75
|
||||
|
||||
Note that the apparent behavior will be exactly like that of lru_cache
|
||||
except that the cache is stored on each instance, so values in one
|
||||
instance will not flush values from another, and when an instance is
|
||||
deleted, so are the cached values for that instance.
|
||||
|
||||
>>> b = MyClass()
|
||||
>>> for x in range(35):
|
||||
... res = b.method(x)
|
||||
>>> b.calls
|
||||
35
|
||||
>>> a.method(0)
|
||||
0
|
||||
>>> a.calls
|
||||
75
|
||||
|
||||
Note that if method had been decorated with ``functools.lru_cache()``,
|
||||
a.calls would have been 76 (due to the cached value of 0 having been
|
||||
flushed by the 'b' instance).
|
||||
|
||||
Clear the cache with ``.cache_clear()``
|
||||
|
||||
>>> a.method.cache_clear()
|
||||
|
||||
Same for a method that hasn't yet been called.
|
||||
|
||||
>>> c = MyClass()
|
||||
>>> c.method.cache_clear()
|
||||
|
||||
Another cache wrapper may be supplied:
|
||||
|
||||
>>> cache = functools.lru_cache(maxsize=2)
|
||||
>>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
|
||||
>>> a = MyClass()
|
||||
>>> a.method2()
|
||||
3
|
||||
|
||||
Caution - do not subsequently wrap the method with another decorator, such
|
||||
as ``@property``, which changes the semantics of the function.
|
||||
|
||||
See also
|
||||
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
|
||||
for another implementation and additional justification.
|
||||
"""
|
||||
cache_wrapper = cache_wrapper or functools.lru_cache()
|
||||
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# it's the first call, replace the method with a cached, bound method
|
||||
bound_method = types.MethodType(method, self)
|
||||
cached_method = cache_wrapper(bound_method)
|
||||
setattr(self, method.__name__, cached_method)
|
||||
return cached_method(*args, **kwargs)
|
||||
|
||||
# Support cache clear even before cache has been created.
|
||||
wrapper.cache_clear = lambda: None
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# From jaraco.functools 3.3
|
||||
def pass_none(func):
|
||||
"""
|
||||
Wrap func so it's not called if its first param is None
|
||||
|
||||
>>> print_text = pass_none(print)
|
||||
>>> print_text('text')
|
||||
text
|
||||
>>> print_text(None)
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(param, *args, **kwargs):
|
||||
if param is not None:
|
||||
return func(param, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
73
.CondaPkg/env/Lib/importlib/metadata/_itertools.py
vendored
Normal file
73
.CondaPkg/env/Lib/importlib/metadata/_itertools.py
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
from itertools import filterfalse
|
||||
|
||||
|
||||
def unique_everseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember all elements ever seen."
|
||||
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
|
||||
# unique_everseen('ABBCcAD', str.lower) --> A B C D
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
if key is None:
|
||||
for element in filterfalse(seen.__contains__, iterable):
|
||||
seen_add(element)
|
||||
yield element
|
||||
else:
|
||||
for element in iterable:
|
||||
k = key(element)
|
||||
if k not in seen:
|
||||
seen_add(k)
|
||||
yield element
|
||||
|
||||
|
||||
# copied from more_itertools 8.8
|
||||
def always_iterable(obj, base_type=(str, bytes)):
|
||||
"""If *obj* is iterable, return an iterator over its items::
|
||||
|
||||
>>> obj = (1, 2, 3)
|
||||
>>> list(always_iterable(obj))
|
||||
[1, 2, 3]
|
||||
|
||||
If *obj* is not iterable, return a one-item iterable containing *obj*::
|
||||
|
||||
>>> obj = 1
|
||||
>>> list(always_iterable(obj))
|
||||
[1]
|
||||
|
||||
If *obj* is ``None``, return an empty iterable:
|
||||
|
||||
>>> obj = None
|
||||
>>> list(always_iterable(None))
|
||||
[]
|
||||
|
||||
By default, binary and text strings are not considered iterable::
|
||||
|
||||
>>> obj = 'foo'
|
||||
>>> list(always_iterable(obj))
|
||||
['foo']
|
||||
|
||||
If *base_type* is set, objects for which ``isinstance(obj, base_type)``
|
||||
returns ``True`` won't be considered iterable.
|
||||
|
||||
>>> obj = {'a': 1}
|
||||
>>> list(always_iterable(obj)) # Iterate over the dict's keys
|
||||
['a']
|
||||
>>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
|
||||
[{'a': 1}]
|
||||
|
||||
Set *base_type* to ``None`` to avoid any special handling and treat objects
|
||||
Python considers iterable as iterable:
|
||||
|
||||
>>> obj = 'foo'
|
||||
>>> list(always_iterable(obj, base_type=None))
|
||||
['f', 'o', 'o']
|
||||
"""
|
||||
if obj is None:
|
||||
return iter(())
|
||||
|
||||
if (base_type is not None) and isinstance(obj, base_type):
|
||||
return iter((obj,))
|
||||
|
||||
try:
|
||||
return iter(obj)
|
||||
except TypeError:
|
||||
return iter((obj,))
|
||||
63
.CondaPkg/env/Lib/importlib/metadata/_meta.py
vendored
Normal file
63
.CondaPkg/env/Lib/importlib/metadata/_meta.py
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
from typing import Protocol
|
||||
from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
class PackageMetadata(Protocol):
|
||||
def __len__(self) -> int:
|
||||
... # pragma: no cover
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
... # pragma: no cover
|
||||
|
||||
def __getitem__(self, key: str) -> str:
|
||||
... # pragma: no cover
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
... # pragma: no cover
|
||||
|
||||
@overload
|
||||
def get(self, name: str, failobj: None = None) -> Optional[str]:
|
||||
... # pragma: no cover
|
||||
|
||||
@overload
|
||||
def get(self, name: str, failobj: _T) -> Union[str, _T]:
|
||||
... # pragma: no cover
|
||||
|
||||
# overload per python/importlib_metadata#435
|
||||
@overload
|
||||
def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]:
|
||||
... # pragma: no cover
|
||||
|
||||
@overload
|
||||
def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
|
||||
"""
|
||||
Return all values associated with a possibly multi-valued key.
|
||||
"""
|
||||
|
||||
@property
|
||||
def json(self) -> Dict[str, Union[str, List[str]]]:
|
||||
"""
|
||||
A JSON-compatible form of the metadata.
|
||||
"""
|
||||
|
||||
|
||||
class SimplePath(Protocol[_T]):
|
||||
"""
|
||||
A minimal subset of pathlib.Path required by PathDistribution.
|
||||
"""
|
||||
|
||||
def joinpath(self) -> _T:
|
||||
... # pragma: no cover
|
||||
|
||||
def __truediv__(self, other: Union[str, _T]) -> _T:
|
||||
... # pragma: no cover
|
||||
|
||||
@property
|
||||
def parent(self) -> _T:
|
||||
... # pragma: no cover
|
||||
|
||||
def read_text(self) -> str:
|
||||
... # pragma: no cover
|
||||
99
.CondaPkg/env/Lib/importlib/metadata/_text.py
vendored
Normal file
99
.CondaPkg/env/Lib/importlib/metadata/_text.py
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import re
|
||||
|
||||
from ._functools import method_cache
|
||||
|
||||
|
||||
# from jaraco.text 3.5
|
||||
class FoldedCase(str):
|
||||
"""
|
||||
A case insensitive string class; behaves just like str
|
||||
except compares equal when the only variation is case.
|
||||
|
||||
>>> s = FoldedCase('hello world')
|
||||
|
||||
>>> s == 'Hello World'
|
||||
True
|
||||
|
||||
>>> 'Hello World' == s
|
||||
True
|
||||
|
||||
>>> s != 'Hello World'
|
||||
False
|
||||
|
||||
>>> s.index('O')
|
||||
4
|
||||
|
||||
>>> s.split('O')
|
||||
['hell', ' w', 'rld']
|
||||
|
||||
>>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
|
||||
['alpha', 'Beta', 'GAMMA']
|
||||
|
||||
Sequence membership is straightforward.
|
||||
|
||||
>>> "Hello World" in [s]
|
||||
True
|
||||
>>> s in ["Hello World"]
|
||||
True
|
||||
|
||||
You may test for set inclusion, but candidate and elements
|
||||
must both be folded.
|
||||
|
||||
>>> FoldedCase("Hello World") in {s}
|
||||
True
|
||||
>>> s in {FoldedCase("Hello World")}
|
||||
True
|
||||
|
||||
String inclusion works as long as the FoldedCase object
|
||||
is on the right.
|
||||
|
||||
>>> "hello" in FoldedCase("Hello World")
|
||||
True
|
||||
|
||||
But not if the FoldedCase object is on the left:
|
||||
|
||||
>>> FoldedCase('hello') in 'Hello World'
|
||||
False
|
||||
|
||||
In that case, use in_:
|
||||
|
||||
>>> FoldedCase('hello').in_('Hello World')
|
||||
True
|
||||
|
||||
>>> FoldedCase('hello') > FoldedCase('Hello')
|
||||
False
|
||||
"""
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.lower() < other.lower()
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.lower() > other.lower()
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.lower() == other.lower()
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.lower() != other.lower()
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.lower())
|
||||
|
||||
def __contains__(self, other):
|
||||
return super().lower().__contains__(other.lower())
|
||||
|
||||
def in_(self, other):
|
||||
"Does self appear in other?"
|
||||
return self in FoldedCase(other)
|
||||
|
||||
# cache lower since it's likely to be called frequently.
|
||||
@method_cache
|
||||
def lower(self):
|
||||
return super().lower()
|
||||
|
||||
def index(self, sub):
|
||||
return self.lower().index(sub.lower())
|
||||
|
||||
def split(self, splitter=' ', maxsplit=0):
|
||||
pattern = re.compile(re.escape(splitter), re.I)
|
||||
return pattern.split(self, maxsplit)
|
||||
12
.CondaPkg/env/Lib/importlib/readers.py
vendored
Normal file
12
.CondaPkg/env/Lib/importlib/readers.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Compatibility shim for .resources.readers as found on Python 3.10.
|
||||
|
||||
Consumers that can rely on Python 3.11 should use the other
|
||||
module directly.
|
||||
"""
|
||||
|
||||
from .resources.readers import (
|
||||
FileReader, ZipReader, MultiplexedPath, NamespaceReader,
|
||||
)
|
||||
|
||||
__all__ = ['FileReader', 'ZipReader', 'MultiplexedPath', 'NamespaceReader']
|
||||
36
.CondaPkg/env/Lib/importlib/resources/__init__.py
vendored
Normal file
36
.CondaPkg/env/Lib/importlib/resources/__init__.py
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Read resources contained within a package."""
|
||||
|
||||
from ._common import (
|
||||
as_file,
|
||||
files,
|
||||
Package,
|
||||
)
|
||||
|
||||
from ._legacy import (
|
||||
contents,
|
||||
open_binary,
|
||||
read_binary,
|
||||
open_text,
|
||||
read_text,
|
||||
is_resource,
|
||||
path,
|
||||
Resource,
|
||||
)
|
||||
|
||||
from .abc import ResourceReader
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Package',
|
||||
'Resource',
|
||||
'ResourceReader',
|
||||
'as_file',
|
||||
'contents',
|
||||
'files',
|
||||
'is_resource',
|
||||
'open_binary',
|
||||
'open_text',
|
||||
'path',
|
||||
'read_binary',
|
||||
'read_text',
|
||||
]
|
||||
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_adapters.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_adapters.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_common.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_common.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_itertools.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_itertools.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_legacy.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/_legacy.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/abc.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/abc.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/readers.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/readers.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/simple.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/importlib/resources/__pycache__/simple.cpython-312.pyc
vendored
Normal file
Binary file not shown.
168
.CondaPkg/env/Lib/importlib/resources/_adapters.py
vendored
Normal file
168
.CondaPkg/env/Lib/importlib/resources/_adapters.py
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
from contextlib import suppress
|
||||
from io import TextIOWrapper
|
||||
|
||||
from . import abc
|
||||
|
||||
|
||||
class SpecLoaderAdapter:
|
||||
"""
|
||||
Adapt a package spec to adapt the underlying loader.
|
||||
"""
|
||||
|
||||
def __init__(self, spec, adapter=lambda spec: spec.loader):
|
||||
self.spec = spec
|
||||
self.loader = adapter(spec)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.spec, name)
|
||||
|
||||
|
||||
class TraversableResourcesLoader:
|
||||
"""
|
||||
Adapt a loader to provide TraversableResources.
|
||||
"""
|
||||
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
def get_resource_reader(self, name):
|
||||
return CompatibilityFiles(self.spec)._native()
|
||||
|
||||
|
||||
def _io_wrapper(file, mode='r', *args, **kwargs):
|
||||
if mode == 'r':
|
||||
return TextIOWrapper(file, *args, **kwargs)
|
||||
elif mode == 'rb':
|
||||
return file
|
||||
raise ValueError(f"Invalid mode value '{mode}', only 'r' and 'rb' are supported")
|
||||
|
||||
|
||||
class CompatibilityFiles:
|
||||
"""
|
||||
Adapter for an existing or non-existent resource reader
|
||||
to provide a compatibility .files().
|
||||
"""
|
||||
|
||||
class SpecPath(abc.Traversable):
|
||||
"""
|
||||
Path tied to a module spec.
|
||||
Can be read and exposes the resource reader children.
|
||||
"""
|
||||
|
||||
def __init__(self, spec, reader):
|
||||
self._spec = spec
|
||||
self._reader = reader
|
||||
|
||||
def iterdir(self):
|
||||
if not self._reader:
|
||||
return iter(())
|
||||
return iter(
|
||||
CompatibilityFiles.ChildPath(self._reader, path)
|
||||
for path in self._reader.contents()
|
||||
)
|
||||
|
||||
def is_file(self):
|
||||
return False
|
||||
|
||||
is_dir = is_file
|
||||
|
||||
def joinpath(self, other):
|
||||
if not self._reader:
|
||||
return CompatibilityFiles.OrphanPath(other)
|
||||
return CompatibilityFiles.ChildPath(self._reader, other)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._spec.name
|
||||
|
||||
def open(self, mode='r', *args, **kwargs):
|
||||
return _io_wrapper(self._reader.open_resource(None), mode, *args, **kwargs)
|
||||
|
||||
class ChildPath(abc.Traversable):
|
||||
"""
|
||||
Path tied to a resource reader child.
|
||||
Can be read but doesn't expose any meaningful children.
|
||||
"""
|
||||
|
||||
def __init__(self, reader, name):
|
||||
self._reader = reader
|
||||
self._name = name
|
||||
|
||||
def iterdir(self):
|
||||
return iter(())
|
||||
|
||||
def is_file(self):
|
||||
return self._reader.is_resource(self.name)
|
||||
|
||||
def is_dir(self):
|
||||
return not self.is_file()
|
||||
|
||||
def joinpath(self, other):
|
||||
return CompatibilityFiles.OrphanPath(self.name, other)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def open(self, mode='r', *args, **kwargs):
|
||||
return _io_wrapper(
|
||||
self._reader.open_resource(self.name), mode, *args, **kwargs
|
||||
)
|
||||
|
||||
class OrphanPath(abc.Traversable):
|
||||
"""
|
||||
Orphan path, not tied to a module spec or resource reader.
|
||||
Can't be read and doesn't expose any meaningful children.
|
||||
"""
|
||||
|
||||
def __init__(self, *path_parts):
|
||||
if len(path_parts) < 1:
|
||||
raise ValueError('Need at least one path part to construct a path')
|
||||
self._path = path_parts
|
||||
|
||||
def iterdir(self):
|
||||
return iter(())
|
||||
|
||||
def is_file(self):
|
||||
return False
|
||||
|
||||
is_dir = is_file
|
||||
|
||||
def joinpath(self, other):
|
||||
return CompatibilityFiles.OrphanPath(*self._path, other)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._path[-1]
|
||||
|
||||
def open(self, mode='r', *args, **kwargs):
|
||||
raise FileNotFoundError("Can't open orphan path")
|
||||
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
@property
|
||||
def _reader(self):
|
||||
with suppress(AttributeError):
|
||||
return self.spec.loader.get_resource_reader(self.spec.name)
|
||||
|
||||
def _native(self):
|
||||
"""
|
||||
Return the native reader if it supports files().
|
||||
"""
|
||||
reader = self._reader
|
||||
return reader if hasattr(reader, 'files') else self
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self._reader, attr)
|
||||
|
||||
def files(self):
|
||||
return CompatibilityFiles.SpecPath(self.spec, self._reader)
|
||||
|
||||
|
||||
def wrap_spec(package):
|
||||
"""
|
||||
Construct a package spec with traversable compatibility
|
||||
on the spec/loader/reader.
|
||||
"""
|
||||
return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)
|
||||
207
.CondaPkg/env/Lib/importlib/resources/_common.py
vendored
Normal file
207
.CondaPkg/env/Lib/importlib/resources/_common.py
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
import os
|
||||
import pathlib
|
||||
import tempfile
|
||||
import functools
|
||||
import contextlib
|
||||
import types
|
||||
import importlib
|
||||
import inspect
|
||||
import warnings
|
||||
import itertools
|
||||
|
||||
from typing import Union, Optional, cast
|
||||
from .abc import ResourceReader, Traversable
|
||||
|
||||
from ._adapters import wrap_spec
|
||||
|
||||
Package = Union[types.ModuleType, str]
|
||||
Anchor = Package
|
||||
|
||||
|
||||
def package_to_anchor(func):
|
||||
"""
|
||||
Replace 'package' parameter as 'anchor' and warn about the change.
|
||||
|
||||
Other errors should fall through.
|
||||
|
||||
>>> files('a', 'b')
|
||||
Traceback (most recent call last):
|
||||
TypeError: files() takes from 0 to 1 positional arguments but 2 were given
|
||||
"""
|
||||
undefined = object()
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(anchor=undefined, package=undefined):
|
||||
if package is not undefined:
|
||||
if anchor is not undefined:
|
||||
return func(anchor, package)
|
||||
warnings.warn(
|
||||
"First parameter to files is renamed to 'anchor'",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return func(package)
|
||||
elif anchor is undefined:
|
||||
return func()
|
||||
return func(anchor)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@package_to_anchor
|
||||
def files(anchor: Optional[Anchor] = None) -> Traversable:
|
||||
"""
|
||||
Get a Traversable resource for an anchor.
|
||||
"""
|
||||
return from_package(resolve(anchor))
|
||||
|
||||
|
||||
def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]:
|
||||
"""
|
||||
Return the package's loader if it's a ResourceReader.
|
||||
"""
|
||||
# We can't use
|
||||
# a issubclass() check here because apparently abc.'s __subclasscheck__()
|
||||
# hook wants to create a weak reference to the object, but
|
||||
# zipimport.zipimporter does not support weak references, resulting in a
|
||||
# TypeError. That seems terrible.
|
||||
spec = package.__spec__
|
||||
reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore
|
||||
if reader is None:
|
||||
return None
|
||||
return reader(spec.name) # type: ignore
|
||||
|
||||
|
||||
@functools.singledispatch
|
||||
def resolve(cand: Optional[Anchor]) -> types.ModuleType:
|
||||
return cast(types.ModuleType, cand)
|
||||
|
||||
|
||||
@resolve.register
|
||||
def _(cand: str) -> types.ModuleType:
|
||||
return importlib.import_module(cand)
|
||||
|
||||
|
||||
@resolve.register
|
||||
def _(cand: None) -> types.ModuleType:
|
||||
return resolve(_infer_caller().f_globals['__name__'])
|
||||
|
||||
|
||||
def _infer_caller():
|
||||
"""
|
||||
Walk the stack and find the frame of the first caller not in this module.
|
||||
"""
|
||||
|
||||
def is_this_file(frame_info):
|
||||
return frame_info.filename == __file__
|
||||
|
||||
def is_wrapper(frame_info):
|
||||
return frame_info.function == 'wrapper'
|
||||
|
||||
not_this_file = itertools.filterfalse(is_this_file, inspect.stack())
|
||||
# also exclude 'wrapper' due to singledispatch in the call stack
|
||||
callers = itertools.filterfalse(is_wrapper, not_this_file)
|
||||
return next(callers).frame
|
||||
|
||||
|
||||
def from_package(package: types.ModuleType):
|
||||
"""
|
||||
Return a Traversable object for the given package.
|
||||
|
||||
"""
|
||||
spec = wrap_spec(package)
|
||||
reader = spec.loader.get_resource_reader(spec.name)
|
||||
return reader.files()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _tempfile(
|
||||
reader,
|
||||
suffix='',
|
||||
# gh-93353: Keep a reference to call os.remove() in late Python
|
||||
# finalization.
|
||||
*,
|
||||
_os_remove=os.remove,
|
||||
):
|
||||
# Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
|
||||
# blocks due to the need to close the temporary file to work on Windows
|
||||
# properly.
|
||||
fd, raw_path = tempfile.mkstemp(suffix=suffix)
|
||||
try:
|
||||
try:
|
||||
os.write(fd, reader())
|
||||
finally:
|
||||
os.close(fd)
|
||||
del reader
|
||||
yield pathlib.Path(raw_path)
|
||||
finally:
|
||||
try:
|
||||
_os_remove(raw_path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def _temp_file(path):
|
||||
return _tempfile(path.read_bytes, suffix=path.name)
|
||||
|
||||
|
||||
def _is_present_dir(path: Traversable) -> bool:
|
||||
"""
|
||||
Some Traversables implement ``is_dir()`` to raise an
|
||||
exception (i.e. ``FileNotFoundError``) when the
|
||||
directory doesn't exist. This function wraps that call
|
||||
to always return a boolean and only return True
|
||||
if there's a dir and it exists.
|
||||
"""
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
return path.is_dir()
|
||||
return False
|
||||
|
||||
|
||||
@functools.singledispatch
|
||||
def as_file(path):
|
||||
"""
|
||||
Given a Traversable object, return that object as a
|
||||
path on the local file system in a context manager.
|
||||
"""
|
||||
return _temp_dir(path) if _is_present_dir(path) else _temp_file(path)
|
||||
|
||||
|
||||
@as_file.register(pathlib.Path)
|
||||
@contextlib.contextmanager
|
||||
def _(path):
|
||||
"""
|
||||
Degenerate behavior for pathlib.Path objects.
|
||||
"""
|
||||
yield path
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _temp_path(dir: tempfile.TemporaryDirectory):
|
||||
"""
|
||||
Wrap tempfile.TemporyDirectory to return a pathlib object.
|
||||
"""
|
||||
with dir as result:
|
||||
yield pathlib.Path(result)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _temp_dir(path):
|
||||
"""
|
||||
Given a traversable dir, recursively replicate the whole tree
|
||||
to the file system in a context manager.
|
||||
"""
|
||||
assert path.is_dir()
|
||||
with _temp_path(tempfile.TemporaryDirectory()) as temp_dir:
|
||||
yield _write_contents(temp_dir, path)
|
||||
|
||||
|
||||
def _write_contents(target, source):
|
||||
child = target.joinpath(source.name)
|
||||
if source.is_dir():
|
||||
child.mkdir()
|
||||
for item in source.iterdir():
|
||||
_write_contents(child, item)
|
||||
else:
|
||||
child.write_bytes(source.read_bytes())
|
||||
return child
|
||||
38
.CondaPkg/env/Lib/importlib/resources/_itertools.py
vendored
Normal file
38
.CondaPkg/env/Lib/importlib/resources/_itertools.py
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# from more_itertools 9.0
|
||||
def only(iterable, default=None, too_long=None):
|
||||
"""If *iterable* has only one item, return it.
|
||||
If it has zero items, return *default*.
|
||||
If it has more than one item, raise the exception given by *too_long*,
|
||||
which is ``ValueError`` by default.
|
||||
>>> only([], default='missing')
|
||||
'missing'
|
||||
>>> only([1])
|
||||
1
|
||||
>>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Expected exactly one item in iterable, but got 1, 2,
|
||||
and perhaps more.'
|
||||
>>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError
|
||||
Note that :func:`only` attempts to advance *iterable* twice to ensure there
|
||||
is only one item. See :func:`spy` or :func:`peekable` to check
|
||||
iterable contents less destructively.
|
||||
"""
|
||||
it = iter(iterable)
|
||||
first_value = next(it, default)
|
||||
|
||||
try:
|
||||
second_value = next(it)
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
msg = (
|
||||
'Expected exactly one item in iterable, but got {!r}, {!r}, '
|
||||
'and perhaps more.'.format(first_value, second_value)
|
||||
)
|
||||
raise too_long or ValueError(msg)
|
||||
|
||||
return first_value
|
||||
120
.CondaPkg/env/Lib/importlib/resources/_legacy.py
vendored
Normal file
120
.CondaPkg/env/Lib/importlib/resources/_legacy.py
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import functools
|
||||
import os
|
||||
import pathlib
|
||||
import types
|
||||
import warnings
|
||||
|
||||
from typing import Union, Iterable, ContextManager, BinaryIO, TextIO, Any
|
||||
|
||||
from . import _common
|
||||
|
||||
Package = Union[types.ModuleType, str]
|
||||
Resource = str
|
||||
|
||||
|
||||
def deprecated(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.warn(
|
||||
f"{func.__name__} is deprecated. Use files() instead. "
|
||||
"Refer to https://importlib-resources.readthedocs.io"
|
||||
"/en/latest/using.html#migrating-from-legacy for migration advice.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def normalize_path(path: Any) -> str:
|
||||
"""Normalize a path by ensuring it is a string.
|
||||
|
||||
If the resulting string contains path separators, an exception is raised.
|
||||
"""
|
||||
str_path = str(path)
|
||||
parent, file_name = os.path.split(str_path)
|
||||
if parent:
|
||||
raise ValueError(f'{path!r} must be only a file name')
|
||||
return file_name
|
||||
|
||||
|
||||
@deprecated
|
||||
def open_binary(package: Package, resource: Resource) -> BinaryIO:
|
||||
"""Return a file-like object opened for binary reading of the resource."""
|
||||
return (_common.files(package) / normalize_path(resource)).open('rb')
|
||||
|
||||
|
||||
@deprecated
|
||||
def read_binary(package: Package, resource: Resource) -> bytes:
|
||||
"""Return the binary contents of the resource."""
|
||||
return (_common.files(package) / normalize_path(resource)).read_bytes()
|
||||
|
||||
|
||||
@deprecated
|
||||
def open_text(
|
||||
package: Package,
|
||||
resource: Resource,
|
||||
encoding: str = 'utf-8',
|
||||
errors: str = 'strict',
|
||||
) -> TextIO:
|
||||
"""Return a file-like object opened for text reading of the resource."""
|
||||
return (_common.files(package) / normalize_path(resource)).open(
|
||||
'r', encoding=encoding, errors=errors
|
||||
)
|
||||
|
||||
|
||||
@deprecated
|
||||
def read_text(
|
||||
package: Package,
|
||||
resource: Resource,
|
||||
encoding: str = 'utf-8',
|
||||
errors: str = 'strict',
|
||||
) -> str:
|
||||
"""Return the decoded string of the resource.
|
||||
|
||||
The decoding-related arguments have the same semantics as those of
|
||||
bytes.decode().
|
||||
"""
|
||||
with open_text(package, resource, encoding, errors) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
@deprecated
|
||||
def contents(package: Package) -> Iterable[str]:
|
||||
"""Return an iterable of entries in `package`.
|
||||
|
||||
Note that not all entries are resources. Specifically, directories are
|
||||
not considered resources. Use `is_resource()` on each entry returned here
|
||||
to check if it is a resource or not.
|
||||
"""
|
||||
return [path.name for path in _common.files(package).iterdir()]
|
||||
|
||||
|
||||
@deprecated
|
||||
def is_resource(package: Package, name: str) -> bool:
|
||||
"""True if `name` is a resource inside `package`.
|
||||
|
||||
Directories are *not* resources.
|
||||
"""
|
||||
resource = normalize_path(name)
|
||||
return any(
|
||||
traversable.name == resource and traversable.is_file()
|
||||
for traversable in _common.files(package).iterdir()
|
||||
)
|
||||
|
||||
|
||||
@deprecated
|
||||
def path(
|
||||
package: Package,
|
||||
resource: Resource,
|
||||
) -> ContextManager[pathlib.Path]:
|
||||
"""A context manager providing a file path object to the resource.
|
||||
|
||||
If the resource does not already exist on its own on the file system,
|
||||
a temporary file will be created. If the file was created, the file
|
||||
will be deleted upon exiting the context manager (no exception is
|
||||
raised if the file was deleted prior to the context manager
|
||||
exiting).
|
||||
"""
|
||||
return _common.as_file(_common.files(package) / normalize_path(resource))
|
||||
173
.CondaPkg/env/Lib/importlib/resources/abc.py
vendored
Normal file
173
.CondaPkg/env/Lib/importlib/resources/abc.py
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
import abc
|
||||
import io
|
||||
import itertools
|
||||
import os
|
||||
import pathlib
|
||||
from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional
|
||||
from typing import runtime_checkable, Protocol
|
||||
from typing import Union
|
||||
|
||||
|
||||
StrPath = Union[str, os.PathLike[str]]
|
||||
|
||||
__all__ = ["ResourceReader", "Traversable", "TraversableResources"]
|
||||
|
||||
|
||||
class ResourceReader(metaclass=abc.ABCMeta):
|
||||
"""Abstract base class for loaders to provide resource reading support."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def open_resource(self, resource: Text) -> BinaryIO:
|
||||
"""Return an opened, file-like object for binary reading.
|
||||
|
||||
The 'resource' argument is expected to represent only a file name.
|
||||
If the resource cannot be found, FileNotFoundError is raised.
|
||||
"""
|
||||
# This deliberately raises FileNotFoundError instead of
|
||||
# NotImplementedError so that if this method is accidentally called,
|
||||
# it'll still do the right thing.
|
||||
raise FileNotFoundError
|
||||
|
||||
@abc.abstractmethod
|
||||
def resource_path(self, resource: Text) -> Text:
|
||||
"""Return the file system path to the specified resource.
|
||||
|
||||
The 'resource' argument is expected to represent only a file name.
|
||||
If the resource does not exist on the file system, raise
|
||||
FileNotFoundError.
|
||||
"""
|
||||
# This deliberately raises FileNotFoundError instead of
|
||||
# NotImplementedError so that if this method is accidentally called,
|
||||
# it'll still do the right thing.
|
||||
raise FileNotFoundError
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_resource(self, path: Text) -> bool:
|
||||
"""Return True if the named 'path' is a resource.
|
||||
|
||||
Files are resources, directories are not.
|
||||
"""
|
||||
raise FileNotFoundError
|
||||
|
||||
@abc.abstractmethod
|
||||
def contents(self) -> Iterable[str]:
|
||||
"""Return an iterable of entries in `package`."""
|
||||
raise FileNotFoundError
|
||||
|
||||
|
||||
class TraversalError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Traversable(Protocol):
|
||||
"""
|
||||
An object with a subset of pathlib.Path methods suitable for
|
||||
traversing directories and opening files.
|
||||
|
||||
Any exceptions that occur when accessing the backing resource
|
||||
may propagate unaltered.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def iterdir(self) -> Iterator["Traversable"]:
|
||||
"""
|
||||
Yield Traversable objects in self
|
||||
"""
|
||||
|
||||
def read_bytes(self) -> bytes:
|
||||
"""
|
||||
Read contents of self as bytes
|
||||
"""
|
||||
with self.open('rb') as strm:
|
||||
return strm.read()
|
||||
|
||||
def read_text(self, encoding: Optional[str] = None) -> str:
|
||||
"""
|
||||
Read contents of self as text
|
||||
"""
|
||||
with self.open(encoding=encoding) as strm:
|
||||
return strm.read()
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_dir(self) -> bool:
|
||||
"""
|
||||
Return True if self is a directory
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_file(self) -> bool:
|
||||
"""
|
||||
Return True if self is a file
|
||||
"""
|
||||
|
||||
def joinpath(self, *descendants: StrPath) -> "Traversable":
|
||||
"""
|
||||
Return Traversable resolved with any descendants applied.
|
||||
|
||||
Each descendant should be a path segment relative to self
|
||||
and each may contain multiple levels separated by
|
||||
``posixpath.sep`` (``/``).
|
||||
"""
|
||||
if not descendants:
|
||||
return self
|
||||
names = itertools.chain.from_iterable(
|
||||
path.parts for path in map(pathlib.PurePosixPath, descendants)
|
||||
)
|
||||
target = next(names)
|
||||
matches = (
|
||||
traversable for traversable in self.iterdir() if traversable.name == target
|
||||
)
|
||||
try:
|
||||
match = next(matches)
|
||||
except StopIteration:
|
||||
raise TraversalError(
|
||||
"Target not found during traversal.", target, list(names)
|
||||
)
|
||||
return match.joinpath(*names)
|
||||
|
||||
def __truediv__(self, child: StrPath) -> "Traversable":
|
||||
"""
|
||||
Return Traversable child in self
|
||||
"""
|
||||
return self.joinpath(child)
|
||||
|
||||
@abc.abstractmethod
|
||||
def open(self, mode='r', *args, **kwargs):
|
||||
"""
|
||||
mode may be 'r' or 'rb' to open as text or binary. Return a handle
|
||||
suitable for reading (same as pathlib.Path.open).
|
||||
|
||||
When opening as text, accepts encoding parameters such as those
|
||||
accepted by io.TextIOWrapper.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def name(self) -> str:
|
||||
"""
|
||||
The base name of this object without any parent references.
|
||||
"""
|
||||
|
||||
|
||||
class TraversableResources(ResourceReader):
|
||||
"""
|
||||
The required interface for providing traversable
|
||||
resources.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def files(self) -> "Traversable":
|
||||
"""Return a Traversable object for the loaded package."""
|
||||
|
||||
def open_resource(self, resource: StrPath) -> io.BufferedReader:
|
||||
return self.files().joinpath(resource).open('rb')
|
||||
|
||||
def resource_path(self, resource: Any) -> NoReturn:
|
||||
raise FileNotFoundError(resource)
|
||||
|
||||
def is_resource(self, path: StrPath) -> bool:
|
||||
return self.files().joinpath(path).is_file()
|
||||
|
||||
def contents(self) -> Iterator[str]:
|
||||
return (item.name for item in self.files().iterdir())
|
||||
144
.CondaPkg/env/Lib/importlib/resources/readers.py
vendored
Normal file
144
.CondaPkg/env/Lib/importlib/resources/readers.py
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
import collections
|
||||
import itertools
|
||||
import pathlib
|
||||
import operator
|
||||
import zipfile
|
||||
|
||||
from . import abc
|
||||
|
||||
from ._itertools import only
|
||||
|
||||
|
||||
def remove_duplicates(items):
|
||||
return iter(collections.OrderedDict.fromkeys(items))
|
||||
|
||||
|
||||
class FileReader(abc.TraversableResources):
|
||||
def __init__(self, loader):
|
||||
self.path = pathlib.Path(loader.path).parent
|
||||
|
||||
def resource_path(self, resource):
|
||||
"""
|
||||
Return the file system path to prevent
|
||||
`resources.path()` from creating a temporary
|
||||
copy.
|
||||
"""
|
||||
return str(self.path.joinpath(resource))
|
||||
|
||||
def files(self):
|
||||
return self.path
|
||||
|
||||
|
||||
class ZipReader(abc.TraversableResources):
|
||||
def __init__(self, loader, module):
|
||||
_, _, name = module.rpartition('.')
|
||||
self.prefix = loader.prefix.replace('\\', '/') + name + '/'
|
||||
self.archive = loader.archive
|
||||
|
||||
def open_resource(self, resource):
|
||||
try:
|
||||
return super().open_resource(resource)
|
||||
except KeyError as exc:
|
||||
raise FileNotFoundError(exc.args[0])
|
||||
|
||||
def is_resource(self, path):
|
||||
"""
|
||||
Workaround for `zipfile.Path.is_file` returning true
|
||||
for non-existent paths.
|
||||
"""
|
||||
target = self.files().joinpath(path)
|
||||
return target.is_file() and target.exists()
|
||||
|
||||
def files(self):
|
||||
return zipfile.Path(self.archive, self.prefix)
|
||||
|
||||
|
||||
class MultiplexedPath(abc.Traversable):
|
||||
"""
|
||||
Given a series of Traversable objects, implement a merged
|
||||
version of the interface across all objects. Useful for
|
||||
namespace packages which may be multihomed at a single
|
||||
name.
|
||||
"""
|
||||
|
||||
def __init__(self, *paths):
|
||||
self._paths = list(map(pathlib.Path, remove_duplicates(paths)))
|
||||
if not self._paths:
|
||||
message = 'MultiplexedPath must contain at least one path'
|
||||
raise FileNotFoundError(message)
|
||||
if not all(path.is_dir() for path in self._paths):
|
||||
raise NotADirectoryError('MultiplexedPath only supports directories')
|
||||
|
||||
def iterdir(self):
|
||||
children = (child for path in self._paths for child in path.iterdir())
|
||||
by_name = operator.attrgetter('name')
|
||||
groups = itertools.groupby(sorted(children, key=by_name), key=by_name)
|
||||
return map(self._follow, (locs for name, locs in groups))
|
||||
|
||||
def read_bytes(self):
|
||||
raise FileNotFoundError(f'{self} is not a file')
|
||||
|
||||
def read_text(self, *args, **kwargs):
|
||||
raise FileNotFoundError(f'{self} is not a file')
|
||||
|
||||
def is_dir(self):
|
||||
return True
|
||||
|
||||
def is_file(self):
|
||||
return False
|
||||
|
||||
def joinpath(self, *descendants):
|
||||
try:
|
||||
return super().joinpath(*descendants)
|
||||
except abc.TraversalError:
|
||||
# One of the paths did not resolve (a directory does not exist).
|
||||
# Just return something that will not exist.
|
||||
return self._paths[0].joinpath(*descendants)
|
||||
|
||||
@classmethod
|
||||
def _follow(cls, children):
|
||||
"""
|
||||
Construct a MultiplexedPath if needed.
|
||||
|
||||
If children contains a sole element, return it.
|
||||
Otherwise, return a MultiplexedPath of the items.
|
||||
Unless one of the items is not a Directory, then return the first.
|
||||
"""
|
||||
subdirs, one_dir, one_file = itertools.tee(children, 3)
|
||||
|
||||
try:
|
||||
return only(one_dir)
|
||||
except ValueError:
|
||||
try:
|
||||
return cls(*subdirs)
|
||||
except NotADirectoryError:
|
||||
return next(one_file)
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
raise FileNotFoundError(f'{self} is not a file')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._paths[0].name
|
||||
|
||||
def __repr__(self):
|
||||
paths = ', '.join(f"'{path}'" for path in self._paths)
|
||||
return f'MultiplexedPath({paths})'
|
||||
|
||||
|
||||
class NamespaceReader(abc.TraversableResources):
|
||||
def __init__(self, namespace_path):
|
||||
if 'NamespacePath' not in str(namespace_path):
|
||||
raise ValueError('Invalid path')
|
||||
self.path = MultiplexedPath(*list(namespace_path))
|
||||
|
||||
def resource_path(self, resource):
|
||||
"""
|
||||
Return the file system path to prevent
|
||||
`resources.path()` from creating a temporary
|
||||
copy.
|
||||
"""
|
||||
return str(self.path.joinpath(resource))
|
||||
|
||||
def files(self):
|
||||
return self.path
|
||||
106
.CondaPkg/env/Lib/importlib/resources/simple.py
vendored
Normal file
106
.CondaPkg/env/Lib/importlib/resources/simple.py
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Interface adapters for low-level readers.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import io
|
||||
import itertools
|
||||
from typing import BinaryIO, List
|
||||
|
||||
from .abc import Traversable, TraversableResources
|
||||
|
||||
|
||||
class SimpleReader(abc.ABC):
|
||||
"""
|
||||
The minimum, low-level interface required from a resource
|
||||
provider.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def package(self) -> str:
|
||||
"""
|
||||
The name of the package for which this reader loads resources.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def children(self) -> List['SimpleReader']:
|
||||
"""
|
||||
Obtain an iterable of SimpleReader for available
|
||||
child containers (e.g. directories).
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def resources(self) -> List[str]:
|
||||
"""
|
||||
Obtain available named resources for this virtual package.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def open_binary(self, resource: str) -> BinaryIO:
|
||||
"""
|
||||
Obtain a File-like for a named resource.
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.package.split('.')[-1]
|
||||
|
||||
|
||||
class ResourceContainer(Traversable):
|
||||
"""
|
||||
Traversable container for a package's resources via its reader.
|
||||
"""
|
||||
|
||||
def __init__(self, reader: SimpleReader):
|
||||
self.reader = reader
|
||||
|
||||
def is_dir(self):
|
||||
return True
|
||||
|
||||
def is_file(self):
|
||||
return False
|
||||
|
||||
def iterdir(self):
|
||||
files = (ResourceHandle(self, name) for name in self.reader.resources)
|
||||
dirs = map(ResourceContainer, self.reader.children())
|
||||
return itertools.chain(files, dirs)
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
raise IsADirectoryError()
|
||||
|
||||
|
||||
class ResourceHandle(Traversable):
|
||||
"""
|
||||
Handle to a named resource in a ResourceReader.
|
||||
"""
|
||||
|
||||
def __init__(self, parent: ResourceContainer, name: str):
|
||||
self.parent = parent
|
||||
self.name = name # type: ignore
|
||||
|
||||
def is_file(self):
|
||||
return True
|
||||
|
||||
def is_dir(self):
|
||||
return False
|
||||
|
||||
def open(self, mode='r', *args, **kwargs):
|
||||
stream = self.parent.reader.open_binary(self.name)
|
||||
if 'b' not in mode:
|
||||
stream = io.TextIOWrapper(*args, **kwargs)
|
||||
return stream
|
||||
|
||||
def joinpath(self, name):
|
||||
raise RuntimeError("Cannot traverse into a resource")
|
||||
|
||||
|
||||
class TraversableReader(TraversableResources, SimpleReader):
|
||||
"""
|
||||
A TraversableResources based on SimpleReader. Resource providers
|
||||
may derive from this class to provide the TraversableResources
|
||||
interface by supplying the SimpleReader interface.
|
||||
"""
|
||||
|
||||
def files(self):
|
||||
return ResourceContainer(self)
|
||||
14
.CondaPkg/env/Lib/importlib/simple.py
vendored
Normal file
14
.CondaPkg/env/Lib/importlib/simple.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
Compatibility shim for .resources.simple as found on Python 3.10.
|
||||
|
||||
Consumers that can rely on Python 3.11 should use the other
|
||||
module directly.
|
||||
"""
|
||||
|
||||
from .resources.simple import (
|
||||
SimpleReader, ResourceHandle, ResourceContainer, TraversableReader,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'SimpleReader', 'ResourceHandle', 'ResourceContainer', 'TraversableReader',
|
||||
]
|
||||
248
.CondaPkg/env/Lib/importlib/util.py
vendored
Normal file
248
.CondaPkg/env/Lib/importlib/util.py
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
"""Utility code for constructing importers, etc."""
|
||||
from ._abc import Loader
|
||||
from ._bootstrap import module_from_spec
|
||||
from ._bootstrap import _resolve_name
|
||||
from ._bootstrap import spec_from_loader
|
||||
from ._bootstrap import _find_spec
|
||||
from ._bootstrap_external import MAGIC_NUMBER
|
||||
from ._bootstrap_external import _RAW_MAGIC_NUMBER
|
||||
from ._bootstrap_external import cache_from_source
|
||||
from ._bootstrap_external import decode_source
|
||||
from ._bootstrap_external import source_from_cache
|
||||
from ._bootstrap_external import spec_from_file_location
|
||||
|
||||
import _imp
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
def source_hash(source_bytes):
|
||||
"Return the hash of *source_bytes* as used in hash-based pyc files."
|
||||
return _imp.source_hash(_RAW_MAGIC_NUMBER, source_bytes)
|
||||
|
||||
|
||||
def resolve_name(name, package):
|
||||
"""Resolve a relative module name to an absolute one."""
|
||||
if not name.startswith('.'):
|
||||
return name
|
||||
elif not package:
|
||||
raise ImportError(f'no package specified for {repr(name)} '
|
||||
'(required for relative module names)')
|
||||
level = 0
|
||||
for character in name:
|
||||
if character != '.':
|
||||
break
|
||||
level += 1
|
||||
return _resolve_name(name[level:], package, level)
|
||||
|
||||
|
||||
def _find_spec_from_path(name, path=None):
|
||||
"""Return the spec for the specified module.
|
||||
|
||||
First, sys.modules is checked to see if the module was already imported. If
|
||||
so, then sys.modules[name].__spec__ is returned. If that happens to be
|
||||
set to None, then ValueError is raised. If the module is not in
|
||||
sys.modules, then sys.meta_path is searched for a suitable spec with the
|
||||
value of 'path' given to the finders. None is returned if no spec could
|
||||
be found.
|
||||
|
||||
Dotted names do not have their parent packages implicitly imported. You will
|
||||
most likely need to explicitly import all parent packages in the proper
|
||||
order for a submodule to get the correct spec.
|
||||
|
||||
"""
|
||||
if name not in sys.modules:
|
||||
return _find_spec(name, path)
|
||||
else:
|
||||
module = sys.modules[name]
|
||||
if module is None:
|
||||
return None
|
||||
try:
|
||||
spec = module.__spec__
|
||||
except AttributeError:
|
||||
raise ValueError(f'{name}.__spec__ is not set') from None
|
||||
else:
|
||||
if spec is None:
|
||||
raise ValueError(f'{name}.__spec__ is None')
|
||||
return spec
|
||||
|
||||
|
||||
def find_spec(name, package=None):
|
||||
"""Return the spec for the specified module.
|
||||
|
||||
First, sys.modules is checked to see if the module was already imported. If
|
||||
so, then sys.modules[name].__spec__ is returned. If that happens to be
|
||||
set to None, then ValueError is raised. If the module is not in
|
||||
sys.modules, then sys.meta_path is searched for a suitable spec with the
|
||||
value of 'path' given to the finders. None is returned if no spec could
|
||||
be found.
|
||||
|
||||
If the name is for submodule (contains a dot), the parent module is
|
||||
automatically imported.
|
||||
|
||||
The name and package arguments work the same as importlib.import_module().
|
||||
In other words, relative module names (with leading dots) work.
|
||||
|
||||
"""
|
||||
fullname = resolve_name(name, package) if name.startswith('.') else name
|
||||
if fullname not in sys.modules:
|
||||
parent_name = fullname.rpartition('.')[0]
|
||||
if parent_name:
|
||||
parent = __import__(parent_name, fromlist=['__path__'])
|
||||
try:
|
||||
parent_path = parent.__path__
|
||||
except AttributeError as e:
|
||||
raise ModuleNotFoundError(
|
||||
f"__path__ attribute not found on {parent_name!r} "
|
||||
f"while trying to find {fullname!r}", name=fullname) from e
|
||||
else:
|
||||
parent_path = None
|
||||
return _find_spec(fullname, parent_path)
|
||||
else:
|
||||
module = sys.modules[fullname]
|
||||
if module is None:
|
||||
return None
|
||||
try:
|
||||
spec = module.__spec__
|
||||
except AttributeError:
|
||||
raise ValueError(f'{name}.__spec__ is not set') from None
|
||||
else:
|
||||
if spec is None:
|
||||
raise ValueError(f'{name}.__spec__ is None')
|
||||
return spec
|
||||
|
||||
|
||||
# Normally we would use contextlib.contextmanager. However, this module
|
||||
# is imported by runpy, which means we want to avoid any unnecessary
|
||||
# dependencies. Thus we use a class.
|
||||
|
||||
class _incompatible_extension_module_restrictions:
|
||||
"""A context manager that can temporarily skip the compatibility check.
|
||||
|
||||
NOTE: This function is meant to accommodate an unusual case; one
|
||||
which is likely to eventually go away. There's is a pretty good
|
||||
chance this is not what you were looking for.
|
||||
|
||||
WARNING: Using this function to disable the check can lead to
|
||||
unexpected behavior and even crashes. It should only be used during
|
||||
extension module development.
|
||||
|
||||
If "disable_check" is True then the compatibility check will not
|
||||
happen while the context manager is active. Otherwise the check
|
||||
*will* happen.
|
||||
|
||||
Normally, extensions that do not support multiple interpreters
|
||||
may not be imported in a subinterpreter. That implies modules
|
||||
that do not implement multi-phase init or that explicitly of out.
|
||||
|
||||
Likewise for modules import in a subinterpeter with its own GIL
|
||||
when the extension does not support a per-interpreter GIL. This
|
||||
implies the module does not have a Py_mod_multiple_interpreters slot
|
||||
set to Py_MOD_PER_INTERPRETER_GIL_SUPPORTED.
|
||||
|
||||
In both cases, this context manager may be used to temporarily
|
||||
disable the check for compatible extension modules.
|
||||
|
||||
You can get the same effect as this function by implementing the
|
||||
basic interface of multi-phase init (PEP 489) and lying about
|
||||
support for mulitple interpreters (or per-interpreter GIL).
|
||||
"""
|
||||
|
||||
def __init__(self, *, disable_check):
|
||||
self.disable_check = bool(disable_check)
|
||||
|
||||
def __enter__(self):
|
||||
self.old = _imp._override_multi_interp_extensions_check(self.override)
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
old = self.old
|
||||
del self.old
|
||||
_imp._override_multi_interp_extensions_check(old)
|
||||
|
||||
@property
|
||||
def override(self):
|
||||
return -1 if self.disable_check else 1
|
||||
|
||||
|
||||
class _LazyModule(types.ModuleType):
|
||||
|
||||
"""A subclass of the module type which triggers loading upon attribute access."""
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
"""Trigger the load of the module and return the attribute."""
|
||||
# All module metadata must be garnered from __spec__ in order to avoid
|
||||
# using mutated values.
|
||||
# Stop triggering this method.
|
||||
self.__class__ = types.ModuleType
|
||||
# Get the original name to make sure no object substitution occurred
|
||||
# in sys.modules.
|
||||
original_name = self.__spec__.name
|
||||
# Figure out exactly what attributes were mutated between the creation
|
||||
# of the module and now.
|
||||
attrs_then = self.__spec__.loader_state['__dict__']
|
||||
attrs_now = self.__dict__
|
||||
attrs_updated = {}
|
||||
for key, value in attrs_now.items():
|
||||
# Code that set the attribute may have kept a reference to the
|
||||
# assigned object, making identity more important than equality.
|
||||
if key not in attrs_then:
|
||||
attrs_updated[key] = value
|
||||
elif id(attrs_now[key]) != id(attrs_then[key]):
|
||||
attrs_updated[key] = value
|
||||
self.__spec__.loader.exec_module(self)
|
||||
# If exec_module() was used directly there is no guarantee the module
|
||||
# object was put into sys.modules.
|
||||
if original_name in sys.modules:
|
||||
if id(self) != id(sys.modules[original_name]):
|
||||
raise ValueError(f"module object for {original_name!r} "
|
||||
"substituted in sys.modules during a lazy "
|
||||
"load")
|
||||
# Update after loading since that's what would happen in an eager
|
||||
# loading situation.
|
||||
self.__dict__.update(attrs_updated)
|
||||
return getattr(self, attr)
|
||||
|
||||
def __delattr__(self, attr):
|
||||
"""Trigger the load and then perform the deletion."""
|
||||
# To trigger the load and raise an exception if the attribute
|
||||
# doesn't exist.
|
||||
self.__getattribute__(attr)
|
||||
delattr(self, attr)
|
||||
|
||||
|
||||
class LazyLoader(Loader):
|
||||
|
||||
"""A loader that creates a module which defers loading until attribute access."""
|
||||
|
||||
@staticmethod
|
||||
def __check_eager_loader(loader):
|
||||
if not hasattr(loader, 'exec_module'):
|
||||
raise TypeError('loader must define exec_module()')
|
||||
|
||||
@classmethod
|
||||
def factory(cls, loader):
|
||||
"""Construct a callable which returns the eager loader made lazy."""
|
||||
cls.__check_eager_loader(loader)
|
||||
return lambda *args, **kwargs: cls(loader(*args, **kwargs))
|
||||
|
||||
def __init__(self, loader):
|
||||
self.__check_eager_loader(loader)
|
||||
self.loader = loader
|
||||
|
||||
def create_module(self, spec):
|
||||
return self.loader.create_module(spec)
|
||||
|
||||
def exec_module(self, module):
|
||||
"""Make the module load lazily."""
|
||||
module.__spec__.loader = self.loader
|
||||
module.__loader__ = self.loader
|
||||
# Don't need to worry about deep-copying as trying to set an attribute
|
||||
# on an object would have triggered the load,
|
||||
# e.g. ``module.__spec__.loader = None`` would trigger a load from
|
||||
# trying to access module.__spec__.
|
||||
loader_state = {}
|
||||
loader_state['__dict__'] = module.__dict__.copy()
|
||||
loader_state['__class__'] = module.__class__
|
||||
module.__spec__.loader_state = loader_state
|
||||
module.__class__ = _LazyModule
|
||||
Reference in New Issue
Block a user