##// END OF EJS Templates
demandimport: make it possible to disable by setting HGDEMANDIMPORT=disable...
Mads Kiilerich -
r21025:54af51c1 default
parent child Browse files
Show More
@@ -1,174 +1,175 b''
1 1 # demandimport.py - global demand-loading of modules for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''
9 9 demandimport - automatic demandloading of modules
10 10
11 11 To enable this module, do:
12 12
13 13 import demandimport; demandimport.enable()
14 14
15 15 Imports of the following forms will be demand-loaded:
16 16
17 17 import a, b.c
18 18 import a.b as c
19 19 from a import b,c # a will be loaded immediately
20 20
21 21 These imports will not be delayed:
22 22
23 23 from a import *
24 24 b = __import__(a)
25 25 '''
26 26
27 import __builtin__
27 import __builtin__, os
28 28 _origimport = __import__
29 29
30 30 nothing = object()
31 31
32 32 try:
33 33 _origimport(__builtin__.__name__, {}, {}, None, -1)
34 34 except TypeError: # no level argument
35 35 def _import(name, globals, locals, fromlist, level):
36 36 "call _origimport with no level argument"
37 37 return _origimport(name, globals, locals, fromlist)
38 38 else:
39 39 _import = _origimport
40 40
41 41 def _hgextimport(importfunc, name, globals, *args):
42 42 try:
43 43 return importfunc(name, globals, *args)
44 44 except ImportError:
45 45 if not globals:
46 46 raise
47 47 # extensions are loaded with "hgext_" prefix
48 48 hgextname = 'hgext_%s' % name
49 49 nameroot = hgextname.split('.', 1)[0]
50 50 contextroot = globals.get('__name__', '').split('.', 1)[0]
51 51 if nameroot != contextroot:
52 52 raise
53 53 # retry to import with "hgext_" prefix
54 54 return importfunc(hgextname, globals, *args)
55 55
56 56 class _demandmod(object):
57 57 """module demand-loader and proxy"""
58 58 def __init__(self, name, globals, locals, level=-1):
59 59 if '.' in name:
60 60 head, rest = name.split('.', 1)
61 61 after = [rest]
62 62 else:
63 63 head = name
64 64 after = []
65 65 object.__setattr__(self, "_data",
66 66 (head, globals, locals, after, level))
67 67 object.__setattr__(self, "_module", None)
68 68 def _extend(self, name):
69 69 """add to the list of submodules to load"""
70 70 self._data[3].append(name)
71 71 def _load(self):
72 72 if not self._module:
73 73 head, globals, locals, after, level = self._data
74 74 mod = _hgextimport(_import, head, globals, locals, None, level)
75 75 # load submodules
76 76 def subload(mod, p):
77 77 h, t = p, None
78 78 if '.' in p:
79 79 h, t = p.split('.', 1)
80 80 if getattr(mod, h, nothing) is nothing:
81 81 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
82 82 elif t:
83 83 subload(getattr(mod, h), t)
84 84
85 85 for x in after:
86 86 subload(mod, x)
87 87
88 88 # are we in the locals dictionary still?
89 89 if locals and locals.get(head) == self:
90 90 locals[head] = mod
91 91 object.__setattr__(self, "_module", mod)
92 92
93 93 def __repr__(self):
94 94 if self._module:
95 95 return "<proxied module '%s'>" % self._data[0]
96 96 return "<unloaded module '%s'>" % self._data[0]
97 97 def __call__(self, *args, **kwargs):
98 98 raise TypeError("%s object is not callable" % repr(self))
99 99 def __getattribute__(self, attr):
100 100 if attr in ('_data', '_extend', '_load', '_module'):
101 101 return object.__getattribute__(self, attr)
102 102 self._load()
103 103 return getattr(self._module, attr)
104 104 def __setattr__(self, attr, val):
105 105 self._load()
106 106 setattr(self._module, attr, val)
107 107
108 108 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
109 109 if not locals or name in ignore or fromlist == ('*',):
110 110 # these cases we can't really delay
111 111 return _hgextimport(_import, name, globals, locals, fromlist, level)
112 112 elif not fromlist:
113 113 # import a [as b]
114 114 if '.' in name: # a.b
115 115 base, rest = name.split('.', 1)
116 116 # email.__init__ loading email.mime
117 117 if globals and globals.get('__name__', None) == base:
118 118 return _import(name, globals, locals, fromlist, level)
119 119 # if a is already demand-loaded, add b to its submodule list
120 120 if base in locals:
121 121 if isinstance(locals[base], _demandmod):
122 122 locals[base]._extend(rest)
123 123 return locals[base]
124 124 return _demandmod(name, globals, locals, level)
125 125 else:
126 126 if level != -1:
127 127 # from . import b,c,d or from .a import b,c,d
128 128 return _origimport(name, globals, locals, fromlist, level)
129 129 # from a import b,c,d
130 130 mod = _hgextimport(_origimport, name, globals, locals)
131 131 # recurse down the module chain
132 132 for comp in name.split('.')[1:]:
133 133 if getattr(mod, comp, nothing) is nothing:
134 134 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
135 135 mod = getattr(mod, comp)
136 136 for x in fromlist:
137 137 # set requested submodules for demand load
138 138 if getattr(mod, x, nothing) is nothing:
139 139 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
140 140 return mod
141 141
142 142 ignore = [
143 143 '_hashlib',
144 144 '_xmlplus',
145 145 'fcntl',
146 146 'win32com.gen_py',
147 147 '_winreg', # 2.7 mimetypes needs immediate ImportError
148 148 'pythoncom',
149 149 # imported by tarfile, not available under Windows
150 150 'pwd',
151 151 'grp',
152 152 # imported by profile, itself imported by hotshot.stats,
153 153 # not available under Windows
154 154 'resource',
155 155 # this trips up many extension authors
156 156 'gtk',
157 157 # setuptools' pkg_resources.py expects "from __main__ import x" to
158 158 # raise ImportError if x not defined
159 159 '__main__',
160 160 '_ssl', # conditional imports in the stdlib, issue1964
161 161 'rfc822',
162 162 'mimetools',
163 163 ]
164 164
165 165 def isenabled():
166 166 return __builtin__.__import__ == _demandimport
167 167
168 168 def enable():
169 169 "enable global demand-loading of modules"
170 __builtin__.__import__ = _demandimport
170 if os.environ.get('HGDEMANDIMPORT') != 'disable':
171 __builtin__.__import__ = _demandimport
171 172
172 173 def disable():
173 174 "disable global demand-loading of modules"
174 175 __builtin__.__import__ = _origimport
@@ -1,39 +1,45 b''
1 1 from mercurial import demandimport
2 2 demandimport.enable()
3 3
4 4 import re
5 5
6 6 rsub = re.sub
7 7 def f(obj):
8 8 l = repr(obj)
9 9 l = rsub("0x[0-9a-fA-F]+", "0x?", l)
10 10 l = rsub("from '.*'", "from '?'", l)
11 11 l = rsub("'<[a-z]*>'", "'<whatever>'", l)
12 12 return l
13 13
14 14 import os
15 15
16 16 print "os =", f(os)
17 17 print "os.system =", f(os.system)
18 18 print "os =", f(os)
19 19
20 20 from mercurial import util
21 21
22 22 print "util =", f(util)
23 23 print "util.system =", f(util.system)
24 24 print "util =", f(util)
25 25 print "util.system =", f(util.system)
26 26
27 27 import re as fred
28 28 print "fred =", f(fred)
29 29
30 30 import sys as re
31 31 print "re =", f(re)
32 32
33 33 print "fred =", f(fred)
34 34 print "fred.sub =", f(fred.sub)
35 35 print "fred =", f(fred)
36 36
37 37 print "re =", f(re)
38 38 print "re.stderr =", f(re.stderr)
39 39 print "re =", f(re)
40
41 demandimport.disable()
42 os.environ['HGDEMANDIMPORT'] = 'disable'
43 demandimport.enable()
44 from mercurial import node
45 print "node =", f(node)
@@ -1,15 +1,16 b''
1 1 os = <unloaded module 'os'>
2 2 os.system = <built-in function system>
3 3 os = <module 'os' from '?'>
4 4 util = <unloaded module 'util'>
5 5 util.system = <function system at 0x?>
6 6 util = <module 'mercurial.util' from '?'>
7 7 util.system = <function system at 0x?>
8 8 fred = <unloaded module 're'>
9 9 re = <unloaded module 'sys'>
10 10 fred = <unloaded module 're'>
11 11 fred.sub = <function sub at 0x?>
12 12 fred = <proxied module 're'>
13 13 re = <unloaded module 'sys'>
14 14 re.stderr = <open file '<whatever>', mode 'w' at 0x?>
15 15 re = <proxied module 'sys'>
16 node = <module 'mercurial.node' from '?'>
General Comments 0
You need to be logged in to leave comments. Login now