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