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