##// END OF EJS Templates
demandimport: insert empty line per method...
Yuya Nishihara -
r32446:63365e96 default
parent child Browse files
Show More
@@ -1,297 +1,302 b''
1 1 # demandimport.py - global demand-loading of modules for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''
9 9 demandimport - automatic demandloading of modules
10 10
11 11 To enable this module, do:
12 12
13 13 import demandimport; demandimport.enable()
14 14
15 15 Imports of the following forms will be demand-loaded:
16 16
17 17 import a, b.c
18 18 import a.b as c
19 19 from a import b,c # a will be loaded immediately
20 20
21 21 These imports will not be delayed:
22 22
23 23 from a import *
24 24 b = __import__(a)
25 25 '''
26 26
27 27 from __future__ import absolute_import
28 28
29 29 import contextlib
30 30 import os
31 31 import sys
32 32
33 33 # __builtin__ in Python 2, builtins in Python 3.
34 34 try:
35 35 import __builtin__ as builtins
36 36 except ImportError:
37 37 import builtins
38 38
39 39 contextmanager = contextlib.contextmanager
40 40
41 41 _origimport = __import__
42 42
43 43 nothing = object()
44 44
45 45 # Python 3 doesn't have relative imports nor level -1.
46 46 level = -1
47 47 if sys.version_info[0] >= 3:
48 48 level = 0
49 49 _import = _origimport
50 50
51 51 def _hgextimport(importfunc, name, globals, *args, **kwargs):
52 52 try:
53 53 return importfunc(name, globals, *args, **kwargs)
54 54 except ImportError:
55 55 if not globals:
56 56 raise
57 57 # extensions are loaded with "hgext_" prefix
58 58 hgextname = 'hgext_%s' % name
59 59 nameroot = hgextname.split('.', 1)[0]
60 60 contextroot = globals.get('__name__', '').split('.', 1)[0]
61 61 if nameroot != contextroot:
62 62 raise
63 63 # retry to import with "hgext_" prefix
64 64 return importfunc(hgextname, globals, *args, **kwargs)
65 65
66 66 class _demandmod(object):
67 67 """module demand-loader and proxy
68 68
69 69 Specify 1 as 'level' argument at construction, to import module
70 70 relatively.
71 71 """
72
72 73 def __init__(self, name, globals, locals, level):
73 74 if '.' in name:
74 75 head, rest = name.split('.', 1)
75 76 after = [rest]
76 77 else:
77 78 head = name
78 79 after = []
79 80 object.__setattr__(self, r"_data",
80 81 (head, globals, locals, after, level, set()))
81 82 object.__setattr__(self, r"_module", None)
83
82 84 def _extend(self, name):
83 85 """add to the list of submodules to load"""
84 86 self._data[3].append(name)
85 87
86 88 def _addref(self, name):
87 89 """Record that the named module ``name`` imports this module.
88 90
89 91 References to this proxy class having the name of this module will be
90 92 replaced at module load time. We assume the symbol inside the importing
91 93 module is identical to the "head" name of this module. We don't
92 94 actually know if "as X" syntax is being used to change the symbol name
93 95 because this information isn't exposed to __import__.
94 96 """
95 97 self._data[5].add(name)
96 98
97 99 def _load(self):
98 100 if not self._module:
99 101 head, globals, locals, after, level, modrefs = self._data
100 102 mod = _hgextimport(_import, head, globals, locals, None, level)
101 103 if mod is self:
102 104 # In this case, _hgextimport() above should imply
103 105 # _demandimport(). Otherwise, _hgextimport() never
104 106 # returns _demandmod. This isn't intentional behavior,
105 107 # in fact. (see also issue5304 for detail)
106 108 #
107 109 # If self._module is already bound at this point, self
108 110 # should be already _load()-ed while _hgextimport().
109 111 # Otherwise, there is no way to import actual module
110 112 # as expected, because (re-)invoking _hgextimport()
111 113 # should cause same result.
112 114 # This is reason why _load() returns without any more
113 115 # setup but assumes self to be already bound.
114 116 mod = self._module
115 117 assert mod and mod is not self, "%s, %s" % (self, mod)
116 118 return
117 119
118 120 # load submodules
119 121 def subload(mod, p):
120 122 h, t = p, None
121 123 if '.' in p:
122 124 h, t = p.split('.', 1)
123 125 if getattr(mod, h, nothing) is nothing:
124 126 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__,
125 127 level=1))
126 128 elif t:
127 129 subload(getattr(mod, h), t)
128 130
129 131 for x in after:
130 132 subload(mod, x)
131 133
132 134 # Replace references to this proxy instance with the actual module.
133 135 if locals and locals.get(head) is self:
134 136 locals[head] = mod
135 137
136 138 for modname in modrefs:
137 139 modref = sys.modules.get(modname, None)
138 140 if modref and getattr(modref, head, None) is self:
139 141 setattr(modref, head, mod)
140 142
141 143 object.__setattr__(self, r"_module", mod)
142 144
143 145 def __repr__(self):
144 146 if self._module:
145 147 return "<proxied module '%s'>" % self._data[0]
146 148 return "<unloaded module '%s'>" % self._data[0]
149
147 150 def __call__(self, *args, **kwargs):
148 151 raise TypeError("%s object is not callable" % repr(self))
152
149 153 def __getattribute__(self, attr):
150 154 if attr in ('_data', '_extend', '_load', '_module', '_addref'):
151 155 return object.__getattribute__(self, attr)
152 156 self._load()
153 157 return getattr(self._module, attr)
158
154 159 def __setattr__(self, attr, val):
155 160 self._load()
156 161 setattr(self._module, attr, val)
157 162
158 163 _pypy = '__pypy__' in sys.builtin_module_names
159 164
160 165 def _demandimport(name, globals=None, locals=None, fromlist=None, level=level):
161 166 if locals is None or name in ignore or fromlist == ('*',):
162 167 # these cases we can't really delay
163 168 return _hgextimport(_import, name, globals, locals, fromlist, level)
164 169 elif not fromlist:
165 170 # import a [as b]
166 171 if '.' in name: # a.b
167 172 base, rest = name.split('.', 1)
168 173 # email.__init__ loading email.mime
169 174 if globals and globals.get('__name__', None) == base:
170 175 return _import(name, globals, locals, fromlist, level)
171 176 # if a is already demand-loaded, add b to its submodule list
172 177 if base in locals:
173 178 if isinstance(locals[base], _demandmod):
174 179 locals[base]._extend(rest)
175 180 return locals[base]
176 181 return _demandmod(name, globals, locals, level)
177 182 else:
178 183 # There is a fromlist.
179 184 # from a import b,c,d
180 185 # from . import b,c,d
181 186 # from .a import b,c,d
182 187
183 188 # level == -1: relative and absolute attempted (Python 2 only).
184 189 # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3).
185 190 # The modern Mercurial convention is to use absolute_import everywhere,
186 191 # so modern Mercurial code will have level >= 0.
187 192
188 193 # The name of the module the import statement is located in.
189 194 globalname = globals.get('__name__')
190 195
191 196 def processfromitem(mod, attr):
192 197 """Process an imported symbol in the import statement.
193 198
194 199 If the symbol doesn't exist in the parent module, and if the
195 200 parent module is a package, it must be a module. We set missing
196 201 modules up as _demandmod instances.
197 202 """
198 203 symbol = getattr(mod, attr, nothing)
199 204 nonpkg = getattr(mod, '__path__', nothing) is nothing
200 205 if symbol is nothing:
201 206 if nonpkg:
202 207 # do not try relative import, which would raise ValueError,
203 208 # and leave unknown attribute as the default __import__()
204 209 # would do. the missing attribute will be detected later
205 210 # while processing the import statement.
206 211 return
207 212 mn = '%s.%s' % (mod.__name__, attr)
208 213 if mn in ignore:
209 214 importfunc = _origimport
210 215 else:
211 216 importfunc = _demandmod
212 217 symbol = importfunc(attr, mod.__dict__, locals, level=1)
213 218 setattr(mod, attr, symbol)
214 219
215 220 # Record the importing module references this symbol so we can
216 221 # replace the symbol with the actual module instance at load
217 222 # time.
218 223 if globalname and isinstance(symbol, _demandmod):
219 224 symbol._addref(globalname)
220 225
221 226 def chainmodules(rootmod, modname):
222 227 # recurse down the module chain, and return the leaf module
223 228 mod = rootmod
224 229 for comp in modname.split('.')[1:]:
225 230 if getattr(mod, comp, nothing) is nothing:
226 231 setattr(mod, comp, _demandmod(comp, mod.__dict__,
227 232 mod.__dict__, level=1))
228 233 mod = getattr(mod, comp)
229 234 return mod
230 235
231 236 if level >= 0:
232 237 if name:
233 238 # "from a import b" or "from .a import b" style
234 239 rootmod = _hgextimport(_origimport, name, globals, locals,
235 240 level=level)
236 241 mod = chainmodules(rootmod, name)
237 242 elif _pypy:
238 243 # PyPy's __import__ throws an exception if invoked
239 244 # with an empty name and no fromlist. Recreate the
240 245 # desired behaviour by hand.
241 246 mn = globalname
242 247 mod = sys.modules[mn]
243 248 if getattr(mod, '__path__', nothing) is nothing:
244 249 mn = mn.rsplit('.', 1)[0]
245 250 mod = sys.modules[mn]
246 251 if level > 1:
247 252 mn = mn.rsplit('.', level - 1)[0]
248 253 mod = sys.modules[mn]
249 254 else:
250 255 mod = _hgextimport(_origimport, name, globals, locals,
251 256 level=level)
252 257
253 258 for x in fromlist:
254 259 processfromitem(mod, x)
255 260
256 261 return mod
257 262
258 263 # But, we still need to support lazy loading of standard library and 3rd
259 264 # party modules. So handle level == -1.
260 265 mod = _hgextimport(_origimport, name, globals, locals)
261 266 mod = chainmodules(mod, name)
262 267
263 268 for x in fromlist:
264 269 processfromitem(mod, x)
265 270
266 271 return mod
267 272
268 273 ignore = []
269 274
270 275 def init(ignorelist):
271 276 global ignore
272 277 ignore = ignorelist
273 278
274 279 def isenabled():
275 280 return builtins.__import__ == _demandimport
276 281
277 282 def enable():
278 283 "enable global demand-loading of modules"
279 284 if os.environ.get('HGDEMANDIMPORT') != 'disable':
280 285 builtins.__import__ = _demandimport
281 286
282 287 def disable():
283 288 "disable global demand-loading of modules"
284 289 builtins.__import__ = _origimport
285 290
286 291 @contextmanager
287 292 def deactivated():
288 293 "context manager for disabling demandimport in 'with' blocks"
289 294 demandenabled = isenabled()
290 295 if demandenabled:
291 296 disable()
292 297
293 298 try:
294 299 yield
295 300 finally:
296 301 if demandenabled:
297 302 enable()
General Comments 0
You need to be logged in to leave comments. Login now