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