|
1 '''Demand load modules when used, not when imported.''' |
|
2 |
|
3 __author__ = '''Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>. |
|
4 This software may be used and distributed according to the terms |
|
5 of the GNU General Public License, incorporated herein by reference.''' |
|
6 |
|
7 # this is based on matt's original demandload module. it is a |
|
8 # complete rewrite. some time, we may need to support syntax of |
|
9 # "import foo as bar". |
|
10 |
|
11 class _importer(object): |
|
12 '''import a module. it is not imported until needed, and is |
|
13 imported at most once per scope.''' |
|
14 |
|
15 def __init__(self, scope, modname, fromlist): |
|
16 '''scope is context (globals() or locals()) in which import |
|
17 should be made. modname is name of module to import. |
|
18 fromlist is list of modules for "from foo import ..." |
|
19 emulation.''' |
|
20 |
|
21 self.scope = scope |
|
22 self.modname = modname |
|
23 self.fromlist = fromlist |
|
24 self.mod = None |
|
25 |
|
26 def module(self): |
|
27 '''import the module if needed, and return.''' |
|
28 if self.mod is None: |
|
29 self.mod = __import__(self.modname, self.scope, self.scope, |
|
30 self.fromlist) |
|
31 del self.modname, self.fromlist |
|
32 return self.mod |
|
33 |
|
34 class _replacer(object): |
|
35 '''placeholder for a demand loaded module. demandload puts this in |
|
36 a target scope. when an attribute of this object is looked up, |
|
37 this object is replaced in the target scope with the actual |
|
38 module. |
|
39 |
|
40 we use __getattribute__ to avoid namespace clashes between |
|
41 placeholder object and real module.''' |
|
42 |
|
43 def __init__(self, importer, target): |
|
44 self.importer = importer |
|
45 self.target = target |
|
46 # consider case where we do this: |
|
47 # demandload(globals(), 'foo.bar foo.quux') |
|
48 # foo will already exist in target scope when we get to |
|
49 # foo.quux. so we remember that we will need to demandload |
|
50 # quux into foo's scope when we really load it. |
|
51 self.later = [] |
|
52 |
|
53 def module(self): |
|
54 return object.__getattribute__(self, 'importer').module() |
|
55 |
|
56 def __getattribute__(self, key): |
|
57 '''look up an attribute in a module and return it. replace the |
|
58 name of the module in the caller\'s dict with the actual |
|
59 module.''' |
|
60 |
|
61 module = object.__getattribute__(self, 'module')() |
|
62 target = object.__getattribute__(self, 'target') |
|
63 importer = object.__getattribute__(self, 'importer') |
|
64 later = object.__getattribute__(self, 'later') |
|
65 |
|
66 if later: |
|
67 demandload(module.__dict__, ' '.join(later)) |
|
68 |
|
69 importer.scope[target] = module |
|
70 |
|
71 return getattr(module, key) |
|
72 |
|
73 class _replacer_from(_replacer): |
|
74 '''placeholder for a demand loaded module. used for "from foo |
|
75 import ..." emulation. semantics of this are different than |
|
76 regular import, so different implementation needed.''' |
|
77 |
|
78 def module(self): |
|
79 importer = object.__getattribute__(self, 'importer') |
|
80 target = object.__getattribute__(self, 'target') |
|
81 |
|
82 return getattr(importer.module(), target) |
|
83 |
1 def demandload(scope, modules): |
84 def demandload(scope, modules): |
2 class d: |
85 '''import modules into scope when each is first used. |
3 def __getattr__(self, name): |
|
4 mod = self.__dict__["mod"] |
|
5 scope = self.__dict__["scope"] |
|
6 scope[mod] = __import__(mod, scope, scope, []) |
|
7 return getattr(scope[mod], name) |
|
8 |
86 |
9 for m in modules.split(): |
87 scope should be the value of globals() in the module calling this |
10 dl = d() |
88 function, or locals() in the calling function. |
11 dl.mod = m |
|
12 dl.scope = scope |
|
13 scope[m] = dl |
|
14 |
89 |
|
90 modules is a string listing module names, separated by white |
|
91 space. names are handled like this: |
15 |
92 |
|
93 foo import foo |
|
94 foo bar import foo, bar |
|
95 foo.bar import foo.bar |
|
96 foo:bar from foo import bar |
|
97 foo:bar,quux from foo import bar, quux |
|
98 foo.bar:quux from foo.bar import quux''' |
|
99 |
|
100 for mod in modules.split(): |
|
101 col = mod.find(':') |
|
102 if col >= 0: |
|
103 fromlist = mod[col+1:].split(',') |
|
104 mod = mod[:col] |
|
105 else: |
|
106 fromlist = [] |
|
107 importer = _importer(scope, mod, fromlist) |
|
108 if fromlist: |
|
109 for name in fromlist: |
|
110 scope[name] = _replacer_from(importer, name) |
|
111 else: |
|
112 dot = mod.find('.') |
|
113 if dot >= 0: |
|
114 basemod = mod[:dot] |
|
115 val = scope.get(basemod) |
|
116 # if base module has already been demandload()ed, |
|
117 # remember to load this submodule into its namespace |
|
118 # when needed. |
|
119 if isinstance(val, _replacer): |
|
120 later = object.__getattribute__(val, 'later') |
|
121 later.append(mod[dot+1:]) |
|
122 continue |
|
123 else: |
|
124 basemod = mod |
|
125 scope[basemod] = _replacer(importer, basemod) |