blob: e50ef6dc2733906cf9503d7e6a263522dc2a00a9 [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 Cannon05a647d2013-06-14 19:02:34 -04003from ._bootstrap import MAGIC_NUMBER
Brett Cannona3c96152013-06-14 22:26:30 -04004from ._bootstrap import cache_from_source
Brett Cannonf24fecd2013-06-16 18:37:53 -04005from ._bootstrap import decode_source
Brett Cannona3c96152013-06-14 22:26:30 -04006from ._bootstrap import source_from_cache
Eric Snowb523f842013-11-22 09:05:39 -07007from ._bootstrap import spec_from_loader
8from ._bootstrap import spec_from_file_location
Brett Cannond200bf52012-05-13 13:45:09 -04009from ._bootstrap import _resolve_name
Eric Snow6029e082014-01-25 15:32:46 -070010from ._bootstrap import _find_spec
Brett Cannond200bf52012-05-13 13:45:09 -040011
Eric Snowb523f842013-11-22 09:05:39 -070012from contextlib import contextmanager
Brett Cannon0dbb4c72013-05-31 18:56:47 -040013import functools
Eric Snowb523f842013-11-22 09:05:39 -070014import sys
Brett Cannona04dbe42014-04-04 13:53:38 -040015import types
Brett Cannon0dbb4c72013-05-31 18:56:47 -040016import warnings
17
Brett Cannond200bf52012-05-13 13:45:09 -040018
19def resolve_name(name, package):
20 """Resolve a relative module name to an absolute one."""
21 if not name.startswith('.'):
22 return name
23 elif not package:
24 raise ValueError('{!r} is not a relative name '
25 '(no leading dot)'.format(name))
26 level = 0
27 for character in name:
28 if character != '.':
29 break
30 level += 1
31 return _resolve_name(name[level:], package, level)
Brett Cannon0dbb4c72013-05-31 18:56:47 -040032
33
Eric Snow6029e082014-01-25 15:32:46 -070034def _find_spec_from_path(name, path=None):
35 """Return the spec for the specified module.
36
37 First, sys.modules is checked to see if the module was already imported. If
38 so, then sys.modules[name].__spec__ is returned. If that happens to be
39 set to None, then ValueError is raised. If the module is not in
40 sys.modules, then sys.meta_path is searched for a suitable spec with the
41 value of 'path' given to the finders. None is returned if no spec could
42 be found.
43
44 Dotted names do not have their parent packages implicitly imported. You will
45 most likely need to explicitly import all parent packages in the proper
46 order for a submodule to get the correct spec.
47
48 """
49 if name not in sys.modules:
50 return _find_spec(name, path)
51 else:
52 module = sys.modules[name]
53 if module is None:
54 return None
55 try:
56 spec = module.__spec__
57 except AttributeError:
58 raise ValueError('{}.__spec__ is not set'.format(name))
59 else:
60 if spec is None:
61 raise ValueError('{}.__spec__ is None'.format(name))
62 return spec
63
64
65def find_spec(name, package=None):
66 """Return the spec for the specified module.
67
68 First, sys.modules is checked to see if the module was already imported. If
69 so, then sys.modules[name].__spec__ is returned. If that happens to be
70 set to None, then ValueError is raised. If the module is not in
71 sys.modules, then sys.meta_path is searched for a suitable spec with the
72 value of 'path' given to the finders. None is returned if no spec could
73 be found.
74
75 If the name is for submodule (contains a dot), the parent module is
76 automatically imported.
77
78 The name and package arguments work the same as importlib.import_module().
79 In other words, relative module names (with leading dots) work.
80
81 """
82 fullname = resolve_name(name, package) if name.startswith('.') else name
83 if fullname not in sys.modules:
84 parent_name = fullname.rpartition('.')[0]
85 if parent_name:
86 # Use builtins.__import__() in case someone replaced it.
87 parent = __import__(parent_name, fromlist=['__path__'])
88 return _find_spec(fullname, parent.__path__)
89 else:
90 return _find_spec(fullname, None)
91 else:
92 module = sys.modules[fullname]
93 if module is None:
94 return None
95 try:
96 spec = module.__spec__
97 except AttributeError:
98 raise ValueError('{}.__spec__ is not set'.format(name))
99 else:
100 if spec is None:
101 raise ValueError('{}.__spec__ is None'.format(name))
102 return spec
103
104
Eric Snowb523f842013-11-22 09:05:39 -0700105@contextmanager
106def _module_to_load(name):
107 is_reload = name in sys.modules
108
109 module = sys.modules.get(name)
110 if not is_reload:
111 # This must be done before open() is called as the 'io' module
112 # implicitly imports 'locale' and would otherwise trigger an
113 # infinite loop.
114 module = type(sys)(name)
115 # This must be done before putting the module in sys.modules
116 # (otherwise an optimization shortcut in import.c becomes wrong)
117 module.__initializing__ = True
118 sys.modules[name] = module
119 try:
120 yield module
121 except Exception:
122 if not is_reload:
123 try:
124 del sys.modules[name]
125 except KeyError:
126 pass
127 finally:
128 module.__initializing__ = False
129
130
Eric Snowb523f842013-11-22 09:05:39 -0700131def set_package(fxn):
Eric Snow1500d492014-01-06 20:49:04 -0700132 """Set __package__ on the returned module.
133
134 This function is deprecated.
135
136 """
Eric Snowb523f842013-11-22 09:05:39 -0700137 @functools.wraps(fxn)
138 def set_package_wrapper(*args, **kwargs):
Eric Snow1500d492014-01-06 20:49:04 -0700139 warnings.warn('The import system now takes care of this automatically.',
140 DeprecationWarning, stacklevel=2)
Eric Snowb523f842013-11-22 09:05:39 -0700141 module = fxn(*args, **kwargs)
142 if getattr(module, '__package__', None) is None:
143 module.__package__ = module.__name__
144 if not hasattr(module, '__path__'):
145 module.__package__ = module.__package__.rpartition('.')[0]
146 return module
147 return set_package_wrapper
148
149
Eric Snowb523f842013-11-22 09:05:39 -0700150def set_loader(fxn):
Eric Snow1500d492014-01-06 20:49:04 -0700151 """Set __loader__ on the returned module.
152
153 This function is deprecated.
154
155 """
Eric Snowb523f842013-11-22 09:05:39 -0700156 @functools.wraps(fxn)
157 def set_loader_wrapper(self, *args, **kwargs):
Eric Snow1500d492014-01-06 20:49:04 -0700158 warnings.warn('The import system now takes care of this automatically.',
159 DeprecationWarning, stacklevel=2)
Eric Snowb523f842013-11-22 09:05:39 -0700160 module = fxn(self, *args, **kwargs)
161 if getattr(module, '__loader__', None) is None:
162 module.__loader__ = self
163 return module
164 return set_loader_wrapper
165
166
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400167def module_for_loader(fxn):
168 """Decorator to handle selecting the proper module for loaders.
169
170 The decorated function is passed the module to use instead of the module
171 name. The module passed in to the function is either from sys.modules if
172 it already exists or is a new module. If the module is new, then __name__
173 is set the first argument to the method, __loader__ is set to self, and
174 __package__ is set accordingly (if self.is_package() is defined) will be set
175 before it is passed to the decorated function (if self.is_package() does
176 not work for the module it will be set post-load).
177
178 If an exception is raised and the decorator created the module it is
179 subsequently removed from sys.modules.
180
181 The decorator assumes that the decorated function takes the module name as
182 the second argument.
183
184 """
Eric Snowb523f842013-11-22 09:05:39 -0700185 warnings.warn('The import system now takes care of this automatically.',
Eric Snow1500d492014-01-06 20:49:04 -0700186 DeprecationWarning, stacklevel=2)
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400187 @functools.wraps(fxn)
188 def module_for_loader_wrapper(self, fullname, *args, **kwargs):
Eric Snowb523f842013-11-22 09:05:39 -0700189 with _module_to_load(fullname) as module:
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400190 module.__loader__ = self
191 try:
192 is_package = self.is_package(fullname)
193 except (ImportError, AttributeError):
194 pass
195 else:
196 if is_package:
197 module.__package__ = fullname
198 else:
199 module.__package__ = fullname.rpartition('.')[0]
200 # If __package__ was not set above, __import__() will do it later.
201 return fxn(self, module, *args, **kwargs)
202
Brett Cannon0e75c062013-05-31 18:57:45 -0400203 return module_for_loader_wrapper
Brett Cannona04dbe42014-04-04 13:53:38 -0400204
205
206class _Module(types.ModuleType):
207
208 """A subclass of the module type to allow __class__ manipulation."""
209
210
211class _LazyModule(types.ModuleType):
212
213 """A subclass of the module type which triggers loading upon attribute access."""
214
215 def __getattribute__(self, attr):
216 """Trigger the load of the module and return the attribute."""
217 # All module metadata must be garnered from __spec__ in order to avoid
218 # using mutated values.
219 # Stop triggering this method.
220 self.__class__ = _Module
221 # Get the original name to make sure no object substitution occurred
222 # in sys.modules.
223 original_name = self.__spec__.name
224 # Figure out exactly what attributes were mutated between the creation
225 # of the module and now.
226 attrs_then = self.__spec__.loader_state
227 attrs_now = self.__dict__
228 attrs_updated = {}
229 for key, value in attrs_now.items():
230 # Code that set the attribute may have kept a reference to the
231 # assigned object, making identity more important than equality.
232 if key not in attrs_then:
233 attrs_updated[key] = value
234 elif id(attrs_now[key]) != id(attrs_then[key]):
235 attrs_updated[key] = value
236 self.__spec__.loader.exec_module(self)
237 # If exec_module() was used directly there is no guarantee the module
238 # object was put into sys.modules.
239 if original_name in sys.modules:
240 if id(self) != id(sys.modules[original_name]):
241 msg = ('module object for {!r} substituted in sys.modules '
242 'during a lazy load')
243 raise ValueError(msg.format(original_name))
244 # Update after loading since that's what would happen in an eager
245 # loading situation.
246 self.__dict__.update(attrs_updated)
247 return getattr(self, attr)
248
249 def __delattr__(self, attr):
250 """Trigger the load and then perform the deletion."""
251 # To trigger the load and raise an exception if the attribute
252 # doesn't exist.
253 self.__getattribute__(attr)
254 delattr(self, attr)
255
256
257class LazyLoader(abc.Loader):
258
259 """A loader that creates a module which defers loading until attribute access."""
260
261 @staticmethod
262 def __check_eager_loader(loader):
263 if not hasattr(loader, 'exec_module'):
264 raise TypeError('loader must define exec_module()')
265 elif hasattr(loader.__class__, 'create_module'):
266 if abc.Loader.create_module != loader.__class__.create_module:
267 # Only care if create_module() is overridden in a subclass of
268 # importlib.abc.Loader.
269 raise TypeError('loader cannot define create_module()')
270
271 @classmethod
272 def factory(cls, loader):
273 """Construct a callable which returns the eager loader made lazy."""
274 cls.__check_eager_loader(loader)
275 return lambda *args, **kwargs: cls(loader(*args, **kwargs))
276
277 def __init__(self, loader):
278 self.__check_eager_loader(loader)
279 self.loader = loader
280
281 def create_module(self, spec):
282 """Create a module which can have its __class__ manipulated."""
283 return _Module(spec.name)
284
285 def exec_module(self, module):
286 """Make the module load lazily."""
287 module.__spec__.loader = self.loader
288 module.__loader__ = self.loader
289 # Don't need to worry about deep-copying as trying to set an attribute
290 # on an object would have triggered the load,
291 # e.g. ``module.__spec__.loader = None`` would trigger a load from
292 # trying to access module.__spec__.
293 module.__spec__.loader_state = module.__dict__.copy()
294 module.__class__ = _LazyModule