|
1 # demandimport.py - global demand-loading of modules for Mercurial |
|
2 # |
|
3 # Copyright 2006 Matt Mackall <mpm@selenic.com> |
|
4 # |
|
5 # This software may be used and distributed according to the terms |
|
6 # of the GNU General Public License, incorporated herein by reference. |
|
7 |
|
8 ''' |
|
9 demandimport - automatic demandloading of modules |
|
10 |
|
11 To enable this module, do: |
|
12 |
|
13 import demandimport; demandimport.enable() |
|
14 |
|
15 Imports of the following forms will be demand-loaded: |
|
16 |
|
17 import a, b.c |
|
18 import a.b as c |
|
19 from a import b,c # a will be loaded immediately |
|
20 |
|
21 These imports will not be delayed: |
|
22 |
|
23 from a import * |
|
24 b = __import__(a) |
|
25 ''' |
|
26 |
|
27 _origimport = __import__ |
|
28 |
|
29 class _demandmod(object): |
|
30 """module demand-loader and proxy""" |
|
31 def __init__(self, name, globals, locals): |
|
32 if '.' in name: |
|
33 head, rest = name.split('.', 1) |
|
34 after = [rest] |
|
35 else: |
|
36 head = name |
|
37 after = [] |
|
38 self.__dict__["_data"] = (head, globals, locals, after) |
|
39 self.__dict__["_module"] = None |
|
40 def _extend(self, name): |
|
41 """add to the list of submodules to load""" |
|
42 self._data[3].append(name) |
|
43 def _load(self): |
|
44 if not self._module: |
|
45 head, globals, locals, after = self._data |
|
46 mod = _origimport(head, globals, locals) |
|
47 # load submodules |
|
48 for x in after: |
|
49 hx = x |
|
50 if '.' in x: |
|
51 hx = x.split('.')[0] |
|
52 if not hasattr(mod, hx): |
|
53 setattr(mod, hx, _demandmod(x, mod.__dict__, mod.__dict__)) |
|
54 # are we in the locals dictionary still? |
|
55 if locals and locals.get(head) == self: |
|
56 locals[head] = mod |
|
57 self.__dict__["_module"] = mod |
|
58 def __repr__(self): |
|
59 return "<unloaded module '%s'>" % self._data[0] |
|
60 def __call__(self, *args, **kwargs): |
|
61 raise TypeError("'unloaded module' object is not callable") |
|
62 def __getattr__(self, attr): |
|
63 self._load() |
|
64 return getattr(self._module, attr) |
|
65 def __setattr__(self, attr, val): |
|
66 self._load() |
|
67 setattr(self._module, attr, val) |
|
68 |
|
69 def _demandimport(name, globals=None, locals=None, fromlist=None): |
|
70 if not locals or name in ignore or fromlist == ('*',): |
|
71 # these cases we can't really delay |
|
72 return _origimport(name, globals, locals, fromlist) |
|
73 elif not fromlist: |
|
74 # import a [as b] |
|
75 if '.' in name: # a.b |
|
76 base, rest = name.split('.', 1) |
|
77 # if a is already demand-loaded, add b to its submodule list |
|
78 if base in locals: |
|
79 if isinstance(locals[base], _demandmod): |
|
80 locals[base]._extend(rest) |
|
81 return locals[base] |
|
82 return _demandmod(name, globals, locals) |
|
83 else: |
|
84 # from a import b,c,d |
|
85 mod = _origimport(name, globals, locals) |
|
86 # recurse down the module chain |
|
87 for comp in name.split('.')[1:]: |
|
88 mod = getattr(mod, comp) |
|
89 for x in fromlist: |
|
90 # set requested submodules for demand load |
|
91 if not(hasattr(mod, x)): |
|
92 setattr(mod, x, _demandmod(x, mod.__dict__, mod.__dict__)) |
|
93 return mod |
|
94 |
|
95 ignore = [] |
|
96 |
|
97 def enable(): |
|
98 "enable global demand-loading of modules" |
|
99 __builtins__["__import__"] = _demandimport |
|
100 |
|
101 def disable(): |
|
102 "disable global demand-loading of modules" |
|
103 __builtins__["__import__"] = _origimport |
|
104 |