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