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