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