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