Show More
@@ -100,21 +100,21 b' Some of the known remaining caveats are:' | |||
|
100 | 100 | |
|
101 | 101 | skip_doctest = True |
|
102 | 102 | |
|
103 | #----------------------------------------------------------------------------- | |
|
103 | # ----------------------------------------------------------------------------- | |
|
104 | 104 | # Copyright (C) 2000 Thomas Heller |
|
105 | 105 | # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi> |
|
106 | 106 | # Copyright (C) 2012 The IPython Development Team |
|
107 | 107 | # |
|
108 | 108 | # Distributed under the terms of the BSD License. The full license is in |
|
109 | 109 | # the file COPYING, distributed as part of this software. |
|
110 | #----------------------------------------------------------------------------- | |
|
110 | # ----------------------------------------------------------------------------- | |
|
111 | 111 | # |
|
112 | 112 | # This IPython module is written by Pauli Virtanen, based on the autoreload |
|
113 | 113 | # code by Thomas Heller. |
|
114 | 114 | |
|
115 | #----------------------------------------------------------------------------- | |
|
115 | # ----------------------------------------------------------------------------- | |
|
116 | 116 | # Imports |
|
117 | #----------------------------------------------------------------------------- | |
|
117 | # ----------------------------------------------------------------------------- | |
|
118 | 118 | |
|
119 | 119 | import os |
|
120 | 120 | import sys |
@@ -126,9 +126,10 b' from importlib import import_module' | |||
|
126 | 126 | from importlib.util import source_from_cache |
|
127 | 127 | from imp import reload |
|
128 | 128 | |
|
129 | #------------------------------------------------------------------------------ | |
|
129 | # ------------------------------------------------------------------------------ | |
|
130 | 130 | # Autoreload functionality |
|
131 | #------------------------------------------------------------------------------ | |
|
131 | # ------------------------------------------------------------------------------ | |
|
132 | ||
|
132 | 133 | |
|
133 | 134 | class ModuleReloader: |
|
134 | 135 | enabled = False |
@@ -186,22 +187,22 b' class ModuleReloader:' | |||
|
186 | 187 | self.mark_module_reloadable(module_name) |
|
187 | 188 | |
|
188 | 189 | import_module(module_name) |
|
189 |
top_name = module_name.split( |
|
|
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 |
if not hasattr(module, |
|
|
195 | if not hasattr(module, "__file__") or module.__file__ is None: | |
|
195 | 196 | return None, None |
|
196 | 197 | |
|
197 |
if getattr(module, |
|
|
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 |
if ext.lower() == |
|
|
205 | if ext.lower() == ".py": | |
|
205 | 206 | py_filename = filename |
|
206 | 207 | else: |
|
207 | 208 | try: |
@@ -259,17 +260,28 b' class ModuleReloader:' | |||
|
259 | 260 | if py_filename in self.failed: |
|
260 | 261 | del self.failed[py_filename] |
|
261 | 262 | except: |
|
262 | print("[autoreload of {} failed: {}]".format( | |
|
263 | modname, traceback.format_exc(10)), file=sys.stderr) | |
|
263 | print( | |
|
264 | "[autoreload of {} failed: {}]".format( | |
|
265 | modname, traceback.format_exc(10) | |
|
266 | ), | |
|
267 | file=sys.stderr, | |
|
268 | ) | |
|
264 | 269 | self.failed[py_filename] = pymtime |
|
265 | 270 | |
|
266 | #------------------------------------------------------------------------------ | |
|
271 | ||
|
272 | # ------------------------------------------------------------------------------ | |
|
267 | 273 | # superreload |
|
268 | #------------------------------------------------------------------------------ | |
|
274 | # ------------------------------------------------------------------------------ | |
|
269 | 275 | |
|
270 | 276 | |
|
271 | func_attrs = ['__code__', '__defaults__', '__doc__', | |
|
272 | '__closure__', '__globals__', '__dict__'] | |
|
277 | func_attrs = [ | |
|
278 | "__code__", | |
|
279 | "__defaults__", | |
|
280 | "__doc__", | |
|
281 | "__closure__", | |
|
282 | "__globals__", | |
|
283 | "__dict__", | |
|
284 | ] | |
|
273 | 285 | |
|
274 | 286 | |
|
275 | 287 | def update_function(old, new): |
@@ -285,7 +297,7 b' def update_instances(old, new):' | |||
|
285 | 297 | """Use garbage collector to find all instances that refer to the old |
|
286 | 298 | class definition and update their __class__ to point to the new class |
|
287 | 299 | definition""" |
|
288 | ||
|
300 | ||
|
289 | 301 | refs = gc.get_referrers(old) |
|
290 | 302 | |
|
291 | 303 | for ref in refs: |
@@ -312,19 +324,20 b' def update_class(old, new):' | |||
|
312 | 324 | pass |
|
313 | 325 | continue |
|
314 | 326 | |
|
315 |
if update_generic(old_obj, new_obj): |
|
|
327 | if update_generic(old_obj, new_obj): | |
|
328 | continue | |
|
316 | 329 | |
|
317 | 330 | try: |
|
318 | 331 | setattr(old, key, getattr(new, key)) |
|
319 | 332 | except (AttributeError, TypeError): |
|
320 | pass # skip non-writable attributes | |
|
333 | pass # skip non-writable attributes | |
|
321 | 334 | |
|
322 | 335 | for key in list(new.__dict__.keys()): |
|
323 | 336 | if key not in list(old.__dict__.keys()): |
|
324 | 337 | try: |
|
325 | 338 | setattr(old, key, getattr(new, key)) |
|
326 | 339 | except (AttributeError, TypeError): |
|
327 | pass # skip non-writable attributes | |
|
340 | pass # skip non-writable attributes | |
|
328 | 341 | |
|
329 | 342 | # update all instances of class |
|
330 | 343 | update_instances(old, new) |
@@ -342,16 +355,18 b' def isinstance2(a, b, typ):' | |||
|
342 | 355 | |
|
343 | 356 | |
|
344 | 357 | UPDATE_RULES = [ |
|
345 | (lambda a, b: isinstance2(a, b, type), | |
|
346 | update_class), | |
|
347 |
(lambda a, b: isinstance2(a, b, |
|
|
348 | update_function), | |
|
349 | (lambda a, b: isinstance2(a, b, property), | |
|
350 | update_property), | |
|
358 | (lambda a, b: isinstance2(a, b, type), update_class), | |
|
359 | (lambda a, b: isinstance2(a, b, types.FunctionType), update_function), | |
|
360 | (lambda a, b: isinstance2(a, b, property), update_property), | |
|
351 | 361 | ] |
|
352 | UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType), | |
|
353 | lambda a, b: update_function(a.__func__, b.__func__)), | |
|
354 | ]) | |
|
362 | UPDATE_RULES.extend( | |
|
363 | [ | |
|
364 | ( | |
|
365 | lambda a, b: isinstance2(a, b, types.MethodType), | |
|
366 | lambda a, b: update_function(a.__func__, b.__func__), | |
|
367 | ), | |
|
368 | ] | |
|
369 | ) | |
|
355 | 370 | |
|
356 | 371 | |
|
357 | 372 | def update_generic(a, b): |
@@ -365,15 +380,16 b' def update_generic(a, b):' | |||
|
365 | 380 | class StrongRef: |
|
366 | 381 | def __init__(self, obj): |
|
367 | 382 | self.obj = obj |
|
383 | ||
|
368 | 384 | def __call__(self): |
|
369 | 385 | return self.obj |
|
370 | 386 | |
|
371 | 387 | |
|
372 | 388 | def append_obj(module, d, name, obj, autoload=False): |
|
373 |
not_in_mod = not hasattr(obj, |
|
|
389 | not_in_mod = not hasattr(obj, "__module__") or obj.__module__ != module.__name__ | |
|
374 | 390 | if autoload: |
|
375 | 391 | # check needed for module global built-ins (int, str, dict,..) |
|
376 |
if name.startswith( |
|
|
392 | if name.startswith("__") and not_in_mod: | |
|
377 | 393 | return False |
|
378 | 394 | else: |
|
379 | 395 | if not_in_mod: |
@@ -416,8 +432,8 b' def superreload(module, reload=reload, old_objects=None, shell=None):' | |||
|
416 | 432 | old_dict = module.__dict__.copy() |
|
417 | 433 | old_name = module.__name__ |
|
418 | 434 | module.__dict__.clear() |
|
419 |
module.__dict__[ |
|
|
420 |
module.__dict__[ |
|
|
435 | module.__dict__["__name__"] = old_name | |
|
436 | module.__dict__["__loader__"] = old_dict["__loader__"] | |
|
421 | 437 | except (TypeError, AttributeError, KeyError): |
|
422 | 438 | pass |
|
423 | 439 | |
@@ -434,9 +450,9 b' def superreload(module, reload=reload, old_objects=None, shell=None):' | |||
|
434 | 450 | if key not in old_objects: |
|
435 | 451 | # here 'shell' acts both as a flag and as an output var |
|
436 | 452 | if ( |
|
437 |
shell is None |
|
|
438 |
name == |
|
|
439 | not append_obj(module, old_objects, name, new_obj, True) | |
|
453 | shell is None | |
|
454 | or name == "Enum" | |
|
455 | or not append_obj(module, old_objects, name, new_obj, True) | |
|
440 | 456 | ): |
|
441 | 457 | continue |
|
442 | 458 | shell.user_ns[name] = new_obj |
@@ -444,7 +460,8 b' def superreload(module, reload=reload, old_objects=None, shell=None):' | |||
|
444 | 460 | new_refs = [] |
|
445 | 461 | for old_ref in old_objects[key]: |
|
446 | 462 | old_obj = old_ref() |
|
447 |
if old_obj is None: |
|
|
463 | if old_obj is None: | |
|
464 | continue | |
|
448 | 465 | new_refs.append(old_ref) |
|
449 | 466 | update_generic(old_obj, new_obj) |
|
450 | 467 | |
@@ -455,12 +472,14 b' def superreload(module, reload=reload, old_objects=None, shell=None):' | |||
|
455 | 472 | |
|
456 | 473 | return module |
|
457 | 474 | |
|
458 | #------------------------------------------------------------------------------ | |
|
475 | ||
|
476 | # ------------------------------------------------------------------------------ | |
|
459 | 477 | # IPython connectivity |
|
460 | #------------------------------------------------------------------------------ | |
|
478 | # ------------------------------------------------------------------------------ | |
|
461 | 479 | |
|
462 | 480 | from IPython.core.magic import Magics, magics_class, line_magic |
|
463 | 481 | |
|
482 | ||
|
464 | 483 | @magics_class |
|
465 | 484 | class AutoreloadMagics(Magics): |
|
466 | 485 | def __init__(self, *a, **kw): |
@@ -471,7 +490,7 b' class AutoreloadMagics(Magics):' | |||
|
471 | 490 | self.loaded_modules = set(sys.modules) |
|
472 | 491 | |
|
473 | 492 | @line_magic |
|
474 |
def autoreload(self, parameter_s= |
|
|
493 | def autoreload(self, parameter_s=""): | |
|
475 | 494 | r"""%autoreload => Reload modules automatically |
|
476 | 495 | |
|
477 | 496 | %autoreload |
@@ -515,24 +534,24 b' class AutoreloadMagics(Magics):' | |||
|
515 | 534 | autoreloaded. |
|
516 | 535 | |
|
517 | 536 | """ |
|
518 |
if parameter_s == |
|
|
537 | if parameter_s == "": | |
|
519 | 538 | self._reloader.check(True) |
|
520 |
elif parameter_s == |
|
|
539 | elif parameter_s == "0": | |
|
521 | 540 | self._reloader.enabled = False |
|
522 |
elif parameter_s == |
|
|
541 | elif parameter_s == "1": | |
|
523 | 542 | self._reloader.check_all = False |
|
524 | 543 | self._reloader.enabled = True |
|
525 |
elif parameter_s == |
|
|
544 | elif parameter_s == "2": | |
|
526 | 545 | self._reloader.check_all = True |
|
527 | 546 | self._reloader.enabled = True |
|
528 | 547 | self._reloader.enabled = True |
|
529 |
elif parameter_s == |
|
|
548 | elif parameter_s == "3": | |
|
530 | 549 | self._reloader.check_all = True |
|
531 | 550 | self._reloader.enabled = True |
|
532 | 551 | self._reloader.autoload_obj = True |
|
533 | 552 | |
|
534 | 553 | @line_magic |
|
535 |
def aimport(self, parameter_s= |
|
|
554 | def aimport(self, parameter_s="", stream=None): | |
|
536 | 555 | """%aimport => Import modules for automatic reloading. |
|
537 | 556 | |
|
538 | 557 | %aimport |
@@ -556,13 +575,13 b' class AutoreloadMagics(Magics):' | |||
|
556 | 575 | if self._reloader.check_all: |
|
557 | 576 | stream.write("Modules to reload:\nall-except-skipped\n") |
|
558 | 577 | else: |
|
559 |
stream.write("Modules to reload:\n%s\n" % |
|
|
560 |
stream.write("\nModules to skip:\n%s\n" % |
|
|
561 |
elif modname.startswith( |
|
|
578 | stream.write("Modules to reload:\n%s\n" % " ".join(to_reload)) | |
|
579 | stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip)) | |
|
580 | elif modname.startswith("-"): | |
|
562 | 581 | modname = modname[1:] |
|
563 | 582 | self._reloader.mark_module_skipped(modname) |
|
564 | 583 | else: |
|
565 |
for _module in |
|
|
584 | for _module in [_.strip() for _ in modname.split(",")]: | |
|
566 | 585 | top_module, top_name = self._reloader.aimport_module(_module) |
|
567 | 586 | |
|
568 | 587 | # Inject module to user namespace |
@@ -576,8 +595,7 b' class AutoreloadMagics(Magics):' | |||
|
576 | 595 | pass |
|
577 | 596 | |
|
578 | 597 | def post_execute_hook(self): |
|
579 | """Cache the modification times of any modules imported in this execution | |
|
580 | """ | |
|
598 | """Cache the modification times of any modules imported in this execution""" | |
|
581 | 599 | newly_loaded_modules = set(sys.modules) - self.loaded_modules |
|
582 | 600 | for modname in newly_loaded_modules: |
|
583 | 601 | _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname]) |
@@ -591,5 +609,5 b' def load_ipython_extension(ip):' | |||
|
591 | 609 | """Load the extension in IPython.""" |
|
592 | 610 | auto_reload = AutoreloadMagics(ip) |
|
593 | 611 | ip.register_magics(auto_reload) |
|
594 |
ip.events.register( |
|
|
595 |
ip.events.register( |
|
|
612 | ip.events.register("pre_run_cell", auto_reload.pre_run_cell) | |
|
613 | ip.events.register("post_execute", auto_reload.post_execute_hook) |
General Comments 0
You need to be logged in to leave comments.
Login now