##// END OF EJS Templates
demandimport: do not raise ImportError for unknown item in fromlist...
Yuya Nishihara -
r30647:1914db1b stable
parent child Browse files
Show More
@@ -1,327 +1,330 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, and if the
194 If the symbol doesn't exist in the parent module, and if the
195 parent module is a package, it must be a module. We set missing
195 parent module is a package, it must be a module. We set missing
196 modules up as _demandmod instances.
196 modules up as _demandmod instances.
197 """
197 """
198 symbol = getattr(mod, attr, nothing)
198 symbol = getattr(mod, attr, nothing)
199 nonpkg = getattr(mod, '__path__', nothing) is nothing
199 nonpkg = getattr(mod, '__path__', nothing) is nothing
200 if symbol is nothing:
200 if symbol is nothing:
201 if nonpkg:
201 if nonpkg:
202 # do not try relative import, which would raise ValueError
202 # do not try relative import, which would raise ValueError,
203 raise ImportError('cannot import name %s' % attr)
203 # and leave unknown attribute as the default __import__()
204 # would do. the missing attribute will be detected later
205 # while processing the import statement.
206 return
204 mn = '%s.%s' % (mod.__name__, attr)
207 mn = '%s.%s' % (mod.__name__, attr)
205 if mn in ignore:
208 if mn in ignore:
206 importfunc = _origimport
209 importfunc = _origimport
207 else:
210 else:
208 importfunc = _demandmod
211 importfunc = _demandmod
209 symbol = importfunc(attr, mod.__dict__, locals, level=1)
212 symbol = importfunc(attr, mod.__dict__, locals, level=1)
210 setattr(mod, attr, symbol)
213 setattr(mod, attr, symbol)
211
214
212 # Record the importing module references this symbol so we can
215 # Record the importing module references this symbol so we can
213 # replace the symbol with the actual module instance at load
216 # replace the symbol with the actual module instance at load
214 # time.
217 # time.
215 if globalname and isinstance(symbol, _demandmod):
218 if globalname and isinstance(symbol, _demandmod):
216 symbol._addref(globalname)
219 symbol._addref(globalname)
217
220
218 def chainmodules(rootmod, modname):
221 def chainmodules(rootmod, modname):
219 # recurse down the module chain, and return the leaf module
222 # recurse down the module chain, and return the leaf module
220 mod = rootmod
223 mod = rootmod
221 for comp in modname.split('.')[1:]:
224 for comp in modname.split('.')[1:]:
222 if getattr(mod, comp, nothing) is nothing:
225 if getattr(mod, comp, nothing) is nothing:
223 setattr(mod, comp, _demandmod(comp, mod.__dict__,
226 setattr(mod, comp, _demandmod(comp, mod.__dict__,
224 mod.__dict__, level=1))
227 mod.__dict__, level=1))
225 mod = getattr(mod, comp)
228 mod = getattr(mod, comp)
226 return mod
229 return mod
227
230
228 if level >= 0:
231 if level >= 0:
229 if name:
232 if name:
230 # "from a import b" or "from .a import b" style
233 # "from a import b" or "from .a import b" style
231 rootmod = _hgextimport(_origimport, name, globals, locals,
234 rootmod = _hgextimport(_origimport, name, globals, locals,
232 level=level)
235 level=level)
233 mod = chainmodules(rootmod, name)
236 mod = chainmodules(rootmod, name)
234 elif _pypy:
237 elif _pypy:
235 # PyPy's __import__ throws an exception if invoked
238 # PyPy's __import__ throws an exception if invoked
236 # with an empty name and no fromlist. Recreate the
239 # with an empty name and no fromlist. Recreate the
237 # desired behaviour by hand.
240 # desired behaviour by hand.
238 mn = globalname
241 mn = globalname
239 mod = sys.modules[mn]
242 mod = sys.modules[mn]
240 if getattr(mod, '__path__', nothing) is nothing:
243 if getattr(mod, '__path__', nothing) is nothing:
241 mn = mn.rsplit('.', 1)[0]
244 mn = mn.rsplit('.', 1)[0]
242 mod = sys.modules[mn]
245 mod = sys.modules[mn]
243 if level > 1:
246 if level > 1:
244 mn = mn.rsplit('.', level - 1)[0]
247 mn = mn.rsplit('.', level - 1)[0]
245 mod = sys.modules[mn]
248 mod = sys.modules[mn]
246 else:
249 else:
247 mod = _hgextimport(_origimport, name, globals, locals,
250 mod = _hgextimport(_origimport, name, globals, locals,
248 level=level)
251 level=level)
249
252
250 for x in fromlist:
253 for x in fromlist:
251 processfromitem(mod, x)
254 processfromitem(mod, x)
252
255
253 return mod
256 return mod
254
257
255 # But, we still need to support lazy loading of standard library and 3rd
258 # But, we still need to support lazy loading of standard library and 3rd
256 # party modules. So handle level == -1.
259 # party modules. So handle level == -1.
257 mod = _hgextimport(_origimport, name, globals, locals)
260 mod = _hgextimport(_origimport, name, globals, locals)
258 mod = chainmodules(mod, name)
261 mod = chainmodules(mod, name)
259
262
260 for x in fromlist:
263 for x in fromlist:
261 processfromitem(mod, x)
264 processfromitem(mod, x)
262
265
263 return mod
266 return mod
264
267
265 ignore = [
268 ignore = [
266 '__future__',
269 '__future__',
267 '_hashlib',
270 '_hashlib',
268 # ImportError during pkg_resources/__init__.py:fixup_namespace_package
271 # ImportError during pkg_resources/__init__.py:fixup_namespace_package
269 '_imp',
272 '_imp',
270 '_xmlplus',
273 '_xmlplus',
271 'fcntl',
274 'fcntl',
272 'nt', # pathlib2 tests the existence of built-in 'nt' module
275 'nt', # pathlib2 tests the existence of built-in 'nt' module
273 'win32com.gen_py',
276 'win32com.gen_py',
274 '_winreg', # 2.7 mimetypes needs immediate ImportError
277 '_winreg', # 2.7 mimetypes needs immediate ImportError
275 'pythoncom',
278 'pythoncom',
276 # imported by tarfile, not available under Windows
279 # imported by tarfile, not available under Windows
277 'pwd',
280 'pwd',
278 'grp',
281 'grp',
279 # imported by profile, itself imported by hotshot.stats,
282 # imported by profile, itself imported by hotshot.stats,
280 # not available under Windows
283 # not available under Windows
281 'resource',
284 'resource',
282 # this trips up many extension authors
285 # this trips up many extension authors
283 'gtk',
286 'gtk',
284 # setuptools' pkg_resources.py expects "from __main__ import x" to
287 # setuptools' pkg_resources.py expects "from __main__ import x" to
285 # raise ImportError if x not defined
288 # raise ImportError if x not defined
286 '__main__',
289 '__main__',
287 '_ssl', # conditional imports in the stdlib, issue1964
290 '_ssl', # conditional imports in the stdlib, issue1964
288 '_sre', # issue4920
291 '_sre', # issue4920
289 'rfc822',
292 'rfc822',
290 'mimetools',
293 'mimetools',
291 'sqlalchemy.events', # has import-time side effects (issue5085)
294 'sqlalchemy.events', # has import-time side effects (issue5085)
292 # setuptools 8 expects this module to explode early when not on windows
295 # setuptools 8 expects this module to explode early when not on windows
293 'distutils.msvc9compiler',
296 'distutils.msvc9compiler',
294 '__builtin__',
297 '__builtin__',
295 'builtins',
298 'builtins',
296 ]
299 ]
297
300
298 if _pypy:
301 if _pypy:
299 ignore.extend([
302 ignore.extend([
300 # _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5)
303 # _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5)
301 '_ctypes.pointer',
304 '_ctypes.pointer',
302 ])
305 ])
303
306
304 def isenabled():
307 def isenabled():
305 return builtins.__import__ == _demandimport
308 return builtins.__import__ == _demandimport
306
309
307 def enable():
310 def enable():
308 "enable global demand-loading of modules"
311 "enable global demand-loading of modules"
309 if os.environ.get('HGDEMANDIMPORT') != 'disable':
312 if os.environ.get('HGDEMANDIMPORT') != 'disable':
310 builtins.__import__ = _demandimport
313 builtins.__import__ = _demandimport
311
314
312 def disable():
315 def disable():
313 "disable global demand-loading of modules"
316 "disable global demand-loading of modules"
314 builtins.__import__ = _origimport
317 builtins.__import__ = _origimport
315
318
316 @contextmanager
319 @contextmanager
317 def deactivated():
320 def deactivated():
318 "context manager for disabling demandimport in 'with' blocks"
321 "context manager for disabling demandimport in 'with' blocks"
319 demandenabled = isenabled()
322 demandenabled = isenabled()
320 if demandenabled:
323 if demandenabled:
321 disable()
324 disable()
322
325
323 try:
326 try:
324 yield
327 yield
325 finally:
328 finally:
326 if demandenabled:
329 if demandenabled:
327 enable()
330 enable()
@@ -1,80 +1,89 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
66 import contextlib
67 print("contextlib =", f(contextlib))
67 print("contextlib =", f(contextlib))
68 try:
68 try:
69 from contextlib import unknownattr
69 from contextlib import unknownattr
70 print('no demandmod should be created for attribute of non-package '
70 print('no demandmod should be created for attribute of non-package '
71 'module:\ncontextlib.unknownattr =', f(unknownattr))
71 'module:\ncontextlib.unknownattr =', f(unknownattr))
72 except ImportError as inst:
72 except ImportError as inst:
73 print('contextlib.unknownattr = ImportError: %s' % inst)
73 print('contextlib.unknownattr = ImportError: %s'
74 % rsub(r"'", '', str(inst)))
75
76 # Unlike the import statement, __import__() function should not raise
77 # ImportError even if fromlist has an unknown item
78 # (see Python/import.c:import_module_level() and ensure_fromlist())
79 contextlibimp = __import__('contextlib', globals(), locals(), ['unknownattr'])
80 print("__import__('contextlib', ..., ['unknownattr']) =", f(contextlibimp))
81 print("hasattr(contextlibimp, 'unknownattr') =",
82 util.safehasattr(contextlibimp, 'unknownattr'))
74
83
75 demandimport.disable()
84 demandimport.disable()
76 os.environ['HGDEMANDIMPORT'] = 'disable'
85 os.environ['HGDEMANDIMPORT'] = 'disable'
77 # this enable call should not actually enable demandimport!
86 # this enable call should not actually enable demandimport!
78 demandimport.enable()
87 demandimport.enable()
79 from mercurial import node
88 from mercurial import node
80 print("node =", f(node))
89 print("node =", f(node))
@@ -1,21 +1,23 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'>
19 contextlib = <unloaded module 'contextlib'>
20 contextlib.unknownattr = ImportError: cannot import name unknownattr
20 contextlib.unknownattr = ImportError: cannot import name unknownattr
21 __import__('contextlib', ..., ['unknownattr']) = <module 'contextlib' from '?'>
22 hasattr(contextlibimp, 'unknownattr') = False
21 node = <module 'mercurial.node' from '?'>
23 node = <module 'mercurial.node' from '?'>
General Comments 0
You need to be logged in to leave comments. Login now