77from ._bootstrap import spec_from_loader
88from ._bootstrap import spec_from_file_location
99from ._bootstrap import _resolve_name
10+ from ._bootstrap import _find_spec
1011
1112from contextlib import contextmanager
1213import functools
@@ -29,6 +30,77 @@ def resolve_name(name, package):
2930 return _resolve_name (name [level :], package , level )
3031
3132
33+ def _find_spec_from_path (name , path = None ):
34+ """Return the spec for the specified module.
35+
36+ First, sys.modules is checked to see if the module was already imported. If
37+ so, then sys.modules[name].__spec__ is returned. If that happens to be
38+ set to None, then ValueError is raised. If the module is not in
39+ sys.modules, then sys.meta_path is searched for a suitable spec with the
40+ value of 'path' given to the finders. None is returned if no spec could
41+ be found.
42+
43+ Dotted names do not have their parent packages implicitly imported. You will
44+ most likely need to explicitly import all parent packages in the proper
45+ order for a submodule to get the correct spec.
46+
47+ """
48+ if name not in sys .modules :
49+ return _find_spec (name , path )
50+ else :
51+ module = sys .modules [name ]
52+ if module is None :
53+ return None
54+ try :
55+ spec = module .__spec__
56+ except AttributeError :
57+ raise ValueError ('{}.__spec__ is not set' .format (name ))
58+ else :
59+ if spec is None :
60+ raise ValueError ('{}.__spec__ is None' .format (name ))
61+ return spec
62+
63+
64+ def find_spec (name , package = None ):
65+ """Return the spec for the specified module.
66+
67+ First, sys.modules is checked to see if the module was already imported. If
68+ so, then sys.modules[name].__spec__ is returned. If that happens to be
69+ set to None, then ValueError is raised. If the module is not in
70+ sys.modules, then sys.meta_path is searched for a suitable spec with the
71+ value of 'path' given to the finders. None is returned if no spec could
72+ be found.
73+
74+ If the name is for submodule (contains a dot), the parent module is
75+ automatically imported.
76+
77+ The name and package arguments work the same as importlib.import_module().
78+ In other words, relative module names (with leading dots) work.
79+
80+ """
81+ fullname = resolve_name (name , package ) if name .startswith ('.' ) else name
82+ if fullname not in sys .modules :
83+ parent_name = fullname .rpartition ('.' )[0 ]
84+ if parent_name :
85+ # Use builtins.__import__() in case someone replaced it.
86+ parent = __import__ (parent_name , fromlist = ['__path__' ])
87+ return _find_spec (fullname , parent .__path__ )
88+ else :
89+ return _find_spec (fullname , None )
90+ else :
91+ module = sys .modules [fullname ]
92+ if module is None :
93+ return None
94+ try :
95+ spec = module .__spec__
96+ except AttributeError :
97+ raise ValueError ('{}.__spec__ is not set' .format (name ))
98+ else :
99+ if spec is None :
100+ raise ValueError ('{}.__spec__ is None' .format (name ))
101+ return spec
102+
103+
32104@contextmanager
33105def _module_to_load (name ):
34106 is_reload = name in sys .modules
0 commit comments