Mercurial > hg > mercurial-crew-with-dirclash
comparison mercurial/demandload.py @ 1826:f3abe0bdccdd
rewrite demandload module to be more flexible.
performance not affected.
old demandload module could only import top-level modules like os and
sys.
with rewrite, all of following syntax is emulated perfectly:
import foo, bar foo bar
import foo.bar.quux foo.bar.quux
from foo.bar import quux, blurfl foo.bar:quux,blurfl
only thing missing is "import foo as bar".
also, namespace clashes between placeholder object and demand loaded
module are no longer possible.
author | Vadim Gelfer <vadim.gelger@gmail.com> |
---|---|
date | Thu, 02 Mar 2006 11:38:03 -0800 |
parents | 3db700146536 |
children | d01eac5968c6 |
comparison
equal
deleted
inserted
replaced
1819:d657bfdc06c4 | 1826:f3abe0bdccdd |
---|---|
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) |