##// END OF EJS Templates
Backport PR #10523: Don't reload __mp_main__...
Thomas Kluyver -
Show More
@@ -1,536 +1,536 b''
1 1 """IPython extension to reload modules before executing user code.
2 2
3 3 ``autoreload`` reloads modules automatically before entering the execution of
4 4 code typed at the IPython prompt.
5 5
6 6 This makes for example the following workflow possible:
7 7
8 8 .. sourcecode:: ipython
9 9
10 10 In [1]: %load_ext autoreload
11 11
12 12 In [2]: %autoreload 2
13 13
14 14 In [3]: from foo import some_function
15 15
16 16 In [4]: some_function()
17 17 Out[4]: 42
18 18
19 19 In [5]: # open foo.py in an editor and change some_function to return 43
20 20
21 21 In [6]: some_function()
22 22 Out[6]: 43
23 23
24 24 The module was reloaded without reloading it explicitly, and the object
25 25 imported with ``from foo import ...`` was also updated.
26 26
27 27 Usage
28 28 =====
29 29
30 30 The following magic commands are provided:
31 31
32 32 ``%autoreload``
33 33
34 34 Reload all modules (except those excluded by ``%aimport``)
35 35 automatically now.
36 36
37 37 ``%autoreload 0``
38 38
39 39 Disable automatic reloading.
40 40
41 41 ``%autoreload 1``
42 42
43 43 Reload all modules imported with ``%aimport`` every time before
44 44 executing the Python code typed.
45 45
46 46 ``%autoreload 2``
47 47
48 48 Reload all modules (except those excluded by ``%aimport``) every
49 49 time before executing the Python code typed.
50 50
51 51 ``%aimport``
52 52
53 53 List modules which are to be automatically imported or not to be imported.
54 54
55 55 ``%aimport foo``
56 56
57 57 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
58 58
59 59 ``%aimport -foo``
60 60
61 61 Mark module 'foo' to not be autoreloaded.
62 62
63 63 Caveats
64 64 =======
65 65
66 66 Reloading Python modules in a reliable way is in general difficult,
67 67 and unexpected things may occur. ``%autoreload`` tries to work around
68 68 common pitfalls by replacing function code objects and parts of
69 69 classes previously in the module with new versions. This makes the
70 70 following things to work:
71 71
72 72 - Functions and classes imported via 'from xxx import foo' are upgraded
73 73 to new versions when 'xxx' is reloaded.
74 74
75 75 - Methods and properties of classes are upgraded on reload, so that
76 76 calling 'c.foo()' on an object 'c' created before the reload causes
77 77 the new code for 'foo' to be executed.
78 78
79 79 Some of the known remaining caveats are:
80 80
81 81 - Replacing code objects does not always succeed: changing a @property
82 82 in a class to an ordinary method or a method to a member variable
83 83 can cause problems (but in old objects only).
84 84
85 85 - Functions that are removed (eg. via monkey-patching) from a module
86 86 before it is reloaded are not upgraded.
87 87
88 88 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
89 89 """
90 90 from __future__ import print_function
91 91
92 92 skip_doctest = True
93 93
94 94 #-----------------------------------------------------------------------------
95 95 # Copyright (C) 2000 Thomas Heller
96 96 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
97 97 # Copyright (C) 2012 The IPython Development Team
98 98 #
99 99 # Distributed under the terms of the BSD License. The full license is in
100 100 # the file COPYING, distributed as part of this software.
101 101 #-----------------------------------------------------------------------------
102 102 #
103 103 # This IPython module is written by Pauli Virtanen, based on the autoreload
104 104 # code by Thomas Heller.
105 105
106 106 #-----------------------------------------------------------------------------
107 107 # Imports
108 108 #-----------------------------------------------------------------------------
109 109
110 110 import os
111 111 import sys
112 112 import traceback
113 113 import types
114 114 import weakref
115 115
116 116 try:
117 117 # Reload is not defined by default in Python3.
118 118 reload
119 119 except NameError:
120 120 from imp import reload
121 121
122 122 from IPython.utils import openpy
123 123 from IPython.utils.py3compat import PY3
124 124
125 125 #------------------------------------------------------------------------------
126 126 # Autoreload functionality
127 127 #------------------------------------------------------------------------------
128 128
129 129 class ModuleReloader(object):
130 130 enabled = False
131 131 """Whether this reloader is enabled"""
132 132
133 133 check_all = True
134 134 """Autoreload all modules, not just those listed in 'modules'"""
135 135
136 136 def __init__(self):
137 137 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
138 138 self.failed = {}
139 139 # Modules specially marked as autoreloadable.
140 140 self.modules = {}
141 141 # Modules specially marked as not autoreloadable.
142 142 self.skip_modules = {}
143 143 # (module-name, name) -> weakref, for replacing old code objects
144 144 self.old_objects = {}
145 145 # Module modification timestamps
146 146 self.modules_mtimes = {}
147 147
148 148 # Cache module modification times
149 149 self.check(check_all=True, do_reload=False)
150 150
151 151 def mark_module_skipped(self, module_name):
152 152 """Skip reloading the named module in the future"""
153 153 try:
154 154 del self.modules[module_name]
155 155 except KeyError:
156 156 pass
157 157 self.skip_modules[module_name] = True
158 158
159 159 def mark_module_reloadable(self, module_name):
160 160 """Reload the named module in the future (if it is imported)"""
161 161 try:
162 162 del self.skip_modules[module_name]
163 163 except KeyError:
164 164 pass
165 165 self.modules[module_name] = True
166 166
167 167 def aimport_module(self, module_name):
168 168 """Import a module, and mark it reloadable
169 169
170 170 Returns
171 171 -------
172 172 top_module : module
173 173 The imported module if it is top-level, or the top-level
174 174 top_name : module
175 175 Name of top_module
176 176
177 177 """
178 178 self.mark_module_reloadable(module_name)
179 179
180 180 __import__(module_name)
181 181 top_name = module_name.split('.')[0]
182 182 top_module = sys.modules[top_name]
183 183 return top_module, top_name
184 184
185 185 def filename_and_mtime(self, module):
186 186 if not hasattr(module, '__file__') or module.__file__ is None:
187 187 return None, None
188 188
189 if getattr(module, '__name__', None) == '__main__':
190 # we cannot reload(__main__)
189 if getattr(module, '__name__', None) in ['__mp_main__', '__main__']:
190 # we cannot reload(__main__) or reload(__mp_main__)
191 191 return None, None
192 192
193 193 filename = module.__file__
194 194 path, ext = os.path.splitext(filename)
195 195
196 196 if ext.lower() == '.py':
197 197 py_filename = filename
198 198 else:
199 199 try:
200 200 py_filename = openpy.source_from_cache(filename)
201 201 except ValueError:
202 202 return None, None
203 203
204 204 try:
205 205 pymtime = os.stat(py_filename).st_mtime
206 206 except OSError:
207 207 return None, None
208 208
209 209 return py_filename, pymtime
210 210
211 211 def check(self, check_all=False, do_reload=True):
212 212 """Check whether some modules need to be reloaded."""
213 213
214 214 if not self.enabled and not check_all:
215 215 return
216 216
217 217 if check_all or self.check_all:
218 218 modules = list(sys.modules.keys())
219 219 else:
220 220 modules = list(self.modules.keys())
221 221
222 222 for modname in modules:
223 223 m = sys.modules.get(modname, None)
224 224
225 225 if modname in self.skip_modules:
226 226 continue
227 227
228 228 py_filename, pymtime = self.filename_and_mtime(m)
229 229 if py_filename is None:
230 230 continue
231 231
232 232 try:
233 233 if pymtime <= self.modules_mtimes[modname]:
234 234 continue
235 235 except KeyError:
236 236 self.modules_mtimes[modname] = pymtime
237 237 continue
238 238 else:
239 239 if self.failed.get(py_filename, None) == pymtime:
240 240 continue
241 241
242 242 self.modules_mtimes[modname] = pymtime
243 243
244 244 # If we've reached this point, we should try to reload the module
245 245 if do_reload:
246 246 try:
247 247 superreload(m, reload, self.old_objects)
248 248 if py_filename in self.failed:
249 249 del self.failed[py_filename]
250 250 except:
251 251 print("[autoreload of %s failed: %s]" % (
252 252 modname, traceback.format_exc(1)), file=sys.stderr)
253 253 self.failed[py_filename] = pymtime
254 254
255 255 #------------------------------------------------------------------------------
256 256 # superreload
257 257 #------------------------------------------------------------------------------
258 258
259 259 if PY3:
260 260 func_attrs = ['__code__', '__defaults__', '__doc__',
261 261 '__closure__', '__globals__', '__dict__']
262 262 else:
263 263 func_attrs = ['func_code', 'func_defaults', 'func_doc',
264 264 'func_closure', 'func_globals', 'func_dict']
265 265
266 266
267 267 def update_function(old, new):
268 268 """Upgrade the code object of a function"""
269 269 for name in func_attrs:
270 270 try:
271 271 setattr(old, name, getattr(new, name))
272 272 except (AttributeError, TypeError):
273 273 pass
274 274
275 275
276 276 def update_class(old, new):
277 277 """Replace stuff in the __dict__ of a class, and upgrade
278 278 method code objects"""
279 279 for key in list(old.__dict__.keys()):
280 280 old_obj = getattr(old, key)
281 281
282 282 try:
283 283 new_obj = getattr(new, key)
284 284 except AttributeError:
285 285 # obsolete attribute: remove it
286 286 try:
287 287 delattr(old, key)
288 288 except (AttributeError, TypeError):
289 289 pass
290 290 continue
291 291
292 292 if update_generic(old_obj, new_obj): continue
293 293
294 294 try:
295 295 setattr(old, key, getattr(new, key))
296 296 except (AttributeError, TypeError):
297 297 pass # skip non-writable attributes
298 298
299 299
300 300 def update_property(old, new):
301 301 """Replace get/set/del functions of a property"""
302 302 update_generic(old.fdel, new.fdel)
303 303 update_generic(old.fget, new.fget)
304 304 update_generic(old.fset, new.fset)
305 305
306 306
307 307 def isinstance2(a, b, typ):
308 308 return isinstance(a, typ) and isinstance(b, typ)
309 309
310 310
311 311 UPDATE_RULES = [
312 312 (lambda a, b: isinstance2(a, b, type),
313 313 update_class),
314 314 (lambda a, b: isinstance2(a, b, types.FunctionType),
315 315 update_function),
316 316 (lambda a, b: isinstance2(a, b, property),
317 317 update_property),
318 318 ]
319 319
320 320
321 321 if PY3:
322 322 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
323 323 lambda a, b: update_function(a.__func__, b.__func__)),
324 324 ])
325 325 else:
326 326 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
327 327 update_class),
328 328 (lambda a, b: isinstance2(a, b, types.MethodType),
329 329 lambda a, b: update_function(a.__func__, b.__func__)),
330 330 ])
331 331
332 332
333 333 def update_generic(a, b):
334 334 for type_check, update in UPDATE_RULES:
335 335 if type_check(a, b):
336 336 update(a, b)
337 337 return True
338 338 return False
339 339
340 340
341 341 class StrongRef(object):
342 342 def __init__(self, obj):
343 343 self.obj = obj
344 344 def __call__(self):
345 345 return self.obj
346 346
347 347
348 348 def superreload(module, reload=reload, old_objects={}):
349 349 """Enhanced version of the builtin reload function.
350 350
351 351 superreload remembers objects previously in the module, and
352 352
353 353 - upgrades the class dictionary of every old class in the module
354 354 - upgrades the code object of every old function and method
355 355 - clears the module's namespace before reloading
356 356
357 357 """
358 358
359 359 # collect old objects in the module
360 360 for name, obj in list(module.__dict__.items()):
361 361 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
362 362 continue
363 363 key = (module.__name__, name)
364 364 try:
365 365 old_objects.setdefault(key, []).append(weakref.ref(obj))
366 366 except TypeError:
367 367 # weakref doesn't work for all types;
368 368 # create strong references for 'important' cases
369 369 if not PY3 and isinstance(obj, types.ClassType):
370 370 old_objects.setdefault(key, []).append(StrongRef(obj))
371 371
372 372 # reload module
373 373 try:
374 374 # clear namespace first from old cruft
375 375 old_dict = module.__dict__.copy()
376 376 old_name = module.__name__
377 377 module.__dict__.clear()
378 378 module.__dict__['__name__'] = old_name
379 379 module.__dict__['__loader__'] = old_dict['__loader__']
380 380 except (TypeError, AttributeError, KeyError):
381 381 pass
382 382
383 383 try:
384 384 module = reload(module)
385 385 except:
386 386 # restore module dictionary on failed reload
387 387 module.__dict__.update(old_dict)
388 388 raise
389 389
390 390 # iterate over all objects and update functions & classes
391 391 for name, new_obj in list(module.__dict__.items()):
392 392 key = (module.__name__, name)
393 393 if key not in old_objects: continue
394 394
395 395 new_refs = []
396 396 for old_ref in old_objects[key]:
397 397 old_obj = old_ref()
398 398 if old_obj is None: continue
399 399 new_refs.append(old_ref)
400 400 update_generic(old_obj, new_obj)
401 401
402 402 if new_refs:
403 403 old_objects[key] = new_refs
404 404 else:
405 405 del old_objects[key]
406 406
407 407 return module
408 408
409 409 #------------------------------------------------------------------------------
410 410 # IPython connectivity
411 411 #------------------------------------------------------------------------------
412 412
413 413 from IPython.core.magic import Magics, magics_class, line_magic
414 414
415 415 @magics_class
416 416 class AutoreloadMagics(Magics):
417 417 def __init__(self, *a, **kw):
418 418 super(AutoreloadMagics, self).__init__(*a, **kw)
419 419 self._reloader = ModuleReloader()
420 420 self._reloader.check_all = False
421 421 self.loaded_modules = set(sys.modules)
422 422
423 423 @line_magic
424 424 def autoreload(self, parameter_s=''):
425 425 r"""%autoreload => Reload modules automatically
426 426
427 427 %autoreload
428 428 Reload all modules (except those excluded by %aimport) automatically
429 429 now.
430 430
431 431 %autoreload 0
432 432 Disable automatic reloading.
433 433
434 434 %autoreload 1
435 435 Reload all modules imported with %aimport every time before executing
436 436 the Python code typed.
437 437
438 438 %autoreload 2
439 439 Reload all modules (except those excluded by %aimport) every time
440 440 before executing the Python code typed.
441 441
442 442 Reloading Python modules in a reliable way is in general
443 443 difficult, and unexpected things may occur. %autoreload tries to
444 444 work around common pitfalls by replacing function code objects and
445 445 parts of classes previously in the module with new versions. This
446 446 makes the following things to work:
447 447
448 448 - Functions and classes imported via 'from xxx import foo' are upgraded
449 449 to new versions when 'xxx' is reloaded.
450 450
451 451 - Methods and properties of classes are upgraded on reload, so that
452 452 calling 'c.foo()' on an object 'c' created before the reload causes
453 453 the new code for 'foo' to be executed.
454 454
455 455 Some of the known remaining caveats are:
456 456
457 457 - Replacing code objects does not always succeed: changing a @property
458 458 in a class to an ordinary method or a method to a member variable
459 459 can cause problems (but in old objects only).
460 460
461 461 - Functions that are removed (eg. via monkey-patching) from a module
462 462 before it is reloaded are not upgraded.
463 463
464 464 - C extension modules cannot be reloaded, and so cannot be
465 465 autoreloaded.
466 466
467 467 """
468 468 if parameter_s == '':
469 469 self._reloader.check(True)
470 470 elif parameter_s == '0':
471 471 self._reloader.enabled = False
472 472 elif parameter_s == '1':
473 473 self._reloader.check_all = False
474 474 self._reloader.enabled = True
475 475 elif parameter_s == '2':
476 476 self._reloader.check_all = True
477 477 self._reloader.enabled = True
478 478
479 479 @line_magic
480 480 def aimport(self, parameter_s='', stream=None):
481 481 """%aimport => Import modules for automatic reloading.
482 482
483 483 %aimport
484 484 List modules to automatically import and not to import.
485 485
486 486 %aimport foo
487 487 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
488 488
489 489 %aimport -foo
490 490 Mark module 'foo' to not be autoreloaded for %autoreload 1
491 491 """
492 492 modname = parameter_s
493 493 if not modname:
494 494 to_reload = sorted(self._reloader.modules.keys())
495 495 to_skip = sorted(self._reloader.skip_modules.keys())
496 496 if stream is None:
497 497 stream = sys.stdout
498 498 if self._reloader.check_all:
499 499 stream.write("Modules to reload:\nall-except-skipped\n")
500 500 else:
501 501 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
502 502 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
503 503 elif modname.startswith('-'):
504 504 modname = modname[1:]
505 505 self._reloader.mark_module_skipped(modname)
506 506 else:
507 507 top_module, top_name = self._reloader.aimport_module(modname)
508 508
509 509 # Inject module to user namespace
510 510 self.shell.push({top_name: top_module})
511 511
512 512 def pre_run_cell(self):
513 513 if self._reloader.enabled:
514 514 try:
515 515 self._reloader.check()
516 516 except:
517 517 pass
518 518
519 519 def post_execute_hook(self):
520 520 """Cache the modification times of any modules imported in this execution
521 521 """
522 522 newly_loaded_modules = set(sys.modules) - self.loaded_modules
523 523 for modname in newly_loaded_modules:
524 524 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
525 525 if pymtime is not None:
526 526 self._reloader.modules_mtimes[modname] = pymtime
527 527
528 528 self.loaded_modules.update(newly_loaded_modules)
529 529
530 530
531 531 def load_ipython_extension(ip):
532 532 """Load the extension in IPython."""
533 533 auto_reload = AutoreloadMagics(ip)
534 534 ip.register_magics(auto_reload)
535 535 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
536 536 ip.events.register('post_execute', auto_reload.post_execute_hook)
General Comments 0
You need to be logged in to leave comments. Login now