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