diff options
Diffstat (limited to 'Lib/importlib/__init__.py')
-rw-r--r-- | Lib/importlib/__init__.py | 107 |
1 files changed, 98 insertions, 9 deletions
diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py index 22c90f24a7e..c66e46cc935 100644 --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -1,5 +1,5 @@ """A pure Python implementation of import.""" -__all__ = ['__import__', 'import_module', 'invalidate_caches'] +__all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload'] # Bootstrap help ##################################################### @@ -11,6 +11,8 @@ __all__ = ['__import__', 'import_module', 'invalidate_caches'] # initialised below if the frozen one is not available). import _imp # Just the builtin component, NOT the full Python module import sys +import types +import warnings try: import _frozen_importlib as _bootstrap @@ -45,21 +47,47 @@ def invalidate_caches(): finder.invalidate_caches() -def find_loader(name, path=None): - """Find the loader for the specified module. +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].__loader__ is returned. If that happens to be + 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 loader with the - value of 'path' given to the finders. None is returned if no loader could + 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 loader. + 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 + + +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.find_spec(). """ + warnings.warn('Use importlib.find_spec() instead.', DeprecationWarning, + stacklevel=2) try: loader = sys.modules[name].__loader__ if loader is None: @@ -68,7 +96,20 @@ def find_loader(name, path=None): return loader except KeyError: pass - return _bootstrap._find_module(name, path) + 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): @@ -82,9 +123,57 @@ def import_module(name, package=None): level = 0 if name.startswith('.'): if not package: - raise TypeError("relative imports require the 'package' argument") + 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) + else: + pkgpath = parent.__path__ + else: + pkgpath = None + spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, 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 |