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