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