##// END OF EJS Templates
demandimport: pass proper level to __import__ in Python 3...
Gregory Szorc -
r21290:74be3fb1 default
parent child Browse files
Show More
@@ -1,175 +1,179 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 import __builtin__, os
27 import __builtin__, os, sys
28 _origimport = __import__
28 _origimport = __import__
29
29
30 nothing = object()
30 nothing = object()
31
31
32 try:
32 try:
33 _origimport(__builtin__.__name__, {}, {}, None, -1)
33 # Python 3 doesn't have relative imports nor level -1.
34 level = -1
35 if sys.version_info[0] >= 3:
36 level = 0
37 _origimport(__builtin__.__name__, {}, {}, None, level)
34 except TypeError: # no level argument
38 except TypeError: # no level argument
35 def _import(name, globals, locals, fromlist, level):
39 def _import(name, globals, locals, fromlist, level):
36 "call _origimport with no level argument"
40 "call _origimport with no level argument"
37 return _origimport(name, globals, locals, fromlist)
41 return _origimport(name, globals, locals, fromlist)
38 else:
42 else:
39 _import = _origimport
43 _import = _origimport
40
44
41 def _hgextimport(importfunc, name, globals, *args):
45 def _hgextimport(importfunc, name, globals, *args):
42 try:
46 try:
43 return importfunc(name, globals, *args)
47 return importfunc(name, globals, *args)
44 except ImportError:
48 except ImportError:
45 if not globals:
49 if not globals:
46 raise
50 raise
47 # extensions are loaded with "hgext_" prefix
51 # extensions are loaded with "hgext_" prefix
48 hgextname = 'hgext_%s' % name
52 hgextname = 'hgext_%s' % name
49 nameroot = hgextname.split('.', 1)[0]
53 nameroot = hgextname.split('.', 1)[0]
50 contextroot = globals.get('__name__', '').split('.', 1)[0]
54 contextroot = globals.get('__name__', '').split('.', 1)[0]
51 if nameroot != contextroot:
55 if nameroot != contextroot:
52 raise
56 raise
53 # retry to import with "hgext_" prefix
57 # retry to import with "hgext_" prefix
54 return importfunc(hgextname, globals, *args)
58 return importfunc(hgextname, globals, *args)
55
59
56 class _demandmod(object):
60 class _demandmod(object):
57 """module demand-loader and proxy"""
61 """module demand-loader and proxy"""
58 def __init__(self, name, globals, locals, level=-1):
62 def __init__(self, name, globals, locals, level=level):
59 if '.' in name:
63 if '.' in name:
60 head, rest = name.split('.', 1)
64 head, rest = name.split('.', 1)
61 after = [rest]
65 after = [rest]
62 else:
66 else:
63 head = name
67 head = name
64 after = []
68 after = []
65 object.__setattr__(self, "_data",
69 object.__setattr__(self, "_data",
66 (head, globals, locals, after, level))
70 (head, globals, locals, after, level))
67 object.__setattr__(self, "_module", None)
71 object.__setattr__(self, "_module", None)
68 def _extend(self, name):
72 def _extend(self, name):
69 """add to the list of submodules to load"""
73 """add to the list of submodules to load"""
70 self._data[3].append(name)
74 self._data[3].append(name)
71 def _load(self):
75 def _load(self):
72 if not self._module:
76 if not self._module:
73 head, globals, locals, after, level = self._data
77 head, globals, locals, after, level = self._data
74 mod = _hgextimport(_import, head, globals, locals, None, level)
78 mod = _hgextimport(_import, head, globals, locals, None, level)
75 # load submodules
79 # load submodules
76 def subload(mod, p):
80 def subload(mod, p):
77 h, t = p, None
81 h, t = p, None
78 if '.' in p:
82 if '.' in p:
79 h, t = p.split('.', 1)
83 h, t = p.split('.', 1)
80 if getattr(mod, h, nothing) is nothing:
84 if getattr(mod, h, nothing) is nothing:
81 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
85 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
82 elif t:
86 elif t:
83 subload(getattr(mod, h), t)
87 subload(getattr(mod, h), t)
84
88
85 for x in after:
89 for x in after:
86 subload(mod, x)
90 subload(mod, x)
87
91
88 # are we in the locals dictionary still?
92 # are we in the locals dictionary still?
89 if locals and locals.get(head) == self:
93 if locals and locals.get(head) == self:
90 locals[head] = mod
94 locals[head] = mod
91 object.__setattr__(self, "_module", mod)
95 object.__setattr__(self, "_module", mod)
92
96
93 def __repr__(self):
97 def __repr__(self):
94 if self._module:
98 if self._module:
95 return "<proxied module '%s'>" % self._data[0]
99 return "<proxied module '%s'>" % self._data[0]
96 return "<unloaded module '%s'>" % self._data[0]
100 return "<unloaded module '%s'>" % self._data[0]
97 def __call__(self, *args, **kwargs):
101 def __call__(self, *args, **kwargs):
98 raise TypeError("%s object is not callable" % repr(self))
102 raise TypeError("%s object is not callable" % repr(self))
99 def __getattribute__(self, attr):
103 def __getattribute__(self, attr):
100 if attr in ('_data', '_extend', '_load', '_module'):
104 if attr in ('_data', '_extend', '_load', '_module'):
101 return object.__getattribute__(self, attr)
105 return object.__getattribute__(self, attr)
102 self._load()
106 self._load()
103 return getattr(self._module, attr)
107 return getattr(self._module, attr)
104 def __setattr__(self, attr, val):
108 def __setattr__(self, attr, val):
105 self._load()
109 self._load()
106 setattr(self._module, attr, val)
110 setattr(self._module, attr, val)
107
111
108 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
112 def _demandimport(name, globals=None, locals=None, fromlist=None, level=level):
109 if not locals or name in ignore or fromlist == ('*',):
113 if not locals or name in ignore or fromlist == ('*',):
110 # these cases we can't really delay
114 # these cases we can't really delay
111 return _hgextimport(_import, name, globals, locals, fromlist, level)
115 return _hgextimport(_import, name, globals, locals, fromlist, level)
112 elif not fromlist:
116 elif not fromlist:
113 # import a [as b]
117 # import a [as b]
114 if '.' in name: # a.b
118 if '.' in name: # a.b
115 base, rest = name.split('.', 1)
119 base, rest = name.split('.', 1)
116 # email.__init__ loading email.mime
120 # email.__init__ loading email.mime
117 if globals and globals.get('__name__', None) == base:
121 if globals and globals.get('__name__', None) == base:
118 return _import(name, globals, locals, fromlist, level)
122 return _import(name, globals, locals, fromlist, level)
119 # if a is already demand-loaded, add b to its submodule list
123 # if a is already demand-loaded, add b to its submodule list
120 if base in locals:
124 if base in locals:
121 if isinstance(locals[base], _demandmod):
125 if isinstance(locals[base], _demandmod):
122 locals[base]._extend(rest)
126 locals[base]._extend(rest)
123 return locals[base]
127 return locals[base]
124 return _demandmod(name, globals, locals, level)
128 return _demandmod(name, globals, locals, level)
125 else:
129 else:
126 if level != -1:
130 if level != -1:
127 # from . import b,c,d or from .a import b,c,d
131 # from . import b,c,d or from .a import b,c,d
128 return _origimport(name, globals, locals, fromlist, level)
132 return _origimport(name, globals, locals, fromlist, level)
129 # from a import b,c,d
133 # from a import b,c,d
130 mod = _hgextimport(_origimport, name, globals, locals)
134 mod = _hgextimport(_origimport, name, globals, locals)
131 # recurse down the module chain
135 # recurse down the module chain
132 for comp in name.split('.')[1:]:
136 for comp in name.split('.')[1:]:
133 if getattr(mod, comp, nothing) is nothing:
137 if getattr(mod, comp, nothing) is nothing:
134 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
138 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
135 mod = getattr(mod, comp)
139 mod = getattr(mod, comp)
136 for x in fromlist:
140 for x in fromlist:
137 # set requested submodules for demand load
141 # set requested submodules for demand load
138 if getattr(mod, x, nothing) is nothing:
142 if getattr(mod, x, nothing) is nothing:
139 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
143 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
140 return mod
144 return mod
141
145
142 ignore = [
146 ignore = [
143 '_hashlib',
147 '_hashlib',
144 '_xmlplus',
148 '_xmlplus',
145 'fcntl',
149 'fcntl',
146 'win32com.gen_py',
150 'win32com.gen_py',
147 '_winreg', # 2.7 mimetypes needs immediate ImportError
151 '_winreg', # 2.7 mimetypes needs immediate ImportError
148 'pythoncom',
152 'pythoncom',
149 # imported by tarfile, not available under Windows
153 # imported by tarfile, not available under Windows
150 'pwd',
154 'pwd',
151 'grp',
155 'grp',
152 # imported by profile, itself imported by hotshot.stats,
156 # imported by profile, itself imported by hotshot.stats,
153 # not available under Windows
157 # not available under Windows
154 'resource',
158 'resource',
155 # this trips up many extension authors
159 # this trips up many extension authors
156 'gtk',
160 'gtk',
157 # setuptools' pkg_resources.py expects "from __main__ import x" to
161 # setuptools' pkg_resources.py expects "from __main__ import x" to
158 # raise ImportError if x not defined
162 # raise ImportError if x not defined
159 '__main__',
163 '__main__',
160 '_ssl', # conditional imports in the stdlib, issue1964
164 '_ssl', # conditional imports in the stdlib, issue1964
161 'rfc822',
165 'rfc822',
162 'mimetools',
166 'mimetools',
163 ]
167 ]
164
168
165 def isenabled():
169 def isenabled():
166 return __builtin__.__import__ == _demandimport
170 return __builtin__.__import__ == _demandimport
167
171
168 def enable():
172 def enable():
169 "enable global demand-loading of modules"
173 "enable global demand-loading of modules"
170 if os.environ.get('HGDEMANDIMPORT') != 'disable':
174 if os.environ.get('HGDEMANDIMPORT') != 'disable':
171 __builtin__.__import__ = _demandimport
175 __builtin__.__import__ = _demandimport
172
176
173 def disable():
177 def disable():
174 "disable global demand-loading of modules"
178 "disable global demand-loading of modules"
175 __builtin__.__import__ = _origimport
179 __builtin__.__import__ = _origimport
General Comments 0
You need to be logged in to leave comments. Login now