forked from Rust-related/RustPython
Merge pull request #1371 from RustPython/coolreader18/importlib-module
Add importlib and pkgutil modules (CPython 3.7)
This commit is contained in:
@@ -1,97 +0,0 @@
|
||||
"""Utility code for constructing importers, etc."""
|
||||
from _frozen_importlib import _resolve_name
|
||||
from _frozen_importlib import _find_spec
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def resolve_name(name, package):
|
||||
"""Resolve a relative module name to an absolute one."""
|
||||
if not name.startswith('.'):
|
||||
return name
|
||||
elif not package:
|
||||
raise ValueError(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('{}.__spec__ is not set'.format(name)) from None
|
||||
else:
|
||||
if spec is None:
|
||||
raise ValueError('{}.__spec__ is None'.format(name))
|
||||
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('{}.__spec__ is not set'.format(name)) from None
|
||||
else:
|
||||
if spec is None:
|
||||
raise ValueError('{}.__spec__ is None'.format(name))
|
||||
return spec
|
||||
|
||||
173
Lib/importlib/__init__.py
Normal file
173
Lib/importlib/__init__.py
Normal file
@@ -0,0 +1,173 @@
|
||||
"""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._setup(_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
|
||||
_w_long = _bootstrap_external._w_long
|
||||
_r_long = _bootstrap_external._r_long
|
||||
|
||||
# Fully bootstrapped at this point, import whatever you like, circular
|
||||
# dependencies and startup overhead minimisation permitting :)
|
||||
|
||||
import types
|
||||
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 find_loader(name, path=None):
|
||||
"""Return the loader for the specified module.
|
||||
|
||||
This is a backward-compatible wrapper around find_spec().
|
||||
|
||||
This function is deprecated in favor of importlib.util.find_spec().
|
||||
|
||||
"""
|
||||
warnings.warn('Use importlib.util.find_spec() instead.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
try:
|
||||
loader = sys.modules[name].__loader__
|
||||
if loader is None:
|
||||
raise ValueError('{}.__loader__ is None'.format(name))
|
||||
else:
|
||||
return loader
|
||||
except KeyError:
|
||||
pass
|
||||
except AttributeError:
|
||||
raise ValueError('{}.__loader__ is not set'.format(name)) from None
|
||||
|
||||
spec = _bootstrap._find_spec(name, path)
|
||||
# We won't worry about malformed specs (missing attributes).
|
||||
if spec is None:
|
||||
return None
|
||||
if spec.loader is None:
|
||||
if spec.submodule_search_locations is None:
|
||||
raise ImportError('spec for {} missing loader'.format(name),
|
||||
name=name)
|
||||
raise ImportError('namespace packages do not have loaders',
|
||||
name=name)
|
||||
return spec.loader
|
||||
|
||||
|
||||
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:
|
||||
msg = ("the 'package' argument is required to perform a relative "
|
||||
"import for {!r}")
|
||||
raise TypeError(msg.format(name))
|
||||
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.
|
||||
|
||||
"""
|
||||
if not module or not isinstance(module, types.ModuleType):
|
||||
raise TypeError("reload() argument must be module")
|
||||
try:
|
||||
name = module.__spec__.name
|
||||
except AttributeError:
|
||||
name = module.__name__
|
||||
|
||||
if sys.modules.get(name) is not module:
|
||||
msg = "module {} not in sys.modules"
|
||||
raise ImportError(msg.format(name), 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:
|
||||
msg = "parent {!r} not in sys.modules"
|
||||
raise ImportError(msg.format(parent_name),
|
||||
name=parent_name) from None
|
||||
else:
|
||||
pkgpath = parent.__path__
|
||||
else:
|
||||
pkgpath = None
|
||||
target = module
|
||||
spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
|
||||
_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
|
||||
329
Lib/importlib/abc.py
Normal file
329
Lib/importlib/abc.py
Normal file
@@ -0,0 +1,329 @@
|
||||
"""Abstract base classes related to import."""
|
||||
from . import _bootstrap
|
||||
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 as exc:
|
||||
_frozen_importlib_external = _bootstrap_external
|
||||
import abc
|
||||
|
||||
|
||||
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 Finder(metaclass=abc.ABCMeta):
|
||||
|
||||
"""Legacy abstract base class for import finders.
|
||||
|
||||
It may be subclassed for compatibility with legacy third party
|
||||
reimplementations of the import system. Otherwise, finder
|
||||
implementations should derive from the more specific MetaPathFinder
|
||||
or PathEntryFinder ABCs.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_module(self, fullname, path=None):
|
||||
"""An abstract method that should find a module.
|
||||
The fullname is a str and the optional path is a str or None.
|
||||
Returns a Loader object or None.
|
||||
"""
|
||||
|
||||
|
||||
class MetaPathFinder(Finder):
|
||||
|
||||
"""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 find_module(self, fullname, path):
|
||||
"""Return a loader for the module.
|
||||
|
||||
If no module is found, return None. The fullname is a str and
|
||||
the path is a list of strings or None.
|
||||
|
||||
This method is deprecated in favor of finder.find_spec(). If find_spec()
|
||||
exists then backwards-compatible functionality is provided for this
|
||||
method.
|
||||
|
||||
"""
|
||||
if not hasattr(self, 'find_spec'):
|
||||
return None
|
||||
found = self.find_spec(fullname, path)
|
||||
return found.loader if found is not None else None
|
||||
|
||||
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(Finder):
|
||||
|
||||
"""Abstract base class for path entry finders used by PathFinder."""
|
||||
|
||||
# We don't define find_spec() here since that would break
|
||||
# hasattr checks we do to support backward compatibility.
|
||||
|
||||
def find_loader(self, fullname):
|
||||
"""Return (loader, namespace portion) for the path entry.
|
||||
|
||||
The fullname is a str. The namespace portion is a sequence of
|
||||
path entries contributing to part of a namespace package. The
|
||||
sequence may be empty. If loader is not None, the portion will
|
||||
be ignored.
|
||||
|
||||
The portion will be discarded if another path entry finder
|
||||
locates the module as a normal module or package.
|
||||
|
||||
This method is deprecated in favor of finder.find_spec(). If find_spec()
|
||||
is provided than backwards-compatible functionality is provided.
|
||||
|
||||
"""
|
||||
if not hasattr(self, 'find_spec'):
|
||||
return None, []
|
||||
found = self.find_spec(fullname)
|
||||
if found is not None:
|
||||
if not found.submodule_search_locations:
|
||||
portions = []
|
||||
else:
|
||||
portions = found.submodule_search_locations
|
||||
return found.loader, portions
|
||||
else:
|
||||
return None, []
|
||||
|
||||
find_module = _bootstrap_external._find_module_shim
|
||||
|
||||
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 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
|
||||
return _bootstrap._load_module_shim(self, fullname)
|
||||
|
||||
def module_repr(self, module):
|
||||
"""Return a module's repr.
|
||||
|
||||
Used by the module type when the method does not raise
|
||||
NotImplementedError.
|
||||
|
||||
This method is deprecated.
|
||||
|
||||
"""
|
||||
# The exception will cause ModuleType.__repr__ to ignore this method.
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
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 IOError
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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 IOError
|
||||
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 IOError
|
||||
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)
|
||||
21
Lib/importlib/machinery.py
Normal file
21
Lib/importlib/machinery.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""The machinery of importlib: finders, loaders, hooks, etc."""
|
||||
|
||||
import _imp
|
||||
|
||||
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
|
||||
|
||||
|
||||
def all_suffixes():
|
||||
"""Returns a list of all recognized module suffixes for this process"""
|
||||
return SOURCE_SUFFIXES + BYTECODE_SUFFIXES + EXTENSION_SUFFIXES
|
||||
288
Lib/importlib/util.py
Normal file
288
Lib/importlib/util.py
Normal file
@@ -0,0 +1,288 @@
|
||||
"""Utility code for constructing importers, etc."""
|
||||
from . import abc
|
||||
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 cache_from_source
|
||||
from ._bootstrap_external import decode_source
|
||||
from ._bootstrap_external import source_from_cache
|
||||
from ._bootstrap_external import spec_from_file_location
|
||||
|
||||
from contextlib import contextmanager
|
||||
import functools
|
||||
import sys
|
||||
import types
|
||||
import warnings
|
||||
|
||||
|
||||
def resolve_name(name, package):
|
||||
"""Resolve a relative module name to an absolute one."""
|
||||
if not name.startswith('.'):
|
||||
return name
|
||||
elif not package:
|
||||
raise ValueError(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('{}.__spec__ is not set'.format(name)) from None
|
||||
else:
|
||||
if spec is None:
|
||||
raise ValueError('{}.__spec__ is None'.format(name))
|
||||
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:
|
||||
# Use builtins.__import__() in case someone replaced it.
|
||||
parent = __import__(parent_name, fromlist=['__path__'])
|
||||
return _find_spec(fullname, parent.__path__)
|
||||
else:
|
||||
return _find_spec(fullname, None)
|
||||
else:
|
||||
module = sys.modules[fullname]
|
||||
if module is None:
|
||||
return None
|
||||
try:
|
||||
spec = module.__spec__
|
||||
except AttributeError:
|
||||
raise ValueError('{}.__spec__ is not set'.format(name)) from None
|
||||
else:
|
||||
if spec is None:
|
||||
raise ValueError('{}.__spec__ is None'.format(name))
|
||||
return spec
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _module_to_load(name):
|
||||
is_reload = name in sys.modules
|
||||
|
||||
module = sys.modules.get(name)
|
||||
if not is_reload:
|
||||
# This must be done before open() is called as the 'io' module
|
||||
# implicitly imports 'locale' and would otherwise trigger an
|
||||
# infinite loop.
|
||||
module = type(sys)(name)
|
||||
# This must be done before putting the module in sys.modules
|
||||
# (otherwise an optimization shortcut in import.c becomes wrong)
|
||||
module.__initializing__ = True
|
||||
sys.modules[name] = module
|
||||
try:
|
||||
yield module
|
||||
except Exception:
|
||||
if not is_reload:
|
||||
try:
|
||||
del sys.modules[name]
|
||||
except KeyError:
|
||||
pass
|
||||
finally:
|
||||
module.__initializing__ = False
|
||||
|
||||
|
||||
def set_package(fxn):
|
||||
"""Set __package__ on the returned module.
|
||||
|
||||
This function is deprecated.
|
||||
|
||||
"""
|
||||
@functools.wraps(fxn)
|
||||
def set_package_wrapper(*args, **kwargs):
|
||||
warnings.warn('The import system now takes care of this automatically.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
module = fxn(*args, **kwargs)
|
||||
if getattr(module, '__package__', None) is None:
|
||||
module.__package__ = module.__name__
|
||||
if not hasattr(module, '__path__'):
|
||||
module.__package__ = module.__package__.rpartition('.')[0]
|
||||
return module
|
||||
return set_package_wrapper
|
||||
|
||||
|
||||
def set_loader(fxn):
|
||||
"""Set __loader__ on the returned module.
|
||||
|
||||
This function is deprecated.
|
||||
|
||||
"""
|
||||
@functools.wraps(fxn)
|
||||
def set_loader_wrapper(self, *args, **kwargs):
|
||||
warnings.warn('The import system now takes care of this automatically.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
module = fxn(self, *args, **kwargs)
|
||||
if getattr(module, '__loader__', None) is None:
|
||||
module.__loader__ = self
|
||||
return module
|
||||
return set_loader_wrapper
|
||||
|
||||
|
||||
def module_for_loader(fxn):
|
||||
"""Decorator to handle selecting the proper module for loaders.
|
||||
|
||||
The decorated function is passed the module to use instead of the module
|
||||
name. The module passed in to the function is either from sys.modules if
|
||||
it already exists or is a new module. If the module is new, then __name__
|
||||
is set the first argument to the method, __loader__ is set to self, and
|
||||
__package__ is set accordingly (if self.is_package() is defined) will be set
|
||||
before it is passed to the decorated function (if self.is_package() does
|
||||
not work for the module it will be set post-load).
|
||||
|
||||
If an exception is raised and the decorator created the module it is
|
||||
subsequently removed from sys.modules.
|
||||
|
||||
The decorator assumes that the decorated function takes the module name as
|
||||
the second argument.
|
||||
|
||||
"""
|
||||
warnings.warn('The import system now takes care of this automatically.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
@functools.wraps(fxn)
|
||||
def module_for_loader_wrapper(self, fullname, *args, **kwargs):
|
||||
with _module_to_load(fullname) as module:
|
||||
module.__loader__ = self
|
||||
try:
|
||||
is_package = self.is_package(fullname)
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
else:
|
||||
if is_package:
|
||||
module.__package__ = fullname
|
||||
else:
|
||||
module.__package__ = fullname.rpartition('.')[0]
|
||||
# If __package__ was not set above, __import__() will do it later.
|
||||
return fxn(self, module, *args, **kwargs)
|
||||
|
||||
return module_for_loader_wrapper
|
||||
|
||||
|
||||
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__']
|
||||
original_type = self.__spec__.loader_state['__class__']
|
||||
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(abc.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
|
||||
634
Lib/pkgutil.py
Normal file
634
Lib/pkgutil.py
Normal file
@@ -0,0 +1,634 @@
|
||||
"""Utilities to support packages."""
|
||||
|
||||
from collections import namedtuple
|
||||
from functools import singledispatch as simplegeneric
|
||||
import importlib
|
||||
import importlib.util
|
||||
import importlib.machinery
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
from types import ModuleType
|
||||
import warnings
|
||||
|
||||
__all__ = [
|
||||
'get_importer', 'iter_importers', 'get_loader', 'find_loader',
|
||||
'walk_packages', 'iter_modules', 'get_data',
|
||||
'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
|
||||
'ModuleInfo',
|
||||
]
|
||||
|
||||
|
||||
ModuleInfo = namedtuple('ModuleInfo', 'module_finder name ispkg')
|
||||
ModuleInfo.__doc__ = 'A namedtuple with minimal info about a module.'
|
||||
|
||||
|
||||
def _get_spec(finder, name):
|
||||
"""Return the finder-specific module spec."""
|
||||
# Works with legacy finders.
|
||||
try:
|
||||
find_spec = finder.find_spec
|
||||
except AttributeError:
|
||||
loader = finder.find_module(name)
|
||||
if loader is None:
|
||||
return None
|
||||
return importlib.util.spec_from_loader(name, loader)
|
||||
else:
|
||||
return find_spec(name)
|
||||
|
||||
|
||||
def read_code(stream):
|
||||
# This helper is needed in order for the PEP 302 emulation to
|
||||
# correctly handle compiled files
|
||||
import marshal
|
||||
|
||||
magic = stream.read(4)
|
||||
if magic != importlib.util.MAGIC_NUMBER:
|
||||
return None
|
||||
|
||||
stream.read(8) # Skip timestamp and size
|
||||
return marshal.load(stream)
|
||||
|
||||
|
||||
def walk_packages(path=None, prefix='', onerror=None):
|
||||
"""Yields ModuleInfo for all modules recursively
|
||||
on path, or, if path is None, all accessible modules.
|
||||
|
||||
'path' should be either None or a list of paths to look for
|
||||
modules in.
|
||||
|
||||
'prefix' is a string to output on the front of every module name
|
||||
on output.
|
||||
|
||||
Note that this function must import all *packages* (NOT all
|
||||
modules!) on the given path, in order to access the __path__
|
||||
attribute to find submodules.
|
||||
|
||||
'onerror' is a function which gets called with one argument (the
|
||||
name of the package which was being imported) if any exception
|
||||
occurs while trying to import a package. If no onerror function is
|
||||
supplied, ImportErrors are caught and ignored, while all other
|
||||
exceptions are propagated, terminating the search.
|
||||
|
||||
Examples:
|
||||
|
||||
# list all modules python can access
|
||||
walk_packages()
|
||||
|
||||
# list all submodules of ctypes
|
||||
walk_packages(ctypes.__path__, ctypes.__name__+'.')
|
||||
"""
|
||||
|
||||
def seen(p, m={}):
|
||||
if p in m:
|
||||
return True
|
||||
m[p] = True
|
||||
|
||||
for info in iter_modules(path, prefix):
|
||||
yield info
|
||||
|
||||
if info.ispkg:
|
||||
try:
|
||||
__import__(info.name)
|
||||
except ImportError:
|
||||
if onerror is not None:
|
||||
onerror(info.name)
|
||||
except Exception:
|
||||
if onerror is not None:
|
||||
onerror(info.name)
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
path = getattr(sys.modules[info.name], '__path__', None) or []
|
||||
|
||||
# don't traverse path items we've seen before
|
||||
path = [p for p in path if not seen(p)]
|
||||
|
||||
yield from walk_packages(path, info.name+'.', onerror)
|
||||
|
||||
|
||||
def iter_modules(path=None, prefix=''):
|
||||
"""Yields ModuleInfo for all submodules on path,
|
||||
or, if path is None, all top-level modules on sys.path.
|
||||
|
||||
'path' should be either None or a list of paths to look for
|
||||
modules in.
|
||||
|
||||
'prefix' is a string to output on the front of every module name
|
||||
on output.
|
||||
"""
|
||||
if path is None:
|
||||
importers = iter_importers()
|
||||
else:
|
||||
importers = map(get_importer, path)
|
||||
|
||||
yielded = {}
|
||||
for i in importers:
|
||||
for name, ispkg in iter_importer_modules(i, prefix):
|
||||
if name not in yielded:
|
||||
yielded[name] = 1
|
||||
yield ModuleInfo(i, name, ispkg)
|
||||
|
||||
|
||||
@simplegeneric
|
||||
def iter_importer_modules(importer, prefix=''):
|
||||
if not hasattr(importer, 'iter_modules'):
|
||||
return []
|
||||
return importer.iter_modules(prefix)
|
||||
|
||||
|
||||
# Implement a file walker for the normal importlib path hook
|
||||
def _iter_file_finder_modules(importer, prefix=''):
|
||||
if importer.path is None or not os.path.isdir(importer.path):
|
||||
return
|
||||
|
||||
yielded = {}
|
||||
import inspect
|
||||
try:
|
||||
filenames = os.listdir(importer.path)
|
||||
except OSError:
|
||||
# ignore unreadable directories like import does
|
||||
filenames = []
|
||||
filenames.sort() # handle packages before same-named modules
|
||||
|
||||
for fn in filenames:
|
||||
modname = inspect.getmodulename(fn)
|
||||
if modname=='__init__' or modname in yielded:
|
||||
continue
|
||||
|
||||
path = os.path.join(importer.path, fn)
|
||||
ispkg = False
|
||||
|
||||
if not modname and os.path.isdir(path) and '.' not in fn:
|
||||
modname = fn
|
||||
try:
|
||||
dircontents = os.listdir(path)
|
||||
except OSError:
|
||||
# ignore unreadable directories like import does
|
||||
dircontents = []
|
||||
for fn in dircontents:
|
||||
subname = inspect.getmodulename(fn)
|
||||
if subname=='__init__':
|
||||
ispkg = True
|
||||
break
|
||||
else:
|
||||
continue # not a package
|
||||
|
||||
if modname and '.' not in modname:
|
||||
yielded[modname] = 1
|
||||
yield prefix + modname, ispkg
|
||||
|
||||
iter_importer_modules.register(
|
||||
importlib.machinery.FileFinder, _iter_file_finder_modules)
|
||||
|
||||
|
||||
def _import_imp():
|
||||
global imp
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', DeprecationWarning)
|
||||
imp = importlib.import_module('imp')
|
||||
|
||||
class ImpImporter:
|
||||
"""PEP 302 Finder that wraps Python's "classic" import algorithm
|
||||
|
||||
ImpImporter(dirname) produces a PEP 302 finder that searches that
|
||||
directory. ImpImporter(None) produces a PEP 302 finder that searches
|
||||
the current sys.path, plus any modules that are frozen or built-in.
|
||||
|
||||
Note that ImpImporter does not currently support being used by placement
|
||||
on sys.meta_path.
|
||||
"""
|
||||
|
||||
def __init__(self, path=None):
|
||||
global imp
|
||||
warnings.warn("This emulation is deprecated, use 'importlib' instead",
|
||||
DeprecationWarning)
|
||||
_import_imp()
|
||||
self.path = path
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
# Note: we ignore 'path' argument since it is only used via meta_path
|
||||
subname = fullname.split(".")[-1]
|
||||
if subname != fullname and self.path is None:
|
||||
return None
|
||||
if self.path is None:
|
||||
path = None
|
||||
else:
|
||||
path = [os.path.realpath(self.path)]
|
||||
try:
|
||||
file, filename, etc = imp.find_module(subname, path)
|
||||
except ImportError:
|
||||
return None
|
||||
return ImpLoader(fullname, file, filename, etc)
|
||||
|
||||
def iter_modules(self, prefix=''):
|
||||
if self.path is None or not os.path.isdir(self.path):
|
||||
return
|
||||
|
||||
yielded = {}
|
||||
import inspect
|
||||
try:
|
||||
filenames = os.listdir(self.path)
|
||||
except OSError:
|
||||
# ignore unreadable directories like import does
|
||||
filenames = []
|
||||
filenames.sort() # handle packages before same-named modules
|
||||
|
||||
for fn in filenames:
|
||||
modname = inspect.getmodulename(fn)
|
||||
if modname=='__init__' or modname in yielded:
|
||||
continue
|
||||
|
||||
path = os.path.join(self.path, fn)
|
||||
ispkg = False
|
||||
|
||||
if not modname and os.path.isdir(path) and '.' not in fn:
|
||||
modname = fn
|
||||
try:
|
||||
dircontents = os.listdir(path)
|
||||
except OSError:
|
||||
# ignore unreadable directories like import does
|
||||
dircontents = []
|
||||
for fn in dircontents:
|
||||
subname = inspect.getmodulename(fn)
|
||||
if subname=='__init__':
|
||||
ispkg = True
|
||||
break
|
||||
else:
|
||||
continue # not a package
|
||||
|
||||
if modname and '.' not in modname:
|
||||
yielded[modname] = 1
|
||||
yield prefix + modname, ispkg
|
||||
|
||||
|
||||
class ImpLoader:
|
||||
"""PEP 302 Loader that wraps Python's "classic" import algorithm
|
||||
"""
|
||||
code = source = None
|
||||
|
||||
def __init__(self, fullname, file, filename, etc):
|
||||
warnings.warn("This emulation is deprecated, use 'importlib' instead",
|
||||
DeprecationWarning)
|
||||
_import_imp()
|
||||
self.file = file
|
||||
self.filename = filename
|
||||
self.fullname = fullname
|
||||
self.etc = etc
|
||||
|
||||
def load_module(self, fullname):
|
||||
self._reopen()
|
||||
try:
|
||||
mod = imp.load_module(fullname, self.file, self.filename, self.etc)
|
||||
finally:
|
||||
if self.file:
|
||||
self.file.close()
|
||||
# Note: we don't set __loader__ because we want the module to look
|
||||
# normal; i.e. this is just a wrapper for standard import machinery
|
||||
return mod
|
||||
|
||||
def get_data(self, pathname):
|
||||
with open(pathname, "rb") as file:
|
||||
return file.read()
|
||||
|
||||
def _reopen(self):
|
||||
if self.file and self.file.closed:
|
||||
mod_type = self.etc[2]
|
||||
if mod_type==imp.PY_SOURCE:
|
||||
self.file = open(self.filename, 'r')
|
||||
elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
|
||||
self.file = open(self.filename, 'rb')
|
||||
|
||||
def _fix_name(self, fullname):
|
||||
if fullname is None:
|
||||
fullname = self.fullname
|
||||
elif fullname != self.fullname:
|
||||
raise ImportError("Loader for module %s cannot handle "
|
||||
"module %s" % (self.fullname, fullname))
|
||||
return fullname
|
||||
|
||||
def is_package(self, fullname):
|
||||
fullname = self._fix_name(fullname)
|
||||
return self.etc[2]==imp.PKG_DIRECTORY
|
||||
|
||||
def get_code(self, fullname=None):
|
||||
fullname = self._fix_name(fullname)
|
||||
if self.code is None:
|
||||
mod_type = self.etc[2]
|
||||
if mod_type==imp.PY_SOURCE:
|
||||
source = self.get_source(fullname)
|
||||
self.code = compile(source, self.filename, 'exec')
|
||||
elif mod_type==imp.PY_COMPILED:
|
||||
self._reopen()
|
||||
try:
|
||||
self.code = read_code(self.file)
|
||||
finally:
|
||||
self.file.close()
|
||||
elif mod_type==imp.PKG_DIRECTORY:
|
||||
self.code = self._get_delegate().get_code()
|
||||
return self.code
|
||||
|
||||
def get_source(self, fullname=None):
|
||||
fullname = self._fix_name(fullname)
|
||||
if self.source is None:
|
||||
mod_type = self.etc[2]
|
||||
if mod_type==imp.PY_SOURCE:
|
||||
self._reopen()
|
||||
try:
|
||||
self.source = self.file.read()
|
||||
finally:
|
||||
self.file.close()
|
||||
elif mod_type==imp.PY_COMPILED:
|
||||
if os.path.exists(self.filename[:-1]):
|
||||
with open(self.filename[:-1], 'r') as f:
|
||||
self.source = f.read()
|
||||
elif mod_type==imp.PKG_DIRECTORY:
|
||||
self.source = self._get_delegate().get_source()
|
||||
return self.source
|
||||
|
||||
def _get_delegate(self):
|
||||
finder = ImpImporter(self.filename)
|
||||
spec = _get_spec(finder, '__init__')
|
||||
return spec.loader
|
||||
|
||||
def get_filename(self, fullname=None):
|
||||
fullname = self._fix_name(fullname)
|
||||
mod_type = self.etc[2]
|
||||
if mod_type==imp.PKG_DIRECTORY:
|
||||
return self._get_delegate().get_filename()
|
||||
elif mod_type in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
|
||||
return self.filename
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
import zipimport
|
||||
from zipimport import zipimporter
|
||||
|
||||
def iter_zipimport_modules(importer, prefix=''):
|
||||
dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
|
||||
_prefix = importer.prefix
|
||||
plen = len(_prefix)
|
||||
yielded = {}
|
||||
import inspect
|
||||
for fn in dirlist:
|
||||
if not fn.startswith(_prefix):
|
||||
continue
|
||||
|
||||
fn = fn[plen:].split(os.sep)
|
||||
|
||||
if len(fn)==2 and fn[1].startswith('__init__.py'):
|
||||
if fn[0] not in yielded:
|
||||
yielded[fn[0]] = 1
|
||||
yield prefix + fn[0], True
|
||||
|
||||
if len(fn)!=1:
|
||||
continue
|
||||
|
||||
modname = inspect.getmodulename(fn[0])
|
||||
if modname=='__init__':
|
||||
continue
|
||||
|
||||
if modname and '.' not in modname and modname not in yielded:
|
||||
yielded[modname] = 1
|
||||
yield prefix + modname, False
|
||||
|
||||
iter_importer_modules.register(zipimporter, iter_zipimport_modules)
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def get_importer(path_item):
|
||||
"""Retrieve a finder for the given path item
|
||||
|
||||
The returned finder is cached in sys.path_importer_cache
|
||||
if it was newly created by a path hook.
|
||||
|
||||
The cache (or part of it) can be cleared manually if a
|
||||
rescan of sys.path_hooks is necessary.
|
||||
"""
|
||||
try:
|
||||
importer = sys.path_importer_cache[path_item]
|
||||
except KeyError:
|
||||
for path_hook in sys.path_hooks:
|
||||
try:
|
||||
importer = path_hook(path_item)
|
||||
sys.path_importer_cache.setdefault(path_item, importer)
|
||||
break
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
importer = None
|
||||
return importer
|
||||
|
||||
|
||||
def iter_importers(fullname=""):
|
||||
"""Yield finders for the given module name
|
||||
|
||||
If fullname contains a '.', the finders will be for the package
|
||||
containing fullname, otherwise they will be all registered top level
|
||||
finders (i.e. those on both sys.meta_path and sys.path_hooks).
|
||||
|
||||
If the named module is in a package, that package is imported as a side
|
||||
effect of invoking this function.
|
||||
|
||||
If no module name is specified, all top level finders are produced.
|
||||
"""
|
||||
if fullname.startswith('.'):
|
||||
msg = "Relative module name {!r} not supported".format(fullname)
|
||||
raise ImportError(msg)
|
||||
if '.' in fullname:
|
||||
# Get the containing package's __path__
|
||||
pkg_name = fullname.rpartition(".")[0]
|
||||
pkg = importlib.import_module(pkg_name)
|
||||
path = getattr(pkg, '__path__', None)
|
||||
if path is None:
|
||||
return
|
||||
else:
|
||||
yield from sys.meta_path
|
||||
path = sys.path
|
||||
for item in path:
|
||||
yield get_importer(item)
|
||||
|
||||
|
||||
def get_loader(module_or_name):
|
||||
"""Get a "loader" object for module_or_name
|
||||
|
||||
Returns None if the module cannot be found or imported.
|
||||
If the named module is not already imported, its containing package
|
||||
(if any) is imported, in order to establish the package __path__.
|
||||
"""
|
||||
if module_or_name in sys.modules:
|
||||
module_or_name = sys.modules[module_or_name]
|
||||
if module_or_name is None:
|
||||
return None
|
||||
if isinstance(module_or_name, ModuleType):
|
||||
module = module_or_name
|
||||
loader = getattr(module, '__loader__', None)
|
||||
if loader is not None:
|
||||
return loader
|
||||
if getattr(module, '__spec__', None) is None:
|
||||
return None
|
||||
fullname = module.__name__
|
||||
else:
|
||||
fullname = module_or_name
|
||||
return find_loader(fullname)
|
||||
|
||||
|
||||
def find_loader(fullname):
|
||||
"""Find a "loader" object for fullname
|
||||
|
||||
This is a backwards compatibility wrapper around
|
||||
importlib.util.find_spec that converts most failures to ImportError
|
||||
and only returns the loader rather than the full spec
|
||||
"""
|
||||
if fullname.startswith('.'):
|
||||
msg = "Relative module name {!r} not supported".format(fullname)
|
||||
raise ImportError(msg)
|
||||
try:
|
||||
spec = importlib.util.find_spec(fullname)
|
||||
except (ImportError, AttributeError, TypeError, ValueError) as ex:
|
||||
# This hack fixes an impedance mismatch between pkgutil and
|
||||
# importlib, where the latter raises other errors for cases where
|
||||
# pkgutil previously raised ImportError
|
||||
msg = "Error while finding loader for {!r} ({}: {})"
|
||||
raise ImportError(msg.format(fullname, type(ex), ex)) from ex
|
||||
return spec.loader if spec is not None else None
|
||||
|
||||
|
||||
def extend_path(path, name):
|
||||
"""Extend a package's path.
|
||||
|
||||
Intended use is to place the following code in a package's __init__.py:
|
||||
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
|
||||
This will add to the package's __path__ all subdirectories of
|
||||
directories on sys.path named after the package. This is useful
|
||||
if one wants to distribute different parts of a single logical
|
||||
package as multiple directories.
|
||||
|
||||
It also looks for *.pkg files beginning where * matches the name
|
||||
argument. This feature is similar to *.pth files (see site.py),
|
||||
except that it doesn't special-case lines starting with 'import'.
|
||||
A *.pkg file is trusted at face value: apart from checking for
|
||||
duplicates, all entries found in a *.pkg file are added to the
|
||||
path, regardless of whether they are exist the filesystem. (This
|
||||
is a feature.)
|
||||
|
||||
If the input path is not a list (as is the case for frozen
|
||||
packages) it is returned unchanged. The input path is not
|
||||
modified; an extended copy is returned. Items are only appended
|
||||
to the copy at the end.
|
||||
|
||||
It is assumed that sys.path is a sequence. Items of sys.path that
|
||||
are not (unicode or 8-bit) strings referring to existing
|
||||
directories are ignored. Unicode items of sys.path that cause
|
||||
errors when used as filenames may cause this function to raise an
|
||||
exception (in line with os.path.isdir() behavior).
|
||||
"""
|
||||
|
||||
if not isinstance(path, list):
|
||||
# This could happen e.g. when this is called from inside a
|
||||
# frozen package. Return the path unchanged in that case.
|
||||
return path
|
||||
|
||||
sname_pkg = name + ".pkg"
|
||||
|
||||
path = path[:] # Start with a copy of the existing path
|
||||
|
||||
parent_package, _, final_name = name.rpartition('.')
|
||||
if parent_package:
|
||||
try:
|
||||
search_path = sys.modules[parent_package].__path__
|
||||
except (KeyError, AttributeError):
|
||||
# We can't do anything: find_loader() returns None when
|
||||
# passed a dotted name.
|
||||
return path
|
||||
else:
|
||||
search_path = sys.path
|
||||
|
||||
for dir in search_path:
|
||||
if not isinstance(dir, str):
|
||||
continue
|
||||
|
||||
finder = get_importer(dir)
|
||||
if finder is not None:
|
||||
portions = []
|
||||
if hasattr(finder, 'find_spec'):
|
||||
spec = finder.find_spec(final_name)
|
||||
if spec is not None:
|
||||
portions = spec.submodule_search_locations or []
|
||||
# Is this finder PEP 420 compliant?
|
||||
elif hasattr(finder, 'find_loader'):
|
||||
_, portions = finder.find_loader(final_name)
|
||||
|
||||
for portion in portions:
|
||||
# XXX This may still add duplicate entries to path on
|
||||
# case-insensitive filesystems
|
||||
if portion not in path:
|
||||
path.append(portion)
|
||||
|
||||
# XXX Is this the right thing for subpackages like zope.app?
|
||||
# It looks for a file named "zope.app.pkg"
|
||||
pkgfile = os.path.join(dir, sname_pkg)
|
||||
if os.path.isfile(pkgfile):
|
||||
try:
|
||||
f = open(pkgfile)
|
||||
except OSError as msg:
|
||||
sys.stderr.write("Can't open %s: %s\n" %
|
||||
(pkgfile, msg))
|
||||
else:
|
||||
with f:
|
||||
for line in f:
|
||||
line = line.rstrip('\n')
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
path.append(line) # Don't check for existence!
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def get_data(package, resource):
|
||||
"""Get a resource from a package.
|
||||
|
||||
This is a wrapper round the PEP 302 loader get_data API. The package
|
||||
argument should be the name of a package, in standard module format
|
||||
(foo.bar). The resource argument should be in the form of a relative
|
||||
filename, using '/' as the path separator. The parent directory name '..'
|
||||
is not allowed, and nor is a rooted name (starting with a '/').
|
||||
|
||||
The function returns a binary string, which is the contents of the
|
||||
specified resource.
|
||||
|
||||
For packages located in the filesystem, which have already been imported,
|
||||
this is the rough equivalent of
|
||||
|
||||
d = os.path.dirname(sys.modules[package].__file__)
|
||||
data = open(os.path.join(d, resource), 'rb').read()
|
||||
|
||||
If the package cannot be located or loaded, or it uses a PEP 302 loader
|
||||
which does not support get_data(), then None is returned.
|
||||
"""
|
||||
|
||||
spec = importlib.util.find_spec(package)
|
||||
if spec is None:
|
||||
return None
|
||||
loader = spec.loader
|
||||
if loader is None or not hasattr(loader, 'get_data'):
|
||||
return None
|
||||
# XXX needs test
|
||||
mod = (sys.modules.get(package) or
|
||||
importlib._bootstrap._load(spec))
|
||||
if mod is None or not hasattr(mod, '__file__'):
|
||||
return None
|
||||
|
||||
# Modify the resource name to be compatible with the loader.get_data
|
||||
# signature - an os.path format "filename" starting with the dirname of
|
||||
# the package's __file__
|
||||
parts = resource.split('/')
|
||||
parts.insert(0, os.path.dirname(mod.__file__))
|
||||
resource_name = os.path.join(*parts)
|
||||
return loader.get_data(resource_name)
|
||||
14
Lib/runpy.py
14
Lib/runpy.py
@@ -11,15 +11,10 @@ importers when locating support scripts as well as when importing modules.
|
||||
|
||||
|
||||
import sys
|
||||
try:
|
||||
import __importlib_util
|
||||
except ImportError:
|
||||
# FIXME replace above with below once we can import importlib
|
||||
import importlib.machinery # importlib first so we can test #15386 via -m
|
||||
import importlib.util as __importlib_util
|
||||
import importlib.machinery # importlib first so we can test #15386 via -m
|
||||
import importlib.util
|
||||
import types
|
||||
# FIXME uncomment line below once we can import pkgutil
|
||||
# from pkgutil import read_code, get_importer
|
||||
from pkgutil import read_code, get_importer
|
||||
|
||||
__all__ = [
|
||||
"run_module", "run_path",
|
||||
@@ -130,8 +125,7 @@ def _get_module_details(mod_name, error=ImportError):
|
||||
warn(RuntimeWarning(msg))
|
||||
|
||||
try:
|
||||
# FIXME replace with importlib.util.find_spec() once we can import importlib
|
||||
spec = __importlib_util.find_spec(mod_name)
|
||||
spec = importlib.util.find_spec(mod_name)
|
||||
except (ImportError, AttributeError, TypeError, ValueError) as ex:
|
||||
# This hack fixes an impedance mismatch between pkgutil and
|
||||
# importlib, where the latter raises other errors for cases where
|
||||
|
||||
Reference in New Issue
Block a user