##// END OF EJS Templates
windows: add win32com.shell to demandimport ignore list...
Kostia Balytskyi -
r31990:3e03a4b9 default
parent child Browse files
Show More
@@ -1,330 +1,331 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 27 from __future__ import absolute_import
28 28
29 29 import contextlib
30 30 import os
31 31 import sys
32 32
33 33 # __builtin__ in Python 2, builtins in Python 3.
34 34 try:
35 35 import __builtin__ as builtins
36 36 except ImportError:
37 37 import builtins
38 38
39 39 contextmanager = contextlib.contextmanager
40 40
41 41 _origimport = __import__
42 42
43 43 nothing = object()
44 44
45 45 # Python 3 doesn't have relative imports nor level -1.
46 46 level = -1
47 47 if sys.version_info[0] >= 3:
48 48 level = 0
49 49 _import = _origimport
50 50
51 51 def _hgextimport(importfunc, name, globals, *args, **kwargs):
52 52 try:
53 53 return importfunc(name, globals, *args, **kwargs)
54 54 except ImportError:
55 55 if not globals:
56 56 raise
57 57 # extensions are loaded with "hgext_" prefix
58 58 hgextname = 'hgext_%s' % name
59 59 nameroot = hgextname.split('.', 1)[0]
60 60 contextroot = globals.get('__name__', '').split('.', 1)[0]
61 61 if nameroot != contextroot:
62 62 raise
63 63 # retry to import with "hgext_" prefix
64 64 return importfunc(hgextname, globals, *args, **kwargs)
65 65
66 66 class _demandmod(object):
67 67 """module demand-loader and proxy
68 68
69 69 Specify 1 as 'level' argument at construction, to import module
70 70 relatively.
71 71 """
72 72 def __init__(self, name, globals, locals, level):
73 73 if '.' in name:
74 74 head, rest = name.split('.', 1)
75 75 after = [rest]
76 76 else:
77 77 head = name
78 78 after = []
79 79 object.__setattr__(self, r"_data",
80 80 (head, globals, locals, after, level, set()))
81 81 object.__setattr__(self, r"_module", None)
82 82 def _extend(self, name):
83 83 """add to the list of submodules to load"""
84 84 self._data[3].append(name)
85 85
86 86 def _addref(self, name):
87 87 """Record that the named module ``name`` imports this module.
88 88
89 89 References to this proxy class having the name of this module will be
90 90 replaced at module load time. We assume the symbol inside the importing
91 91 module is identical to the "head" name of this module. We don't
92 92 actually know if "as X" syntax is being used to change the symbol name
93 93 because this information isn't exposed to __import__.
94 94 """
95 95 self._data[5].add(name)
96 96
97 97 def _load(self):
98 98 if not self._module:
99 99 head, globals, locals, after, level, modrefs = self._data
100 100 mod = _hgextimport(_import, head, globals, locals, None, level)
101 101 if mod is self:
102 102 # In this case, _hgextimport() above should imply
103 103 # _demandimport(). Otherwise, _hgextimport() never
104 104 # returns _demandmod. This isn't intentional behavior,
105 105 # in fact. (see also issue5304 for detail)
106 106 #
107 107 # If self._module is already bound at this point, self
108 108 # should be already _load()-ed while _hgextimport().
109 109 # Otherwise, there is no way to import actual module
110 110 # as expected, because (re-)invoking _hgextimport()
111 111 # should cause same result.
112 112 # This is reason why _load() returns without any more
113 113 # setup but assumes self to be already bound.
114 114 mod = self._module
115 115 assert mod and mod is not self, "%s, %s" % (self, mod)
116 116 return
117 117
118 118 # load submodules
119 119 def subload(mod, p):
120 120 h, t = p, None
121 121 if '.' in p:
122 122 h, t = p.split('.', 1)
123 123 if getattr(mod, h, nothing) is nothing:
124 124 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__,
125 125 level=1))
126 126 elif t:
127 127 subload(getattr(mod, h), t)
128 128
129 129 for x in after:
130 130 subload(mod, x)
131 131
132 132 # Replace references to this proxy instance with the actual module.
133 133 if locals and locals.get(head) == self:
134 134 locals[head] = mod
135 135
136 136 for modname in modrefs:
137 137 modref = sys.modules.get(modname, None)
138 138 if modref and getattr(modref, head, None) == self:
139 139 setattr(modref, head, mod)
140 140
141 141 object.__setattr__(self, r"_module", mod)
142 142
143 143 def __repr__(self):
144 144 if self._module:
145 145 return "<proxied module '%s'>" % self._data[0]
146 146 return "<unloaded module '%s'>" % self._data[0]
147 147 def __call__(self, *args, **kwargs):
148 148 raise TypeError("%s object is not callable" % repr(self))
149 149 def __getattribute__(self, attr):
150 150 if attr in ('_data', '_extend', '_load', '_module', '_addref'):
151 151 return object.__getattribute__(self, attr)
152 152 self._load()
153 153 return getattr(self._module, attr)
154 154 def __setattr__(self, attr, val):
155 155 self._load()
156 156 setattr(self._module, attr, val)
157 157
158 158 _pypy = '__pypy__' in sys.builtin_module_names
159 159
160 160 def _demandimport(name, globals=None, locals=None, fromlist=None, level=level):
161 161 if not locals or name in ignore or fromlist == ('*',):
162 162 # these cases we can't really delay
163 163 return _hgextimport(_import, name, globals, locals, fromlist, level)
164 164 elif not fromlist:
165 165 # import a [as b]
166 166 if '.' in name: # a.b
167 167 base, rest = name.split('.', 1)
168 168 # email.__init__ loading email.mime
169 169 if globals and globals.get('__name__', None) == base:
170 170 return _import(name, globals, locals, fromlist, level)
171 171 # if a is already demand-loaded, add b to its submodule list
172 172 if base in locals:
173 173 if isinstance(locals[base], _demandmod):
174 174 locals[base]._extend(rest)
175 175 return locals[base]
176 176 return _demandmod(name, globals, locals, level)
177 177 else:
178 178 # There is a fromlist.
179 179 # from a import b,c,d
180 180 # from . import b,c,d
181 181 # from .a import b,c,d
182 182
183 183 # level == -1: relative and absolute attempted (Python 2 only).
184 184 # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3).
185 185 # The modern Mercurial convention is to use absolute_import everywhere,
186 186 # so modern Mercurial code will have level >= 0.
187 187
188 188 # The name of the module the import statement is located in.
189 189 globalname = globals.get('__name__')
190 190
191 191 def processfromitem(mod, attr):
192 192 """Process an imported symbol in the import statement.
193 193
194 194 If the symbol doesn't exist in the parent module, and if the
195 195 parent module is a package, it must be a module. We set missing
196 196 modules up as _demandmod instances.
197 197 """
198 198 symbol = getattr(mod, attr, nothing)
199 199 nonpkg = getattr(mod, '__path__', nothing) is nothing
200 200 if symbol is nothing:
201 201 if nonpkg:
202 202 # do not try relative import, which would raise ValueError,
203 203 # and leave unknown attribute as the default __import__()
204 204 # would do. the missing attribute will be detected later
205 205 # while processing the import statement.
206 206 return
207 207 mn = '%s.%s' % (mod.__name__, attr)
208 208 if mn in ignore:
209 209 importfunc = _origimport
210 210 else:
211 211 importfunc = _demandmod
212 212 symbol = importfunc(attr, mod.__dict__, locals, level=1)
213 213 setattr(mod, attr, symbol)
214 214
215 215 # Record the importing module references this symbol so we can
216 216 # replace the symbol with the actual module instance at load
217 217 # time.
218 218 if globalname and isinstance(symbol, _demandmod):
219 219 symbol._addref(globalname)
220 220
221 221 def chainmodules(rootmod, modname):
222 222 # recurse down the module chain, and return the leaf module
223 223 mod = rootmod
224 224 for comp in modname.split('.')[1:]:
225 225 if getattr(mod, comp, nothing) is nothing:
226 226 setattr(mod, comp, _demandmod(comp, mod.__dict__,
227 227 mod.__dict__, level=1))
228 228 mod = getattr(mod, comp)
229 229 return mod
230 230
231 231 if level >= 0:
232 232 if name:
233 233 # "from a import b" or "from .a import b" style
234 234 rootmod = _hgextimport(_origimport, name, globals, locals,
235 235 level=level)
236 236 mod = chainmodules(rootmod, name)
237 237 elif _pypy:
238 238 # PyPy's __import__ throws an exception if invoked
239 239 # with an empty name and no fromlist. Recreate the
240 240 # desired behaviour by hand.
241 241 mn = globalname
242 242 mod = sys.modules[mn]
243 243 if getattr(mod, '__path__', nothing) is nothing:
244 244 mn = mn.rsplit('.', 1)[0]
245 245 mod = sys.modules[mn]
246 246 if level > 1:
247 247 mn = mn.rsplit('.', level - 1)[0]
248 248 mod = sys.modules[mn]
249 249 else:
250 250 mod = _hgextimport(_origimport, name, globals, locals,
251 251 level=level)
252 252
253 253 for x in fromlist:
254 254 processfromitem(mod, x)
255 255
256 256 return mod
257 257
258 258 # But, we still need to support lazy loading of standard library and 3rd
259 259 # party modules. So handle level == -1.
260 260 mod = _hgextimport(_origimport, name, globals, locals)
261 261 mod = chainmodules(mod, name)
262 262
263 263 for x in fromlist:
264 264 processfromitem(mod, x)
265 265
266 266 return mod
267 267
268 268 ignore = [
269 269 '__future__',
270 270 '_hashlib',
271 271 # ImportError during pkg_resources/__init__.py:fixup_namespace_package
272 272 '_imp',
273 273 '_xmlplus',
274 274 'fcntl',
275 275 'nt', # pathlib2 tests the existence of built-in 'nt' module
276 276 'win32com.gen_py',
277 'win32com.shell', # 'appdirs' tries to import win32com.shell
277 278 '_winreg', # 2.7 mimetypes needs immediate ImportError
278 279 'pythoncom',
279 280 # imported by tarfile, not available under Windows
280 281 'pwd',
281 282 'grp',
282 283 # imported by profile, itself imported by hotshot.stats,
283 284 # not available under Windows
284 285 'resource',
285 286 # this trips up many extension authors
286 287 'gtk',
287 288 # setuptools' pkg_resources.py expects "from __main__ import x" to
288 289 # raise ImportError if x not defined
289 290 '__main__',
290 291 '_ssl', # conditional imports in the stdlib, issue1964
291 292 '_sre', # issue4920
292 293 'rfc822',
293 294 'mimetools',
294 295 'sqlalchemy.events', # has import-time side effects (issue5085)
295 296 # setuptools 8 expects this module to explode early when not on windows
296 297 'distutils.msvc9compiler',
297 298 '__builtin__',
298 299 'builtins',
299 300 ]
300 301
301 302 if _pypy:
302 303 ignore.extend([
303 304 # _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5)
304 305 '_ctypes.pointer',
305 306 ])
306 307
307 308 def isenabled():
308 309 return builtins.__import__ == _demandimport
309 310
310 311 def enable():
311 312 "enable global demand-loading of modules"
312 313 if os.environ.get('HGDEMANDIMPORT') != 'disable':
313 314 builtins.__import__ = _demandimport
314 315
315 316 def disable():
316 317 "disable global demand-loading of modules"
317 318 builtins.__import__ = _origimport
318 319
319 320 @contextmanager
320 321 def deactivated():
321 322 "context manager for disabling demandimport in 'with' blocks"
322 323 demandenabled = isenabled()
323 324 if demandenabled:
324 325 disable()
325 326
326 327 try:
327 328 yield
328 329 finally:
329 330 if demandenabled:
330 331 enable()
@@ -1,76 +1,76 b''
1 1 #require test-repo
2 2
3 3 $ . "$TESTDIR/helpers-testrepo.sh"
4 4 $ check_code="$TESTDIR"/../contrib/check-code.py
5 5 $ cd "$TESTDIR"/..
6 6
7 7 New errors are not allowed. Warnings are strongly discouraged.
8 8 (The writing "no-che?k-code" is for not skipping this file when checking.)
9 9
10 10 $ hg locate -X contrib/python-zstandard -X hgext/fsmonitor/pywatchman |
11 11 > sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
12 12 contrib/perf.py:869:
13 13 > r.revision(r.node(x))
14 14 don't convert rev to node before passing to revision(nodeorrev)
15 15 Skipping i18n/polib.py it has no-che?k-code (glob)
16 mercurial/demandimport.py:312:
16 mercurial/demandimport.py:313:
17 17 > if os.environ.get('HGDEMANDIMPORT') != 'disable':
18 18 use encoding.environ instead (py3)
19 19 mercurial/encoding.py:54:
20 20 > environ = os.environ
21 21 use encoding.environ instead (py3)
22 22 mercurial/encoding.py:56:
23 23 > environ = os.environb
24 24 use encoding.environ instead (py3)
25 25 mercurial/encoding.py:61:
26 26 > for k, v in os.environ.items())
27 27 use encoding.environ instead (py3)
28 28 mercurial/encoding.py:221:
29 29 > for k, v in os.environ.items())
30 30 use encoding.environ instead (py3)
31 31 Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
32 32 Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
33 33 mercurial/policy.py:46:
34 34 > if 'HGMODULEPOLICY' in os.environ:
35 35 use encoding.environ instead (py3)
36 36 mercurial/policy.py:47:
37 37 > policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
38 38 use encoding.environ instead (py3)
39 39 mercurial/policy.py:49:
40 40 > policy = os.environ.get('HGMODULEPOLICY', policy)
41 41 use encoding.environ instead (py3)
42 42 Skipping mercurial/statprof.py it has no-che?k-code (glob)
43 43 [1]
44 44
45 45 @commands in debugcommands.py should be in alphabetical order.
46 46
47 47 >>> import re
48 48 >>> commands = []
49 49 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
50 50 ... for line in fh:
51 51 ... m = re.match("^@command\('([a-z]+)", line)
52 52 ... if m:
53 53 ... commands.append(m.group(1))
54 54 >>> scommands = list(sorted(commands))
55 55 >>> for i, command in enumerate(scommands):
56 56 ... if command != commands[i]:
57 57 ... print('commands in debugcommands.py not sorted; first differing '
58 58 ... 'command is %s; expected %s' % (commands[i], command))
59 59 ... break
60 60
61 61 Prevent adding new files in the root directory accidentally.
62 62
63 63 $ hg files 'glob:*'
64 64 .editorconfig
65 65 .hgignore
66 66 .hgsigs
67 67 .hgtags
68 68 CONTRIBUTING
69 69 CONTRIBUTORS
70 70 COPYING
71 71 Makefile
72 72 README
73 73 hg
74 74 hgeditor
75 75 hgweb.cgi
76 76 setup.py
General Comments 0
You need to be logged in to leave comments. Login now