##// END OF EJS Templates
Apply pyupgrade to IPython/extensions/autoreload.py...
Spas Kalaydzhisyki -
Show More
@@ -1,612 +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 = [
388 "__name__",
389 "__doc__",
390 "__package__",
391 "__loader__",
392 "__spec__",
393 "__file__",
394 "__cached__",
395 "__builtins__"
396 ]
397
398
387 def append_obj(module, d, name, obj, autoload=False):
399 def append_obj(module, d, name, obj, autoload=False):
388 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__
389 if autoload:
401 if autoload:
390 # check needed for module global built-ins (int, str, dict,..)
402 # check needed for module global built-ins
391 if name.startswith("__") and not_in_mod:
403 if not_in_mod and name in mod_attrs:
392 return False
404 return False
393 else:
405 else:
394 if not_in_mod:
406 if not_in_mod:
395 return False
407 return False
396
408
397 key = (module.__name__, name)
409 key = (module.__name__, name)
398 try:
410 try:
399 d.setdefault(key, []).append(weakref.ref(obj))
411 d.setdefault(key, []).append(weakref.ref(obj))
400 except TypeError:
412 except TypeError:
401 pass
413 pass
402 return True
414 return True
403
415
404
416
405 def superreload(module, reload=reload, old_objects=None, shell=None):
417 def superreload(module, reload=reload, old_objects=None, shell=None):
406 """Enhanced version of the builtin reload function.
418 """Enhanced version of the builtin reload function.
407
419
408 superreload remembers objects previously in the module, and
420 superreload remembers objects previously in the module, and
409
421
410 - upgrades the class dictionary of every old class in the module
422 - upgrades the class dictionary of every old class in the module
411 - upgrades the code object of every old function and method
423 - upgrades the code object of every old function and method
412 - clears the module's namespace before reloading
424 - clears the module's namespace before reloading
413
425
414 """
426 """
415 if old_objects is None:
427 if old_objects is None:
416 old_objects = {}
428 old_objects = {}
417
429
418 # collect old objects in the module
430 # collect old objects in the module
419 for name, obj in list(module.__dict__.items()):
431 for name, obj in list(module.__dict__.items()):
420 if not append_obj(module, old_objects, name, obj):
432 if not append_obj(module, old_objects, name, obj):
421 continue
433 continue
422 key = (module.__name__, name)
434 key = (module.__name__, name)
423 try:
435 try:
424 old_objects.setdefault(key, []).append(weakref.ref(obj))
436 old_objects.setdefault(key, []).append(weakref.ref(obj))
425 except TypeError:
437 except TypeError:
426 pass
438 pass
427
439
428 # reload module
440 # reload module
429 try:
441 try:
430 # clear namespace first from old cruft
442 # clear namespace first from old cruft
431 old_dict = module.__dict__.copy()
443 old_dict = module.__dict__.copy()
432 old_name = module.__name__
444 old_name = module.__name__
433 module.__dict__.clear()
445 module.__dict__.clear()
434 module.__dict__["__name__"] = old_name
446 module.__dict__["__name__"] = old_name
435 module.__dict__["__loader__"] = old_dict["__loader__"]
447 module.__dict__["__loader__"] = old_dict["__loader__"]
436 except (TypeError, AttributeError, KeyError):
448 except (TypeError, AttributeError, KeyError):
437 pass
449 pass
438
450
439 try:
451 try:
440 module = reload(module)
452 module = reload(module)
441 except:
453 except:
442 # restore module dictionary on failed reload
454 # restore module dictionary on failed reload
443 module.__dict__.update(old_dict)
455 module.__dict__.update(old_dict)
444 raise
456 raise
445
457
446 # iterate over all objects and update functions & classes
458 # iterate over all objects and update functions & classes
447 for name, new_obj in list(module.__dict__.items()):
459 for name, new_obj in list(module.__dict__.items()):
448 key = (module.__name__, name)
460 key = (module.__name__, name)
449 if key not in old_objects:
461 if key not in old_objects:
450 # 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
451 if (
463 if (
452 shell is None
464 shell is None
453 or name == "Enum"
465 or name == "Enum"
454 or not append_obj(module, old_objects, name, new_obj, True)
466 or not append_obj(module, old_objects, name, new_obj, True)
455 ):
467 ):
456 continue
468 continue
457 shell.user_ns[name] = new_obj
469 shell.user_ns[name] = new_obj
458
470
459 new_refs = []
471 new_refs = []
460 for old_ref in old_objects[key]:
472 for old_ref in old_objects[key]:
461 old_obj = old_ref()
473 old_obj = old_ref()
462 if old_obj is None:
474 if old_obj is None:
463 continue
475 continue
464 new_refs.append(old_ref)
476 new_refs.append(old_ref)
465 update_generic(old_obj, new_obj)
477 update_generic(old_obj, new_obj)
466
478
467 if new_refs:
479 if new_refs:
468 old_objects[key] = new_refs
480 old_objects[key] = new_refs
469 else:
481 else:
470 del old_objects[key]
482 del old_objects[key]
471
483
472 return module
484 return module
473
485
474
486
475 # ------------------------------------------------------------------------------
487 # ------------------------------------------------------------------------------
476 # IPython connectivity
488 # IPython connectivity
477 # ------------------------------------------------------------------------------
489 # ------------------------------------------------------------------------------
478
490
479 from IPython.core.magic import Magics, magics_class, line_magic
491 from IPython.core.magic import Magics, magics_class, line_magic
480
492
481
493
482 @magics_class
494 @magics_class
483 class AutoreloadMagics(Magics):
495 class AutoreloadMagics(Magics):
484 def __init__(self, *a, **kw):
496 def __init__(self, *a, **kw):
485 super().__init__(*a, **kw)
497 super().__init__(*a, **kw)
486 self._reloader = ModuleReloader(self.shell)
498 self._reloader = ModuleReloader(self.shell)
487 self._reloader.check_all = False
499 self._reloader.check_all = False
488 self._reloader.autoload_obj = False
500 self._reloader.autoload_obj = False
489 self.loaded_modules = set(sys.modules)
501 self.loaded_modules = set(sys.modules)
490
502
491 @line_magic
503 @line_magic
492 def autoreload(self, parameter_s=""):
504 def autoreload(self, parameter_s=""):
493 r"""%autoreload => Reload modules automatically
505 r"""%autoreload => Reload modules automatically
494
506
495 %autoreload
507 %autoreload
496 Reload all modules (except those excluded by %aimport) automatically
508 Reload all modules (except those excluded by %aimport) automatically
497 now.
509 now.
498
510
499 %autoreload 0
511 %autoreload 0
500 Disable automatic reloading.
512 Disable automatic reloading.
501
513
502 %autoreload 1
514 %autoreload 1
503 Reload all modules imported with %aimport every time before executing
515 Reload all modules imported with %aimport every time before executing
504 the Python code typed.
516 the Python code typed.
505
517
506 %autoreload 2
518 %autoreload 2
507 Reload all modules (except those excluded by %aimport) every time
519 Reload all modules (except those excluded by %aimport) every time
508 before executing the Python code typed.
520 before executing the Python code typed.
509
521
510 Reloading Python modules in a reliable way is in general
522 Reloading Python modules in a reliable way is in general
511 difficult, and unexpected things may occur. %autoreload tries to
523 difficult, and unexpected things may occur. %autoreload tries to
512 work around common pitfalls by replacing function code objects and
524 work around common pitfalls by replacing function code objects and
513 parts of classes previously in the module with new versions. This
525 parts of classes previously in the module with new versions. This
514 makes the following things to work:
526 makes the following things to work:
515
527
516 - Functions and classes imported via 'from xxx import foo' are upgraded
528 - Functions and classes imported via 'from xxx import foo' are upgraded
517 to new versions when 'xxx' is reloaded.
529 to new versions when 'xxx' is reloaded.
518
530
519 - Methods and properties of classes are upgraded on reload, so that
531 - Methods and properties of classes are upgraded on reload, so that
520 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
521 the new code for 'foo' to be executed.
533 the new code for 'foo' to be executed.
522
534
523 Some of the known remaining caveats are:
535 Some of the known remaining caveats are:
524
536
525 - Replacing code objects does not always succeed: changing a @property
537 - Replacing code objects does not always succeed: changing a @property
526 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
527 can cause problems (but in old objects only).
539 can cause problems (but in old objects only).
528
540
529 - Functions that are removed (eg. via monkey-patching) from a module
541 - Functions that are removed (eg. via monkey-patching) from a module
530 before it is reloaded are not upgraded.
542 before it is reloaded are not upgraded.
531
543
532 - C extension modules cannot be reloaded, and so cannot be
544 - C extension modules cannot be reloaded, and so cannot be
533 autoreloaded.
545 autoreloaded.
534
546
535 """
547 """
536 if parameter_s == "":
548 if parameter_s == "":
537 self._reloader.check(True)
549 self._reloader.check(True)
538 elif parameter_s == "0":
550 elif parameter_s == "0":
539 self._reloader.enabled = False
551 self._reloader.enabled = False
540 elif parameter_s == "1":
552 elif parameter_s == "1":
541 self._reloader.check_all = False
553 self._reloader.check_all = False
542 self._reloader.enabled = True
554 self._reloader.enabled = True
543 elif parameter_s == "2":
555 elif parameter_s == "2":
544 self._reloader.check_all = True
556 self._reloader.check_all = True
545 self._reloader.enabled = True
557 self._reloader.enabled = True
546 self._reloader.enabled = True
558 self._reloader.enabled = True
547 elif parameter_s == "3":
559 elif parameter_s == "3":
548 self._reloader.check_all = True
560 self._reloader.check_all = True
549 self._reloader.enabled = True
561 self._reloader.enabled = True
550 self._reloader.autoload_obj = True
562 self._reloader.autoload_obj = True
551
563
552 @line_magic
564 @line_magic
553 def aimport(self, parameter_s="", stream=None):
565 def aimport(self, parameter_s="", stream=None):
554 """%aimport => Import modules for automatic reloading.
566 """%aimport => Import modules for automatic reloading.
555
567
556 %aimport
568 %aimport
557 List modules to automatically import and not to import.
569 List modules to automatically import and not to import.
558
570
559 %aimport foo
571 %aimport foo
560 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
561
573
562 %aimport foo, bar
574 %aimport foo, bar
563 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
564
576
565 %aimport -foo
577 %aimport -foo
566 Mark module 'foo' to not be autoreloaded for %autoreload 1
578 Mark module 'foo' to not be autoreloaded for %autoreload 1
567 """
579 """
568 modname = parameter_s
580 modname = parameter_s
569 if not modname:
581 if not modname:
570 to_reload = sorted(self._reloader.modules.keys())
582 to_reload = sorted(self._reloader.modules.keys())
571 to_skip = sorted(self._reloader.skip_modules.keys())
583 to_skip = sorted(self._reloader.skip_modules.keys())
572 if stream is None:
584 if stream is None:
573 stream = sys.stdout
585 stream = sys.stdout
574 if self._reloader.check_all:
586 if self._reloader.check_all:
575 stream.write("Modules to reload:\nall-except-skipped\n")
587 stream.write("Modules to reload:\nall-except-skipped\n")
576 else:
588 else:
577 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
589 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
578 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
590 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
579 elif modname.startswith("-"):
591 elif modname.startswith("-"):
580 modname = modname[1:]
592 modname = modname[1:]
581 self._reloader.mark_module_skipped(modname)
593 self._reloader.mark_module_skipped(modname)
582 else:
594 else:
583 for _module in [_.strip() for _ in modname.split(",")]:
595 for _module in [_.strip() for _ in modname.split(",")]:
584 top_module, top_name = self._reloader.aimport_module(_module)
596 top_module, top_name = self._reloader.aimport_module(_module)
585
597
586 # Inject module to user namespace
598 # Inject module to user namespace
587 self.shell.push({top_name: top_module})
599 self.shell.push({top_name: top_module})
588
600
589 def pre_run_cell(self):
601 def pre_run_cell(self):
590 if self._reloader.enabled:
602 if self._reloader.enabled:
591 try:
603 try:
592 self._reloader.check()
604 self._reloader.check()
593 except:
605 except:
594 pass
606 pass
595
607
596 def post_execute_hook(self):
608 def post_execute_hook(self):
597 """Cache the modification times of any modules imported in this execution"""
609 """Cache the modification times of any modules imported in this execution"""
598 newly_loaded_modules = set(sys.modules) - self.loaded_modules
610 newly_loaded_modules = set(sys.modules) - self.loaded_modules
599 for modname in newly_loaded_modules:
611 for modname in newly_loaded_modules:
600 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
612 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
601 if pymtime is not None:
613 if pymtime is not None:
602 self._reloader.modules_mtimes[modname] = pymtime
614 self._reloader.modules_mtimes[modname] = pymtime
603
615
604 self.loaded_modules.update(newly_loaded_modules)
616 self.loaded_modules.update(newly_loaded_modules)
605
617
606
618
607 def load_ipython_extension(ip):
619 def load_ipython_extension(ip):
608 """Load the extension in IPython."""
620 """Load the extension in IPython."""
609 auto_reload = AutoreloadMagics(ip)
621 auto_reload = AutoreloadMagics(ip)
610 ip.register_magics(auto_reload)
622 ip.register_magics(auto_reload)
611 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
623 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
612 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