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