##// END OF EJS Templates
Verbose version of `%autoreload` command
Emilio Graff -
Show More
@@ -1,625 +1,630 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 ``%autoreload``
32 ``%autoreload``, ``%autoreload now``
33 33
34 34 Reload all modules (except those excluded by ``%aimport``)
35 35 automatically now.
36 36
37 ``%autoreload 0``
37 ``%autoreload 0``, ``%autoreload off``
38 38
39 39 Disable automatic reloading.
40 40
41 ``%autoreload 1``
41 ``%autoreload 1``, ``%autoreload explicit``
42 42
43 43 Reload all modules imported with ``%aimport`` every time before
44 44 executing the Python code typed.
45 45
46 ``%autoreload 2``
46 ``%autoreload 2``, ``%autoreload all``
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 ``%autoreload 3``
51 ``%autoreload 3``, ``%autoreload complete``
52 52
53 Reload all modules AND autoload newly added objects
54 every time before executing the Python code typed.
53 Reload all modules (except those excluded by ``%aimport``) AND
54 autoload newly added objects every time before executing the
55 Python code typed.
55 56
56 57 ``%aimport``
57 58
58 59 List modules which are to be automatically imported or not to be imported.
59 60
60 61 ``%aimport foo``
61 62
62 63 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
63 64
64 65 ``%aimport foo, bar``
65 66
66 67 Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1``
67 68
68 69 ``%aimport -foo``
69 70
70 71 Mark module 'foo' to not be autoreloaded.
71 72
72 73 Caveats
73 74 =======
74 75
75 76 Reloading Python modules in a reliable way is in general difficult,
76 77 and unexpected things may occur. ``%autoreload`` tries to work around
77 78 common pitfalls by replacing function code objects and parts of
78 79 classes previously in the module with new versions. This makes the
79 80 following things to work:
80 81
81 82 - Functions and classes imported via 'from xxx import foo' are upgraded
82 83 to new versions when 'xxx' is reloaded.
83 84
84 85 - Methods and properties of classes are upgraded on reload, so that
85 86 calling 'c.foo()' on an object 'c' created before the reload causes
86 87 the new code for 'foo' to be executed.
87 88
88 89 Some of the known remaining caveats are:
89 90
90 91 - Replacing code objects does not always succeed: changing a @property
91 92 in a class to an ordinary method or a method to a member variable
92 93 can cause problems (but in old objects only).
93 94
94 95 - Functions that are removed (eg. via monkey-patching) from a module
95 96 before it is reloaded are not upgraded.
96 97
97 98 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
98 99 """
99 100
100 101 from IPython.core.magic import Magics, magics_class, line_magic
101 102 __skip_doctest__ = True
102 103
103 104 # -----------------------------------------------------------------------------
104 105 # Copyright (C) 2000 Thomas Heller
105 106 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
106 107 # Copyright (C) 2012 The IPython Development Team
107 108 #
108 109 # Distributed under the terms of the BSD License. The full license is in
109 110 # the file COPYING, distributed as part of this software.
110 111 # -----------------------------------------------------------------------------
111 112 #
112 113 # This IPython module is written by Pauli Virtanen, based on the autoreload
113 114 # code by Thomas Heller.
114 115
115 116 # -----------------------------------------------------------------------------
116 117 # Imports
117 118 # -----------------------------------------------------------------------------
118 119
119 120 import os
120 121 import sys
121 122 import traceback
122 123 import types
123 124 import weakref
124 125 import gc
125 126 from importlib import import_module, reload
126 127 from importlib.util import source_from_cache
127 128
128 129 # ------------------------------------------------------------------------------
129 130 # Autoreload functionality
130 131 # ------------------------------------------------------------------------------
131 132
132 133
133 134 class ModuleReloader:
134 135 enabled = False
135 136 """Whether this reloader is enabled"""
136 137
137 138 check_all = True
138 139 """Autoreload all modules, not just those listed in 'modules'"""
139 140
140 141 autoload_obj = False
141 142 """Autoreload all modules AND autoload all new objects"""
142 143
143 144 def __init__(self, shell=None):
144 145 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
145 146 self.failed = {}
146 147 # Modules specially marked as autoreloadable.
147 148 self.modules = {}
148 149 # Modules specially marked as not autoreloadable.
149 150 self.skip_modules = {}
150 151 # (module-name, name) -> weakref, for replacing old code objects
151 152 self.old_objects = {}
152 153 # Module modification timestamps
153 154 self.modules_mtimes = {}
154 155 self.shell = shell
155 156
156 157 # Cache module modification times
157 158 self.check(check_all=True, do_reload=False)
158 159
159 160 def mark_module_skipped(self, module_name):
160 161 """Skip reloading the named module in the future"""
161 162 try:
162 163 del self.modules[module_name]
163 164 except KeyError:
164 165 pass
165 166 self.skip_modules[module_name] = True
166 167
167 168 def mark_module_reloadable(self, module_name):
168 169 """Reload the named module in the future (if it is imported)"""
169 170 try:
170 171 del self.skip_modules[module_name]
171 172 except KeyError:
172 173 pass
173 174 self.modules[module_name] = True
174 175
175 176 def aimport_module(self, module_name):
176 177 """Import a module, and mark it reloadable
177 178
178 179 Returns
179 180 -------
180 181 top_module : module
181 182 The imported module if it is top-level, or the top-level
182 183 top_name : module
183 184 Name of top_module
184 185
185 186 """
186 187 self.mark_module_reloadable(module_name)
187 188
188 189 import_module(module_name)
189 190 top_name = module_name.split(".")[0]
190 191 top_module = sys.modules[top_name]
191 192 return top_module, top_name
192 193
193 194 def filename_and_mtime(self, module):
194 195 if not hasattr(module, "__file__") or module.__file__ is None:
195 196 return None, None
196 197
197 198 if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]:
198 199 # we cannot reload(__main__) or reload(__mp_main__)
199 200 return None, None
200 201
201 202 filename = module.__file__
202 203 path, ext = os.path.splitext(filename)
203 204
204 205 if ext.lower() == ".py":
205 206 py_filename = filename
206 207 else:
207 208 try:
208 209 py_filename = source_from_cache(filename)
209 210 except ValueError:
210 211 return None, None
211 212
212 213 try:
213 214 pymtime = os.stat(py_filename).st_mtime
214 215 except OSError:
215 216 return None, None
216 217
217 218 return py_filename, pymtime
218 219
219 220 def check(self, check_all=False, do_reload=True):
220 221 """Check whether some modules need to be reloaded."""
221 222
222 223 if not self.enabled and not check_all:
223 224 return
224 225
225 226 if check_all or self.check_all:
226 227 modules = list(sys.modules.keys())
227 228 else:
228 229 modules = list(self.modules.keys())
229 230
230 231 for modname in modules:
231 232 m = sys.modules.get(modname, None)
232 233
233 234 if modname in self.skip_modules:
234 235 continue
235 236
236 237 py_filename, pymtime = self.filename_and_mtime(m)
237 238 if py_filename is None:
238 239 continue
239 240
240 241 try:
241 242 if pymtime <= self.modules_mtimes[modname]:
242 243 continue
243 244 except KeyError:
244 245 self.modules_mtimes[modname] = pymtime
245 246 continue
246 247 else:
247 248 if self.failed.get(py_filename, None) == pymtime:
248 249 continue
249 250
250 251 self.modules_mtimes[modname] = pymtime
251 252
252 253 # If we've reached this point, we should try to reload the module
253 254 if do_reload:
254 255 try:
255 256 if self.autoload_obj:
256 257 superreload(m, reload, self.old_objects, self.shell)
257 258 else:
258 259 superreload(m, reload, self.old_objects)
259 260 if py_filename in self.failed:
260 261 del self.failed[py_filename]
261 262 except:
262 263 print(
263 264 "[autoreload of {} failed: {}]".format(
264 265 modname, traceback.format_exc(10)
265 266 ),
266 267 file=sys.stderr,
267 268 )
268 269 self.failed[py_filename] = pymtime
269 270
270 271
271 272 # ------------------------------------------------------------------------------
272 273 # superreload
273 274 # ------------------------------------------------------------------------------
274 275
275 276
276 277 func_attrs = [
277 278 "__code__",
278 279 "__defaults__",
279 280 "__doc__",
280 281 "__closure__",
281 282 "__globals__",
282 283 "__dict__",
283 284 ]
284 285
285 286
286 287 def update_function(old, new):
287 288 """Upgrade the code object of a function"""
288 289 for name in func_attrs:
289 290 try:
290 291 setattr(old, name, getattr(new, name))
291 292 except (AttributeError, TypeError):
292 293 pass
293 294
294 295
295 296 def update_instances(old, new):
296 297 """Use garbage collector to find all instances that refer to the old
297 298 class definition and update their __class__ to point to the new class
298 299 definition"""
299 300
300 301 refs = gc.get_referrers(old)
301 302
302 303 for ref in refs:
303 304 if type(ref) is old:
304 305 object.__setattr__(ref, "__class__", new)
305 306
306 307
307 308 def update_class(old, new):
308 309 """Replace stuff in the __dict__ of a class, and upgrade
309 310 method code objects, and add new methods, if any"""
310 311 for key in list(old.__dict__.keys()):
311 312 old_obj = getattr(old, key)
312 313 try:
313 314 new_obj = getattr(new, key)
314 315 # explicitly checking that comparison returns True to handle
315 316 # cases where `==` doesn't return a boolean.
316 317 if (old_obj == new_obj) is True:
317 318 continue
318 319 except AttributeError:
319 320 # obsolete attribute: remove it
320 321 try:
321 322 delattr(old, key)
322 323 except (AttributeError, TypeError):
323 324 pass
324 325 continue
325 326 except ValueError:
326 327 # can't compare nested structures containing
327 328 # numpy arrays using `==`
328 329 pass
329 330
330 331 if update_generic(old_obj, new_obj):
331 332 continue
332 333
333 334 try:
334 335 setattr(old, key, getattr(new, key))
335 336 except (AttributeError, TypeError):
336 337 pass # skip non-writable attributes
337 338
338 339 for key in list(new.__dict__.keys()):
339 340 if key not in list(old.__dict__.keys()):
340 341 try:
341 342 setattr(old, key, getattr(new, key))
342 343 except (AttributeError, TypeError):
343 344 pass # skip non-writable attributes
344 345
345 346 # update all instances of class
346 347 update_instances(old, new)
347 348
348 349
349 350 def update_property(old, new):
350 351 """Replace get/set/del functions of a property"""
351 352 update_generic(old.fdel, new.fdel)
352 353 update_generic(old.fget, new.fget)
353 354 update_generic(old.fset, new.fset)
354 355
355 356
356 357 def isinstance2(a, b, typ):
357 358 return isinstance(a, typ) and isinstance(b, typ)
358 359
359 360
360 361 UPDATE_RULES = [
361 362 (lambda a, b: isinstance2(a, b, type), update_class),
362 363 (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
363 364 (lambda a, b: isinstance2(a, b, property), update_property),
364 365 ]
365 366 UPDATE_RULES.extend(
366 367 [
367 368 (
368 369 lambda a, b: isinstance2(a, b, types.MethodType),
369 370 lambda a, b: update_function(a.__func__, b.__func__),
370 371 ),
371 372 ]
372 373 )
373 374
374 375
375 376 def update_generic(a, b):
376 377 for type_check, update in UPDATE_RULES:
377 378 if type_check(a, b):
378 379 update(a, b)
379 380 return True
380 381 return False
381 382
382 383
383 384 class StrongRef:
384 385 def __init__(self, obj):
385 386 self.obj = obj
386 387
387 388 def __call__(self):
388 389 return self.obj
389 390
390 391
391 392 mod_attrs = [
392 393 "__name__",
393 394 "__doc__",
394 395 "__package__",
395 396 "__loader__",
396 397 "__spec__",
397 398 "__file__",
398 399 "__cached__",
399 400 "__builtins__",
400 401 ]
401 402
402 403
403 404 def append_obj(module, d, name, obj, autoload=False):
404 405 in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__
405 406 if autoload:
406 407 # check needed for module global built-ins
407 408 if not in_module and name in mod_attrs:
408 409 return False
409 410 else:
410 411 if not in_module:
411 412 return False
412 413
413 414 key = (module.__name__, name)
414 415 try:
415 416 d.setdefault(key, []).append(weakref.ref(obj))
416 417 except TypeError:
417 418 pass
418 419 return True
419 420
420 421
421 422 def superreload(module, reload=reload, old_objects=None, shell=None):
422 423 """Enhanced version of the builtin reload function.
423 424
424 425 superreload remembers objects previously in the module, and
425 426
426 427 - upgrades the class dictionary of every old class in the module
427 428 - upgrades the code object of every old function and method
428 429 - clears the module's namespace before reloading
429 430
430 431 """
431 432 if old_objects is None:
432 433 old_objects = {}
433 434
434 435 # collect old objects in the module
435 436 for name, obj in list(module.__dict__.items()):
436 437 if not append_obj(module, old_objects, name, obj):
437 438 continue
438 439 key = (module.__name__, name)
439 440 try:
440 441 old_objects.setdefault(key, []).append(weakref.ref(obj))
441 442 except TypeError:
442 443 pass
443 444
444 445 # reload module
445 446 try:
446 447 # clear namespace first from old cruft
447 448 old_dict = module.__dict__.copy()
448 449 old_name = module.__name__
449 450 module.__dict__.clear()
450 451 module.__dict__["__name__"] = old_name
451 452 module.__dict__["__loader__"] = old_dict["__loader__"]
452 453 except (TypeError, AttributeError, KeyError):
453 454 pass
454 455
455 456 try:
456 457 module = reload(module)
457 458 except:
458 459 # restore module dictionary on failed reload
459 460 module.__dict__.update(old_dict)
460 461 raise
461 462
462 463 # iterate over all objects and update functions & classes
463 464 for name, new_obj in list(module.__dict__.items()):
464 465 key = (module.__name__, name)
465 466 if key not in old_objects:
466 467 # here 'shell' acts both as a flag and as an output var
467 468 if (
468 469 shell is None
469 470 or name == "Enum"
470 471 or not append_obj(module, old_objects, name, new_obj, True)
471 472 ):
472 473 continue
473 474 shell.user_ns[name] = new_obj
474 475
475 476 new_refs = []
476 477 for old_ref in old_objects[key]:
477 478 old_obj = old_ref()
478 479 if old_obj is None:
479 480 continue
480 481 new_refs.append(old_ref)
481 482 update_generic(old_obj, new_obj)
482 483
483 484 if new_refs:
484 485 old_objects[key] = new_refs
485 486 else:
486 487 del old_objects[key]
487 488
488 489 return module
489 490
490 491
491 492 # ------------------------------------------------------------------------------
492 493 # IPython connectivity
493 494 # ------------------------------------------------------------------------------
494 495
495 496
496 497 @magics_class
497 498 class AutoreloadMagics(Magics):
498 499 def __init__(self, *a, **kw):
499 500 super().__init__(*a, **kw)
500 501 self._reloader = ModuleReloader(self.shell)
501 502 self._reloader.check_all = False
502 503 self._reloader.autoload_obj = False
503 504 self.loaded_modules = set(sys.modules)
504 505
505 506 @line_magic
506 507 def autoreload(self, parameter_s=""):
507 508 r"""%autoreload => Reload modules automatically
508 509
509 %autoreload
510 %autoreload or %autoreload now
510 511 Reload all modules (except those excluded by %aimport) automatically
511 512 now.
512 513
513 %autoreload 0
514 %autoreload 0 or %autoreload off
514 515 Disable automatic reloading.
515 516
516 %autoreload 1
517 Reload all modules imported with %aimport every time before executing
517 %autoreload 1 or %autoreload explicit
518 Reload only modules imported with %aimport every time before executing
518 519 the Python code typed.
519 520
520 %autoreload 2
521 %autoreload 2 or %autoreload all
522 Reload all modules (except those excluded by %aimport) every time
523 before executing the Python code typed.
524
525 %autoreload 3 or %autoreload complete
521 526 Reload all modules (except those excluded by %aimport) every time
522 527 before executing the Python code typed.
523 528
524 529 Reloading Python modules in a reliable way is in general
525 530 difficult, and unexpected things may occur. %autoreload tries to
526 531 work around common pitfalls by replacing function code objects and
527 532 parts of classes previously in the module with new versions. This
528 533 makes the following things to work:
529 534
530 535 - Functions and classes imported via 'from xxx import foo' are upgraded
531 536 to new versions when 'xxx' is reloaded.
532 537
533 538 - Methods and properties of classes are upgraded on reload, so that
534 539 calling 'c.foo()' on an object 'c' created before the reload causes
535 540 the new code for 'foo' to be executed.
536 541
537 542 Some of the known remaining caveats are:
538 543
539 544 - Replacing code objects does not always succeed: changing a @property
540 545 in a class to an ordinary method or a method to a member variable
541 546 can cause problems (but in old objects only).
542 547
543 548 - Functions that are removed (eg. via monkey-patching) from a module
544 549 before it is reloaded are not upgraded.
545 550
546 551 - C extension modules cannot be reloaded, and so cannot be
547 552 autoreloaded.
548 553
549 554 """
550 if parameter_s == "":
555 if parameter_s == "" or parameter_s.lower() == "now":
551 556 self._reloader.check(True)
552 elif parameter_s == "0":
557 elif parameter_s == "0" or parameter_s.lower() == "off":
553 558 self._reloader.enabled = False
554 elif parameter_s == "1":
559 elif parameter_s == "1" or parameter_s.lower() == "explicit":
555 560 self._reloader.check_all = False
556 561 self._reloader.enabled = True
557 elif parameter_s == "2":
562 elif parameter_s == "2" or parameter_s.lower() == "all":
558 563 self._reloader.check_all = True
559 564 self._reloader.enabled = True
560 elif parameter_s == "3":
565 elif parameter_s == "3" or parameter_s.lower() == "complete":
561 566 self._reloader.check_all = True
562 567 self._reloader.enabled = True
563 568 self._reloader.autoload_obj = True
564 569
565 570 @line_magic
566 571 def aimport(self, parameter_s="", stream=None):
567 572 """%aimport => Import modules for automatic reloading.
568 573
569 574 %aimport
570 575 List modules to automatically import and not to import.
571 576
572 577 %aimport foo
573 578 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
574 579
575 580 %aimport foo, bar
576 581 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
577 582
578 583 %aimport -foo
579 Mark module 'foo' to not be autoreloaded for %autoreload 1
584 Mark module 'foo' to not be autoreloaded for %autoreload 1, 2, or 3
580 585 """
581 586 modname = parameter_s
582 587 if not modname:
583 588 to_reload = sorted(self._reloader.modules.keys())
584 589 to_skip = sorted(self._reloader.skip_modules.keys())
585 590 if stream is None:
586 591 stream = sys.stdout
587 592 if self._reloader.check_all:
588 593 stream.write("Modules to reload:\nall-except-skipped\n")
589 594 else:
590 595 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
591 596 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
592 597 elif modname.startswith("-"):
593 598 modname = modname[1:]
594 599 self._reloader.mark_module_skipped(modname)
595 600 else:
596 601 for _module in [_.strip() for _ in modname.split(",")]:
597 602 top_module, top_name = self._reloader.aimport_module(_module)
598 603
599 604 # Inject module to user namespace
600 605 self.shell.push({top_name: top_module})
601 606
602 607 def pre_run_cell(self):
603 608 if self._reloader.enabled:
604 609 try:
605 610 self._reloader.check()
606 611 except:
607 612 pass
608 613
609 614 def post_execute_hook(self):
610 615 """Cache the modification times of any modules imported in this execution"""
611 616 newly_loaded_modules = set(sys.modules) - self.loaded_modules
612 617 for modname in newly_loaded_modules:
613 618 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
614 619 if pymtime is not None:
615 620 self._reloader.modules_mtimes[modname] = pymtime
616 621
617 622 self.loaded_modules.update(newly_loaded_modules)
618 623
619 624
620 625 def load_ipython_extension(ip):
621 626 """Load the extension in IPython."""
622 627 auto_reload = AutoreloadMagics(ip)
623 628 ip.register_magics(auto_reload)
624 629 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
625 630 ip.events.register("post_execute", auto_reload.post_execute_hook)
General Comments 0
You need to be logged in to leave comments. Login now