35 contextmanager = contextlib.contextmanager |
35 contextmanager = contextlib.contextmanager |
36 |
36 |
37 _origimport = __import__ |
37 _origimport = __import__ |
38 |
38 |
39 nothing = object() |
39 nothing = object() |
|
40 |
40 |
41 |
41 def _hgextimport(importfunc, name, globals, *args, **kwargs): |
42 def _hgextimport(importfunc, name, globals, *args, **kwargs): |
42 try: |
43 try: |
43 return importfunc(name, globals, *args, **kwargs) |
44 return importfunc(name, globals, *args, **kwargs) |
44 except ImportError: |
45 except ImportError: |
51 if nameroot != contextroot: |
52 if nameroot != contextroot: |
52 raise |
53 raise |
53 # retry to import with "hgext_" prefix |
54 # retry to import with "hgext_" prefix |
54 return importfunc(hgextname, globals, *args, **kwargs) |
55 return importfunc(hgextname, globals, *args, **kwargs) |
55 |
56 |
|
57 |
56 class _demandmod(object): |
58 class _demandmod(object): |
57 """module demand-loader and proxy |
59 """module demand-loader and proxy |
58 |
60 |
59 Specify 1 as 'level' argument at construction, to import module |
61 Specify 1 as 'level' argument at construction, to import module |
60 relatively. |
62 relatively. |
65 head, rest = name.split('.', 1) |
67 head, rest = name.split('.', 1) |
66 after = [rest] |
68 after = [rest] |
67 else: |
69 else: |
68 head = name |
70 head = name |
69 after = [] |
71 after = [] |
70 object.__setattr__(self, r"_data", |
72 object.__setattr__( |
71 (head, globals, locals, after, level, set())) |
73 self, r"_data", (head, globals, locals, after, level, set()) |
|
74 ) |
72 object.__setattr__(self, r"_module", None) |
75 object.__setattr__(self, r"_module", None) |
73 |
76 |
74 def _extend(self, name): |
77 def _extend(self, name): |
75 """add to the list of submodules to load""" |
78 """add to the list of submodules to load""" |
76 self._data[3].append(name) |
79 self._data[3].append(name) |
89 def _load(self): |
92 def _load(self): |
90 if not self._module: |
93 if not self._module: |
91 with tracing.log('demandimport %s', self._data[0]): |
94 with tracing.log('demandimport %s', self._data[0]): |
92 head, globals, locals, after, level, modrefs = self._data |
95 head, globals, locals, after, level, modrefs = self._data |
93 mod = _hgextimport( |
96 mod = _hgextimport( |
94 _origimport, head, globals, locals, None, level) |
97 _origimport, head, globals, locals, None, level |
|
98 ) |
95 if mod is self: |
99 if mod is self: |
96 # In this case, _hgextimport() above should imply |
100 # In this case, _hgextimport() above should imply |
97 # _demandimport(). Otherwise, _hgextimport() never |
101 # _demandimport(). Otherwise, _hgextimport() never |
98 # returns _demandmod. This isn't intentional behavior, |
102 # returns _demandmod. This isn't intentional behavior, |
99 # in fact. (see also issue5304 for detail) |
103 # in fact. (see also issue5304 for detail) |
113 def subload(mod, p): |
117 def subload(mod, p): |
114 h, t = p, None |
118 h, t = p, None |
115 if '.' in p: |
119 if '.' in p: |
116 h, t = p.split('.', 1) |
120 h, t = p.split('.', 1) |
117 if getattr(mod, h, nothing) is nothing: |
121 if getattr(mod, h, nothing) is nothing: |
118 setattr(mod, h, _demandmod( |
122 setattr( |
119 p, mod.__dict__, mod.__dict__, level=1)) |
123 mod, |
|
124 h, |
|
125 _demandmod(p, mod.__dict__, mod.__dict__, level=1), |
|
126 ) |
120 elif t: |
127 elif t: |
121 subload(getattr(mod, h), t) |
128 subload(getattr(mod, h), t) |
122 |
129 |
123 for x in after: |
130 for x in after: |
124 subload(mod, x) |
131 subload(mod, x) |
162 @property |
169 @property |
163 def __doc__(self): |
170 def __doc__(self): |
164 self._load() |
171 self._load() |
165 return self._module.__doc__ |
172 return self._module.__doc__ |
166 |
173 |
|
174 |
167 _pypy = '__pypy__' in sys.builtin_module_names |
175 _pypy = '__pypy__' in sys.builtin_module_names |
|
176 |
168 |
177 |
169 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1): |
178 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1): |
170 if locals is None or name in ignores or fromlist == ('*',): |
179 if locals is None or name in ignores or fromlist == ('*',): |
171 # these cases we can't really delay |
180 # these cases we can't really delay |
172 return _hgextimport(_origimport, name, globals, locals, fromlist, level) |
181 return _hgextimport(_origimport, name, globals, locals, fromlist, level) |
173 elif not fromlist: |
182 elif not fromlist: |
174 # import a [as b] |
183 # import a [as b] |
175 if '.' in name: # a.b |
184 if '.' in name: # a.b |
176 base, rest = name.split('.', 1) |
185 base, rest = name.split('.', 1) |
177 # email.__init__ loading email.mime |
186 # email.__init__ loading email.mime |
178 if globals and globals.get('__name__', None) == base: |
187 if globals and globals.get('__name__', None) == base: |
179 return _origimport(name, globals, locals, fromlist, level) |
188 return _origimport(name, globals, locals, fromlist, level) |
180 # if a is already demand-loaded, add b to its submodule list |
189 # if a is already demand-loaded, add b to its submodule list |
242 return mod |
251 return mod |
243 |
252 |
244 if level >= 0: |
253 if level >= 0: |
245 if name: |
254 if name: |
246 # "from a import b" or "from .a import b" style |
255 # "from a import b" or "from .a import b" style |
247 rootmod = _hgextimport(_origimport, name, globals, locals, |
256 rootmod = _hgextimport( |
248 level=level) |
257 _origimport, name, globals, locals, level=level |
|
258 ) |
249 mod = chainmodules(rootmod, name) |
259 mod = chainmodules(rootmod, name) |
250 elif _pypy: |
260 elif _pypy: |
251 # PyPy's __import__ throws an exception if invoked |
261 # PyPy's __import__ throws an exception if invoked |
252 # with an empty name and no fromlist. Recreate the |
262 # with an empty name and no fromlist. Recreate the |
253 # desired behaviour by hand. |
263 # desired behaviour by hand. |
258 mod = sys.modules[mn] |
268 mod = sys.modules[mn] |
259 if level > 1: |
269 if level > 1: |
260 mn = mn.rsplit('.', level - 1)[0] |
270 mn = mn.rsplit('.', level - 1)[0] |
261 mod = sys.modules[mn] |
271 mod = sys.modules[mn] |
262 else: |
272 else: |
263 mod = _hgextimport(_origimport, name, globals, locals, |
273 mod = _hgextimport( |
264 level=level) |
274 _origimport, name, globals, locals, level=level |
|
275 ) |
265 |
276 |
266 for x in fromlist: |
277 for x in fromlist: |
267 processfromitem(mod, x) |
278 processfromitem(mod, x) |
268 |
279 |
269 return mod |
280 return mod |
276 for x in fromlist: |
287 for x in fromlist: |
277 processfromitem(mod, x) |
288 processfromitem(mod, x) |
278 |
289 |
279 return mod |
290 return mod |
280 |
291 |
|
292 |
281 ignores = set() |
293 ignores = set() |
|
294 |
282 |
295 |
283 def init(ignoreset): |
296 def init(ignoreset): |
284 global ignores |
297 global ignores |
285 ignores = ignoreset |
298 ignores = ignoreset |
286 |
299 |
|
300 |
287 def isenabled(): |
301 def isenabled(): |
288 return builtins.__import__ == _demandimport |
302 return builtins.__import__ == _demandimport |
|
303 |
289 |
304 |
290 def enable(): |
305 def enable(): |
291 "enable global demand-loading of modules" |
306 "enable global demand-loading of modules" |
292 builtins.__import__ = _demandimport |
307 builtins.__import__ = _demandimport |
293 |
308 |
|
309 |
294 def disable(): |
310 def disable(): |
295 "disable global demand-loading of modules" |
311 "disable global demand-loading of modules" |
296 builtins.__import__ = _origimport |
312 builtins.__import__ = _origimport |
|
313 |
297 |
314 |
298 @contextmanager |
315 @contextmanager |
299 def deactivated(): |
316 def deactivated(): |
300 "context manager for disabling demandimport in 'with' blocks" |
317 "context manager for disabling demandimport in 'with' blocks" |
301 demandenabled = isenabled() |
318 demandenabled = isenabled() |