##// END OF EJS Templates
Add new methods in update_class()
oscar6echo -
Show More
@@ -1,523 +1,530 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 method code objects"""
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 for key in list(new.__dict__.keys()):
295 if key not in list(old.__dict__.keys()):
296 try:
297 setattr(old, key, getattr(new, key))
298 except (AttributeError, TypeError):
299 pass # skip non-writable attributes
300
294 301
295 302 def update_property(old, new):
296 303 """Replace get/set/del functions of a property"""
297 304 update_generic(old.fdel, new.fdel)
298 305 update_generic(old.fget, new.fget)
299 306 update_generic(old.fset, new.fset)
300 307
301 308
302 309 def isinstance2(a, b, typ):
303 310 return isinstance(a, typ) and isinstance(b, typ)
304 311
305 312
306 313 UPDATE_RULES = [
307 314 (lambda a, b: isinstance2(a, b, type),
308 315 update_class),
309 316 (lambda a, b: isinstance2(a, b, types.FunctionType),
310 317 update_function),
311 318 (lambda a, b: isinstance2(a, b, property),
312 319 update_property),
313 320 ]
314 321 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
315 322 lambda a, b: update_function(a.__func__, b.__func__)),
316 323 ])
317 324
318 325
319 326 def update_generic(a, b):
320 327 for type_check, update in UPDATE_RULES:
321 328 if type_check(a, b):
322 329 update(a, b)
323 330 return True
324 331 return False
325 332
326 333
327 334 class StrongRef(object):
328 335 def __init__(self, obj):
329 336 self.obj = obj
330 337 def __call__(self):
331 338 return self.obj
332 339
333 340
334 341 def superreload(module, reload=reload, old_objects={}):
335 342 """Enhanced version of the builtin reload function.
336 343
337 344 superreload remembers objects previously in the module, and
338 345
339 346 - upgrades the class dictionary of every old class in the module
340 347 - upgrades the code object of every old function and method
341 348 - clears the module's namespace before reloading
342 349
343 350 """
344 351
345 352 # collect old objects in the module
346 353 for name, obj in list(module.__dict__.items()):
347 354 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
348 355 continue
349 356 key = (module.__name__, name)
350 357 try:
351 358 old_objects.setdefault(key, []).append(weakref.ref(obj))
352 359 except TypeError:
353 360 pass
354 361
355 362 # reload module
356 363 try:
357 364 # clear namespace first from old cruft
358 365 old_dict = module.__dict__.copy()
359 366 old_name = module.__name__
360 367 module.__dict__.clear()
361 368 module.__dict__['__name__'] = old_name
362 369 module.__dict__['__loader__'] = old_dict['__loader__']
363 370 except (TypeError, AttributeError, KeyError):
364 371 pass
365 372
366 373 try:
367 374 module = reload(module)
368 375 except:
369 376 # restore module dictionary on failed reload
370 377 module.__dict__.update(old_dict)
371 378 raise
372 379
373 380 # iterate over all objects and update functions & classes
374 381 for name, new_obj in list(module.__dict__.items()):
375 382 key = (module.__name__, name)
376 383 if key not in old_objects: continue
377 384
378 385 new_refs = []
379 386 for old_ref in old_objects[key]:
380 387 old_obj = old_ref()
381 388 if old_obj is None: continue
382 389 new_refs.append(old_ref)
383 390 update_generic(old_obj, new_obj)
384 391
385 392 if new_refs:
386 393 old_objects[key] = new_refs
387 394 else:
388 395 del old_objects[key]
389 396
390 397 return module
391 398
392 399 #------------------------------------------------------------------------------
393 400 # IPython connectivity
394 401 #------------------------------------------------------------------------------
395 402
396 403 from IPython.core.magic import Magics, magics_class, line_magic
397 404
398 405 @magics_class
399 406 class AutoreloadMagics(Magics):
400 407 def __init__(self, *a, **kw):
401 408 super(AutoreloadMagics, self).__init__(*a, **kw)
402 409 self._reloader = ModuleReloader()
403 410 self._reloader.check_all = False
404 411 self.loaded_modules = set(sys.modules)
405 412
406 413 @line_magic
407 414 def autoreload(self, parameter_s=''):
408 415 r"""%autoreload => Reload modules automatically
409 416
410 417 %autoreload
411 418 Reload all modules (except those excluded by %aimport) automatically
412 419 now.
413 420
414 421 %autoreload 0
415 422 Disable automatic reloading.
416 423
417 424 %autoreload 1
418 425 Reload all modules imported with %aimport every time before executing
419 426 the Python code typed.
420 427
421 428 %autoreload 2
422 429 Reload all modules (except those excluded by %aimport) every time
423 430 before executing the Python code typed.
424 431
425 432 Reloading Python modules in a reliable way is in general
426 433 difficult, and unexpected things may occur. %autoreload tries to
427 434 work around common pitfalls by replacing function code objects and
428 435 parts of classes previously in the module with new versions. This
429 436 makes the following things to work:
430 437
431 438 - Functions and classes imported via 'from xxx import foo' are upgraded
432 439 to new versions when 'xxx' is reloaded.
433 440
434 441 - Methods and properties of classes are upgraded on reload, so that
435 442 calling 'c.foo()' on an object 'c' created before the reload causes
436 443 the new code for 'foo' to be executed.
437 444
438 445 Some of the known remaining caveats are:
439 446
440 447 - Replacing code objects does not always succeed: changing a @property
441 448 in a class to an ordinary method or a method to a member variable
442 449 can cause problems (but in old objects only).
443 450
444 451 - Functions that are removed (eg. via monkey-patching) from a module
445 452 before it is reloaded are not upgraded.
446 453
447 454 - C extension modules cannot be reloaded, and so cannot be
448 455 autoreloaded.
449 456
450 457 """
451 458 if parameter_s == '':
452 459 self._reloader.check(True)
453 460 elif parameter_s == '0':
454 461 self._reloader.enabled = False
455 462 elif parameter_s == '1':
456 463 self._reloader.check_all = False
457 464 self._reloader.enabled = True
458 465 elif parameter_s == '2':
459 466 self._reloader.check_all = True
460 467 self._reloader.enabled = True
461 468
462 469 @line_magic
463 470 def aimport(self, parameter_s='', stream=None):
464 471 """%aimport => Import modules for automatic reloading.
465 472
466 473 %aimport
467 474 List modules to automatically import and not to import.
468 475
469 476 %aimport foo
470 477 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
471 478
472 479 %aimport foo, bar
473 480 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
474 481
475 482 %aimport -foo
476 483 Mark module 'foo' to not be autoreloaded for %autoreload 1
477 484 """
478 485 modname = parameter_s
479 486 if not modname:
480 487 to_reload = sorted(self._reloader.modules.keys())
481 488 to_skip = sorted(self._reloader.skip_modules.keys())
482 489 if stream is None:
483 490 stream = sys.stdout
484 491 if self._reloader.check_all:
485 492 stream.write("Modules to reload:\nall-except-skipped\n")
486 493 else:
487 494 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
488 495 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
489 496 elif modname.startswith('-'):
490 497 modname = modname[1:]
491 498 self._reloader.mark_module_skipped(modname)
492 499 else:
493 500 for _module in ([_.strip() for _ in modname.split(',')]):
494 501 top_module, top_name = self._reloader.aimport_module(_module)
495 502
496 503 # Inject module to user namespace
497 504 self.shell.push({top_name: top_module})
498 505
499 506 def pre_run_cell(self):
500 507 if self._reloader.enabled:
501 508 try:
502 509 self._reloader.check()
503 510 except:
504 511 pass
505 512
506 513 def post_execute_hook(self):
507 514 """Cache the modification times of any modules imported in this execution
508 515 """
509 516 newly_loaded_modules = set(sys.modules) - self.loaded_modules
510 517 for modname in newly_loaded_modules:
511 518 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
512 519 if pymtime is not None:
513 520 self._reloader.modules_mtimes[modname] = pymtime
514 521
515 522 self.loaded_modules.update(newly_loaded_modules)
516 523
517 524
518 525 def load_ipython_extension(ip):
519 526 """Load the extension in IPython."""
520 527 auto_reload = AutoreloadMagics(ip)
521 528 ip.register_magics(auto_reload)
522 529 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
523 530 ip.events.register('post_execute', auto_reload.post_execute_hook)
General Comments 0
You need to be logged in to leave comments. Login now