blob: 41c74d4cc6c2a6a576ab5ba03b4a35dc4724d76b [file] [log] [blame]
Brett Cannond2e7b332009-02-17 02:45:03 +00001"""Utility code for constructing importers, etc."""
Brett Cannona04dbe42014-04-04 13:53:38 -04002from . import abc
Brett Cannon2a17bde2014-05-30 14:55:29 -04003from ._bootstrap import module_from_spec
Brett Cannond200bf52012-05-13 13:45:09 -04004from ._bootstrap import _resolve_name
Eric Snow32439d62015-05-02 19:15:18 -06005from ._bootstrap import spec_from_loader
Eric Snow6029e082014-01-25 15:32:46 -07006from ._bootstrap import _find_spec
Eric Snow32439d62015-05-02 19:15:18 -06007from ._bootstrap_external import MAGIC_NUMBER
8from ._bootstrap_external import cache_from_source
9from ._bootstrap_external import decode_source
10from ._bootstrap_external import source_from_cache
11from ._bootstrap_external import spec_from_file_location
Brett Cannond200bf52012-05-13 13:45:09 -040012
Eric Snowb523f842013-11-22 09:05:39 -070013from contextlib import contextmanager
Brett Cannon0dbb4c72013-05-31 18:56:47 -040014import functools
Eric Snowb523f842013-11-22 09:05:39 -070015import sys
Brett Cannona04dbe42014-04-04 13:53:38 -040016import types
Brett Cannon0dbb4c72013-05-31 18:56:47 -040017import warnings
18
Brett Cannond200bf52012-05-13 13:45:09 -040019
20def resolve_name(name, package):
21 """Resolve a relative module name to an absolute one."""
22 if not name.startswith('.'):
23 return name
24 elif not package:
Brett Cannon65ca88e2015-12-04 15:19:42 -080025 raise ValueError(f'no package specified for {repr(name)} '
26 '(required for relative module names)')
Brett Cannond200bf52012-05-13 13:45:09 -040027 level = 0
28 for character in name:
29 if character != '.':
30 break
31 level += 1
32 return _resolve_name(name[level:], package, level)
Brett Cannon0dbb4c72013-05-31 18:56:47 -040033
34
Eric Snow6029e082014-01-25 15:32:46 -070035def _find_spec_from_path(name, path=None):
36 """Return the spec for the specified module.
37
38 First, sys.modules is checked to see if the module was already imported. If
39 so, then sys.modules[name].__spec__ is returned. If that happens to be
40 set to None, then ValueError is raised. If the module is not in
41 sys.modules, then sys.meta_path is searched for a suitable spec with the
42 value of 'path' given to the finders. None is returned if no spec could
43 be found.
44
45 Dotted names do not have their parent packages implicitly imported. You will
46 most likely need to explicitly import all parent packages in the proper
47 order for a submodule to get the correct spec.
48
49 """
50 if name not in sys.modules:
51 return _find_spec(name, path)
52 else:
53 module = sys.modules[name]
54 if module is None:
55 return None
56 try:
57 spec = module.__spec__
58 except AttributeError:
Serhiy Storchakac4464052014-11-21 20:33:57 +020059 raise ValueError('{}.__spec__ is not set'.format(name)) from None
Eric Snow6029e082014-01-25 15:32:46 -070060 else:
61 if spec is None:
62 raise ValueError('{}.__spec__ is None'.format(name))
63 return spec
64
65
66def find_spec(name, package=None):
67 """Return the spec for the specified module.
68
69 First, sys.modules is checked to see if the module was already imported. If
70 so, then sys.modules[name].__spec__ is returned. If that happens to be
71 set to None, then ValueError is raised. If the module is not in
72 sys.modules, then sys.meta_path is searched for a suitable spec with the
73 value of 'path' given to the finders. None is returned if no spec could
74 be found.
75
76 If the name is for submodule (contains a dot), the parent module is
77 automatically imported.
78
79 The name and package arguments work the same as importlib.import_module().
80 In other words, relative module names (with leading dots) work.
81
82 """
83 fullname = resolve_name(name, package) if name.startswith('.') else name
84 if fullname not in sys.modules:
85 parent_name = fullname.rpartition('.')[0]
86 if parent_name:
Eric Snow6029e082014-01-25 15:32:46 -070087 parent = __import__(parent_name, fromlist=['__path__'])
Milan Oberkirch8c3f05e2017-06-15 07:34:50 +100088 try:
89 parent_path = parent.__path__
90 except AttributeError as e:
91 raise ModuleNotFoundError(
92 f"__path__ attribute not found on {parent_name!r}"
93 f"while trying to find {fullname!r}", name=fullname) from e
Eric Snow6029e082014-01-25 15:32:46 -070094 else:
Milan Oberkirch8c3f05e2017-06-15 07:34:50 +100095 parent_path = None
96 return _find_spec(fullname, parent_path)
Eric Snow6029e082014-01-25 15:32:46 -070097 else:
98 module = sys.modules[fullname]
99 if module is None:
100 return None
101 try:
102 spec = module.__spec__
103 except AttributeError:
Serhiy Storchakac4464052014-11-21 20:33:57 +0200104 raise ValueError('{}.__spec__ is not set'.format(name)) from None
Eric Snow6029e082014-01-25 15:32:46 -0700105 else:
106 if spec is None:
107 raise ValueError('{}.__spec__ is None'.format(name))
108 return spec
109
110
Eric Snowb523f842013-11-22 09:05:39 -0700111@contextmanager
112def _module_to_load(name):
113 is_reload = name in sys.modules
114
115 module = sys.modules.get(name)
116 if not is_reload:
117 # This must be done before open() is called as the 'io' module
118 # implicitly imports 'locale' and would otherwise trigger an
119 # infinite loop.
120 module = type(sys)(name)
121 # This must be done before putting the module in sys.modules
122 # (otherwise an optimization shortcut in import.c becomes wrong)
123 module.__initializing__ = True
124 sys.modules[name] = module
125 try:
126 yield module
127 except Exception:
128 if not is_reload:
129 try:
130 del sys.modules[name]
131 except KeyError:
132 pass
133 finally:
134 module.__initializing__ = False
135
136
Eric Snowb523f842013-11-22 09:05:39 -0700137def set_package(fxn):
Eric Snow1500d492014-01-06 20:49:04 -0700138 """Set __package__ on the returned module.
139
140 This function is deprecated.
141
142 """
Eric Snowb523f842013-11-22 09:05:39 -0700143 @functools.wraps(fxn)
144 def set_package_wrapper(*args, **kwargs):
Eric Snow1500d492014-01-06 20:49:04 -0700145 warnings.warn('The import system now takes care of this automatically.',
146 DeprecationWarning, stacklevel=2)
Eric Snowb523f842013-11-22 09:05:39 -0700147 module = fxn(*args, **kwargs)
148 if getattr(module, '__package__', None) is None:
149 module.__package__ = module.__name__
150 if not hasattr(module, '__path__'):
151 module.__package__ = module.__package__.rpartition('.')[0]
152 return module
153 return set_package_wrapper
154
155
Eric Snowb523f842013-11-22 09:05:39 -0700156def set_loader(fxn):
Eric Snow1500d492014-01-06 20:49:04 -0700157 """Set __loader__ on the returned module.
158
159 This function is deprecated.
160
161 """
Eric Snowb523f842013-11-22 09:05:39 -0700162 @functools.wraps(fxn)
163 def set_loader_wrapper(self, *args, **kwargs):
Eric Snow1500d492014-01-06 20:49:04 -0700164 warnings.warn('The import system now takes care of this automatically.',
165 DeprecationWarning, stacklevel=2)
Eric Snowb523f842013-11-22 09:05:39 -0700166 module = fxn(self, *args, **kwargs)
167 if getattr(module, '__loader__', None) is None:
168 module.__loader__ = self
169 return module
170 return set_loader_wrapper
171
172
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400173def module_for_loader(fxn):
174 """Decorator to handle selecting the proper module for loaders.
175
176 The decorated function is passed the module to use instead of the module
177 name. The module passed in to the function is either from sys.modules if
178 it already exists or is a new module. If the module is new, then __name__
179 is set the first argument to the method, __loader__ is set to self, and
180 __package__ is set accordingly (if self.is_package() is defined) will be set
181 before it is passed to the decorated function (if self.is_package() does
182 not work for the module it will be set post-load).
183
184 If an exception is raised and the decorator created the module it is
185 subsequently removed from sys.modules.
186
187 The decorator assumes that the decorated function takes the module name as
188 the second argument.
189
190 """
Eric Snowb523f842013-11-22 09:05:39 -0700191 warnings.warn('The import system now takes care of this automatically.',
Eric Snow1500d492014-01-06 20:49:04 -0700192 DeprecationWarning, stacklevel=2)
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400193 @functools.wraps(fxn)
194 def module_for_loader_wrapper(self, fullname, *args, **kwargs):
Eric Snowb523f842013-11-22 09:05:39 -0700195 with _module_to_load(fullname) as module:
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400196 module.__loader__ = self
197 try:
198 is_package = self.is_package(fullname)
199 except (ImportError, AttributeError):
200 pass
201 else:
202 if is_package:
203 module.__package__ = fullname
204 else:
205 module.__package__ = fullname.rpartition('.')[0]
206 # If __package__ was not set above, __import__() will do it later.
207 return fxn(self, module, *args, **kwargs)
208
Brett Cannon0e75c062013-05-31 18:57:45 -0400209 return module_for_loader_wrapper
Brett Cannona04dbe42014-04-04 13:53:38 -0400210
211
Brett Cannona04dbe42014-04-04 13:53:38 -0400212class _LazyModule(types.ModuleType):
213
214 """A subclass of the module type which triggers loading upon attribute access."""
215
216 def __getattribute__(self, attr):
217 """Trigger the load of the module and return the attribute."""
218 # All module metadata must be garnered from __spec__ in order to avoid
219 # using mutated values.
220 # Stop triggering this method.
Brett Cannon696c35e2016-06-25 10:58:17 -0700221 self.__class__ = types.ModuleType
Brett Cannona04dbe42014-04-04 13:53:38 -0400222 # Get the original name to make sure no object substitution occurred
223 # in sys.modules.
224 original_name = self.__spec__.name
225 # Figure out exactly what attributes were mutated between the creation
226 # of the module and now.
Brett Cannon696c35e2016-06-25 10:58:17 -0700227 attrs_then = self.__spec__.loader_state['__dict__']
228 original_type = self.__spec__.loader_state['__class__']
Brett Cannona04dbe42014-04-04 13:53:38 -0400229 attrs_now = self.__dict__
230 attrs_updated = {}
231 for key, value in attrs_now.items():
232 # Code that set the attribute may have kept a reference to the
233 # assigned object, making identity more important than equality.
234 if key not in attrs_then:
235 attrs_updated[key] = value
236 elif id(attrs_now[key]) != id(attrs_then[key]):
237 attrs_updated[key] = value
238 self.__spec__.loader.exec_module(self)
239 # If exec_module() was used directly there is no guarantee the module
240 # object was put into sys.modules.
241 if original_name in sys.modules:
242 if id(self) != id(sys.modules[original_name]):
Brett Cannon696c35e2016-06-25 10:58:17 -0700243 raise ValueError(f"module object for {original_name!r} "
244 "substituted in sys.modules during a lazy "
245 "load")
Brett Cannona04dbe42014-04-04 13:53:38 -0400246 # Update after loading since that's what would happen in an eager
247 # loading situation.
248 self.__dict__.update(attrs_updated)
249 return getattr(self, attr)
250
251 def __delattr__(self, attr):
252 """Trigger the load and then perform the deletion."""
253 # To trigger the load and raise an exception if the attribute
254 # doesn't exist.
255 self.__getattribute__(attr)
256 delattr(self, attr)
257
258
259class LazyLoader(abc.Loader):
260
261 """A loader that creates a module which defers loading until attribute access."""
262
263 @staticmethod
264 def __check_eager_loader(loader):
265 if not hasattr(loader, 'exec_module'):
266 raise TypeError('loader must define exec_module()')
Brett Cannona04dbe42014-04-04 13:53:38 -0400267
268 @classmethod
269 def factory(cls, loader):
270 """Construct a callable which returns the eager loader made lazy."""
271 cls.__check_eager_loader(loader)
272 return lambda *args, **kwargs: cls(loader(*args, **kwargs))
273
274 def __init__(self, loader):
275 self.__check_eager_loader(loader)
276 self.loader = loader
277
278 def create_module(self, spec):
Brett Cannon696c35e2016-06-25 10:58:17 -0700279 return self.loader.create_module(spec)
Brett Cannona04dbe42014-04-04 13:53:38 -0400280
281 def exec_module(self, module):
282 """Make the module load lazily."""
283 module.__spec__.loader = self.loader
284 module.__loader__ = self.loader
285 # Don't need to worry about deep-copying as trying to set an attribute
286 # on an object would have triggered the load,
287 # e.g. ``module.__spec__.loader = None`` would trigger a load from
288 # trying to access module.__spec__.
Brett Cannon696c35e2016-06-25 10:58:17 -0700289 loader_state = {}
290 loader_state['__dict__'] = module.__dict__.copy()
291 loader_state['__class__'] = module.__class__
292 module.__spec__.loader_state = loader_state
Brett Cannona04dbe42014-04-04 13:53:38 -0400293 module.__class__ = _LazyModule