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