mirror of https://github.com/python/cpython
169 lines
5.6 KiB
Python
169 lines
5.6 KiB
Python
"""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
|
|
import types
|
|
|
|
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'
|
|
_bootstrap.__file__ = __file__.replace('__init__.py', '_bootstrap.py')
|
|
sys.modules['importlib._bootstrap'] = _bootstrap
|
|
|
|
# To simplify imports in test code
|
|
_w_long = _bootstrap._w_long
|
|
_r_long = _bootstrap._r_long
|
|
|
|
# Fully bootstrapped at this point, import whatever you like, circular
|
|
# dependencies and startup overhead minimisation permitting :)
|
|
|
|
# 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_spec(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 _bootstrap._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))
|
|
else:
|
|
if spec is None:
|
|
raise ValueError('{}.__spec__ is None'.format(name))
|
|
return spec
|
|
|
|
|
|
# XXX Deprecate...
|
|
def find_loader(name, path=None):
|
|
"""Return the loader for the specified module.
|
|
|
|
This is a backward-compatible wrapper around find_spec().
|
|
|
|
"""
|
|
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))
|
|
|
|
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 and parent_name not in sys.modules:
|
|
msg = "parent {!r} not in sys.modules"
|
|
raise ImportError(msg.format(parent_name), name=parent_name)
|
|
spec = module.__spec__ = _bootstrap._find_spec(name, None, module)
|
|
methods = _bootstrap._SpecMethods(spec)
|
|
methods.exec(module)
|
|
# The module may have replaced itself in sys.modules!
|
|
return sys.modules[name]
|
|
finally:
|
|
try:
|
|
del _RELOADING[name]
|
|
except KeyError:
|
|
pass
|