##// END OF EJS Templates
Avoid reloading builtin `types` module....
Matthias Bussonnier -
Show More
@@ -1,339 +1,347 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Provides a reload() function that acts recursively.
4 4
5 5 Python's normal :func:`python:reload` function only reloads the module that it's
6 6 passed. The :func:`reload` function in this module also reloads everything
7 7 imported from that module, which is useful when you're changing files deep
8 8 inside a package.
9 9
10 10 To use this as your default reload function, type this for Python 2::
11 11
12 12 import __builtin__
13 13 from IPython.lib import deepreload
14 14 __builtin__.reload = deepreload.reload
15 15
16 16 Or this for Python 3::
17 17
18 18 import builtins
19 19 from IPython.lib import deepreload
20 20 builtins.reload = deepreload.reload
21 21
22 22 A reference to the original :func:`python:reload` is stored in this module as
23 23 :data:`original_reload`, so you can restore it later.
24 24
25 25 This code is almost entirely based on knee.py, which is a Python
26 26 re-implementation of hierarchical module import.
27 27 """
28 28 #*****************************************************************************
29 29 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
30 30 #
31 31 # Distributed under the terms of the BSD License. The full license is in
32 32 # the file COPYING, distributed as part of this software.
33 33 #*****************************************************************************
34 34
35 35 import builtins as builtin_mod
36 36 from contextlib import contextmanager
37 37 import imp
38 38 import sys
39 39
40 40 from types import ModuleType
41 41 from warnings import warn
42 import types
42 43
43 44 original_import = builtin_mod.__import__
44 45
45 46 @contextmanager
46 47 def replace_import_hook(new_import):
47 48 saved_import = builtin_mod.__import__
48 49 builtin_mod.__import__ = new_import
49 50 try:
50 51 yield
51 52 finally:
52 53 builtin_mod.__import__ = saved_import
53 54
54 55 def get_parent(globals, level):
55 56 """
56 57 parent, name = get_parent(globals, level)
57 58
58 59 Return the package that an import is being performed in. If globals comes
59 60 from the module foo.bar.bat (not itself a package), this returns the
60 61 sys.modules entry for foo.bar. If globals is from a package's __init__.py,
61 62 the package's entry in sys.modules is returned.
62 63
63 64 If globals doesn't come from a package or a module in a package, or a
64 65 corresponding entry is not found in sys.modules, None is returned.
65 66 """
66 67 orig_level = level
67 68
68 69 if not level or not isinstance(globals, dict):
69 70 return None, ''
70 71
71 72 pkgname = globals.get('__package__', None)
72 73
73 74 if pkgname is not None:
74 75 # __package__ is set, so use it
75 76 if not hasattr(pkgname, 'rindex'):
76 77 raise ValueError('__package__ set to non-string')
77 78 if len(pkgname) == 0:
78 79 if level > 0:
79 80 raise ValueError('Attempted relative import in non-package')
80 81 return None, ''
81 82 name = pkgname
82 83 else:
83 84 # __package__ not set, so figure it out and set it
84 85 if '__name__' not in globals:
85 86 return None, ''
86 87 modname = globals['__name__']
87 88
88 89 if '__path__' in globals:
89 90 # __path__ is set, so modname is already the package name
90 91 globals['__package__'] = name = modname
91 92 else:
92 93 # Normal module, so work out the package name if any
93 94 lastdot = modname.rfind('.')
94 95 if lastdot < 0 < level:
95 96 raise ValueError("Attempted relative import in non-package")
96 97 if lastdot < 0:
97 98 globals['__package__'] = None
98 99 return None, ''
99 100 globals['__package__'] = name = modname[:lastdot]
100 101
101 102 dot = len(name)
102 103 for x in range(level, 1, -1):
103 104 try:
104 105 dot = name.rindex('.', 0, dot)
105 106 except ValueError:
106 107 raise ValueError("attempted relative import beyond top-level "
107 108 "package")
108 109 name = name[:dot]
109 110
110 111 try:
111 112 parent = sys.modules[name]
112 113 except:
113 114 if orig_level < 1:
114 115 warn("Parent module '%.200s' not found while handling absolute "
115 116 "import" % name)
116 117 parent = None
117 118 else:
118 119 raise SystemError("Parent module '%.200s' not loaded, cannot "
119 120 "perform relative import" % name)
120 121
121 122 # We expect, but can't guarantee, if parent != None, that:
122 123 # - parent.__name__ == name
123 124 # - parent.__dict__ is globals
124 125 # If this is violated... Who cares?
125 126 return parent, name
126 127
127 128 def load_next(mod, altmod, name, buf):
128 129 """
129 130 mod, name, buf = load_next(mod, altmod, name, buf)
130 131
131 132 altmod is either None or same as mod
132 133 """
133 134
134 135 if len(name) == 0:
135 136 # completely empty module name should only happen in
136 137 # 'from . import' (or '__import__("")')
137 138 return mod, None, buf
138 139
139 140 dot = name.find('.')
140 141 if dot == 0:
141 142 raise ValueError('Empty module name')
142 143
143 144 if dot < 0:
144 145 subname = name
145 146 next = None
146 147 else:
147 148 subname = name[:dot]
148 149 next = name[dot+1:]
149 150
150 151 if buf != '':
151 152 buf += '.'
152 153 buf += subname
153 154
154 155 result = import_submodule(mod, subname, buf)
155 156 if result is None and mod != altmod:
156 157 result = import_submodule(altmod, subname, subname)
157 158 if result is not None:
158 159 buf = subname
159 160
160 161 if result is None:
161 162 raise ImportError("No module named %.200s" % name)
162 163
163 164 return result, next, buf
164 165
166
165 167 # Need to keep track of what we've already reloaded to prevent cyclic evil
166 168 found_now = {}
167 169
168 170 def import_submodule(mod, subname, fullname):
169 171 """m = import_submodule(mod, subname, fullname)"""
170 172 # Require:
171 173 # if mod == None: subname == fullname
172 174 # else: mod.__name__ + "." + subname == fullname
173 175
174 176 global found_now
175 177 if fullname in found_now and fullname in sys.modules:
176 178 m = sys.modules[fullname]
177 179 else:
178 180 print('Reloading', fullname)
179 181 found_now[fullname] = 1
180 182 oldm = sys.modules.get(fullname, None)
181 183
182 184 if mod is None:
183 185 path = None
184 186 elif hasattr(mod, '__path__'):
185 187 path = mod.__path__
186 188 else:
187 189 return None
188 190
189 191 try:
190 192 # This appears to be necessary on Python 3, because imp.find_module()
191 193 # tries to import standard libraries (like io) itself, and we don't
192 194 # want them to be processed by our deep_import_hook.
193 195 with replace_import_hook(original_import):
194 196 fp, filename, stuff = imp.find_module(subname, path)
195 197 except ImportError:
196 198 return None
197 199
198 200 try:
199 201 m = imp.load_module(fullname, fp, filename, stuff)
200 202 except:
201 203 # load_module probably removed name from modules because of
202 204 # the error. Put back the original module object.
203 205 if oldm:
204 206 sys.modules[fullname] = oldm
205 207 raise
206 208 finally:
207 209 if fp: fp.close()
208 210
209 211 add_submodule(mod, m, fullname, subname)
210 212
211 213 return m
212 214
213 215 def add_submodule(mod, submod, fullname, subname):
214 216 """mod.{subname} = submod"""
215 217 if mod is None:
216 218 return #Nothing to do here.
217 219
218 220 if submod is None:
219 221 submod = sys.modules[fullname]
220 222
221 223 setattr(mod, subname, submod)
222 224
223 225 return
224 226
225 227 def ensure_fromlist(mod, fromlist, buf, recursive):
226 228 """Handle 'from module import a, b, c' imports."""
227 229 if not hasattr(mod, '__path__'):
228 230 return
229 231 for item in fromlist:
230 232 if not hasattr(item, 'rindex'):
231 233 raise TypeError("Item in ``from list'' not a string")
232 234 if item == '*':
233 235 if recursive:
234 236 continue # avoid endless recursion
235 237 try:
236 238 all = mod.__all__
237 239 except AttributeError:
238 240 pass
239 241 else:
240 242 ret = ensure_fromlist(mod, all, buf, 1)
241 243 if not ret:
242 244 return 0
243 245 elif not hasattr(mod, item):
244 246 import_submodule(mod, item, buf + '.' + item)
245 247
246 248 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
247 249 """Replacement for __import__()"""
248 250 parent, buf = get_parent(globals, level)
249 251
250 252 head, name, buf = load_next(parent, None if level < 0 else parent, name, buf)
251 253
252 254 tail = head
253 255 while name:
254 256 tail, name, buf = load_next(tail, tail, name, buf)
255 257
256 258 # If tail is None, both get_parent and load_next found
257 259 # an empty module name: someone called __import__("") or
258 260 # doctored faulty bytecode
259 261 if tail is None:
260 262 raise ValueError('Empty module name')
261 263
262 264 if not fromlist:
263 265 return head
264 266
265 267 ensure_fromlist(tail, fromlist, buf, 0)
266 268 return tail
267 269
268 270 modules_reloading = {}
269 271
270 272 def deep_reload_hook(m):
271 273 """Replacement for reload()."""
274 # Hardcode this one as it would raise a NotImplemeentedError from the
275 # bowels of Python and screw up the import machinery after.
276 # unlike other imports the `exclude` list aleady in place is not enough.
277
278 if m is types:
279 return m
272 280 if not isinstance(m, ModuleType):
273 281 raise TypeError("reload() argument must be module")
274 282
275 283 name = m.__name__
276 284
277 285 if name not in sys.modules:
278 286 raise ImportError("reload(): module %.200s not in sys.modules" % name)
279 287
280 288 global modules_reloading
281 289 try:
282 290 return modules_reloading[name]
283 291 except:
284 292 modules_reloading[name] = m
285 293
286 294 dot = name.rfind('.')
287 295 if dot < 0:
288 296 subname = name
289 297 path = None
290 298 else:
291 299 try:
292 300 parent = sys.modules[name[:dot]]
293 301 except KeyError:
294 302 modules_reloading.clear()
295 303 raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot])
296 304 subname = name[dot+1:]
297 305 path = getattr(parent, "__path__", None)
298 306
299 307 try:
300 308 # This appears to be necessary on Python 3, because imp.find_module()
301 309 # tries to import standard libraries (like io) itself, and we don't
302 310 # want them to be processed by our deep_import_hook.
303 311 with replace_import_hook(original_import):
304 312 fp, filename, stuff = imp.find_module(subname, path)
305 313 finally:
306 314 modules_reloading.clear()
307 315
308 316 try:
309 317 newm = imp.load_module(name, fp, filename, stuff)
310 318 except:
311 319 # load_module probably removed name from modules because of
312 320 # the error. Put back the original module object.
313 321 sys.modules[name] = m
314 322 raise
315 323 finally:
316 324 if fp: fp.close()
317 325
318 326 modules_reloading.clear()
319 327 return newm
320 328
321 329 # Save the original hooks
322 original_reload = imp.reload # Python 3
330 original_reload = imp.reload
323 331
324 332 # Replacement for reload()
325 333 def reload(module, exclude=('sys', 'os.path', 'builtins', '__main__',
326 334 'numpy', 'numpy._globals')):
327 335 """Recursively reload all modules used in the given module. Optionally
328 336 takes a list of modules to exclude from reloading. The default exclude
329 337 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
330 338 display, exception, and io hooks.
331 339 """
332 340 global found_now
333 341 for i in exclude:
334 342 found_now[i] = 1
335 343 try:
336 344 with replace_import_hook(deep_import_hook):
337 345 return deep_reload_hook(module)
338 346 finally:
339 347 found_now = {}
General Comments 0
You need to be logged in to leave comments. Login now