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