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