diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2014-01-25 22:32:46 (GMT) |
---|---|---|
committer | Eric Snow <ericsnowcurrently@gmail.com> | 2014-01-25 22:32:46 (GMT) |
commit | 6029e086911be873b2ebacb933e3df08c23084e4 (patch) | |
tree | d91555f72eacd791ecf1bdd9d169bf82ea28a49d /Lib/importlib | |
parent | 128ee220e21bd4e7eb152396ff24d879f38c5d91 (diff) | |
download | cpython-6029e086911be873b2ebacb933e3df08c23084e4.zip cpython-6029e086911be873b2ebacb933e3df08c23084e4.tar.gz cpython-6029e086911be873b2ebacb933e3df08c23084e4.tar.bz2 |
Issue 19944: Fix importlib.find_spec() so it imports parents as needed.
The function is also moved to importlib.util.
Diffstat (limited to 'Lib/importlib')
-rw-r--r-- | Lib/importlib/__init__.py | 46 | ||||
-rw-r--r-- | Lib/importlib/util.py | 72 |
2 files changed, 81 insertions, 37 deletions
diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py index c66e46c..f6adc5c 100644 --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -11,8 +11,6 @@ __all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload'] # 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 @@ -34,6 +32,10 @@ _r_long = _bootstrap._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__ @@ -47,47 +49,16 @@ def 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 - - 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(). + This function is deprecated in favor of importlib.util.find_spec(). """ - warnings.warn('Use importlib.find_spec() instead.', DeprecationWarning, - stacklevel=2) + warnings.warn('Use importlib.util.find_spec() instead.', + DeprecationWarning, stacklevel=2) try: loader = sys.modules[name].__loader__ if loader is None: @@ -167,7 +138,8 @@ def reload(module): pkgpath = parent.__path__ else: pkgpath = None - spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, module) + target = module + spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target) methods = _bootstrap._SpecMethods(spec) methods.exec(module) # The module may have replaced itself in sys.modules! diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index 42fc9ea..6d73b1d 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -7,6 +7,7 @@ from ._bootstrap import source_from_cache from ._bootstrap import spec_from_loader from ._bootstrap import spec_from_file_location from ._bootstrap import _resolve_name +from ._bootstrap import _find_spec from contextlib import contextmanager import functools @@ -29,6 +30,77 @@ def resolve_name(name, package): 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)) + 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)) + 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 |