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