summaryrefslogtreecommitdiff
path: root/Lib/importlib/util.py
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2014-01-25 15:32:46 -0700
committerEric Snow <ericsnowcurrently@gmail.com>2014-01-25 15:32:46 -0700
commit6029e086911be873b2ebacb933e3df08c23084e4 (patch)
treed91555f72eacd791ecf1bdd9d169bf82ea28a49d /Lib/importlib/util.py
parent128ee220e21bd4e7eb152396ff24d879f38c5d91 (diff)
downloadcpython-git-6029e086911be873b2ebacb933e3df08c23084e4.tar.gz
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/util.py')
-rw-r--r--Lib/importlib/util.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
index 42fc9eae93..6d73b1d7b6 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