##// END OF EJS Templates
Apply pyupgrade to IPython/extensions/autoreload.py...
Spas Kalaydzhisyki -
Show More
@@ -1,595 +1,595 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 (except those excluded by ``%aimport``)
54 (except those excluded by ``%aimport``)
55 every time before executing the Python code typed.
55 every time before executing the Python code typed.
56
56
57 ``%aimport``
57 ``%aimport``
58
58
59 List modules which are to be automatically imported or not to be imported.
59 List modules which are to be automatically imported or not to be imported.
60
60
61 ``%aimport foo``
61 ``%aimport foo``
62
62
63 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
63 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
64
64
65 ``%aimport foo, bar``
65 ``%aimport foo, bar``
66
66
67 Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1``
67 Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1``
68
68
69 ``%aimport -foo``
69 ``%aimport -foo``
70
70
71 Mark module 'foo' to not be autoreloaded.
71 Mark module 'foo' to not be autoreloaded.
72
72
73 Caveats
73 Caveats
74 =======
74 =======
75
75
76 Reloading Python modules in a reliable way is in general difficult,
76 Reloading Python modules in a reliable way is in general difficult,
77 and unexpected things may occur. ``%autoreload`` tries to work around
77 and unexpected things may occur. ``%autoreload`` tries to work around
78 common pitfalls by replacing function code objects and parts of
78 common pitfalls by replacing function code objects and parts of
79 classes previously in the module with new versions. This makes the
79 classes previously in the module with new versions. This makes the
80 following things to work:
80 following things to work:
81
81
82 - Functions and classes imported via 'from xxx import foo' are upgraded
82 - Functions and classes imported via 'from xxx import foo' are upgraded
83 to new versions when 'xxx' is reloaded.
83 to new versions when 'xxx' is reloaded.
84
84
85 - Methods and properties of classes are upgraded on reload, so that
85 - Methods and properties of classes are upgraded on reload, so that
86 calling 'c.foo()' on an object 'c' created before the reload causes
86 calling 'c.foo()' on an object 'c' created before the reload causes
87 the new code for 'foo' to be executed.
87 the new code for 'foo' to be executed.
88
88
89 Some of the known remaining caveats are:
89 Some of the known remaining caveats are:
90
90
91 - Replacing code objects does not always succeed: changing a @property
91 - Replacing code objects does not always succeed: changing a @property
92 in a class to an ordinary method or a method to a member variable
92 in a class to an ordinary method or a method to a member variable
93 can cause problems (but in old objects only).
93 can cause problems (but in old objects only).
94
94
95 - Functions that are removed (eg. via monkey-patching) from a module
95 - Functions that are removed (eg. via monkey-patching) from a module
96 before it is reloaded are not upgraded.
96 before it is reloaded are not upgraded.
97
97
98 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
98 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
99 """
99 """
100
100
101 skip_doctest = True
101 skip_doctest = True
102
102
103 #-----------------------------------------------------------------------------
103 #-----------------------------------------------------------------------------
104 # Copyright (C) 2000 Thomas Heller
104 # Copyright (C) 2000 Thomas Heller
105 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
105 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
106 # Copyright (C) 2012 The IPython Development Team
106 # Copyright (C) 2012 The IPython Development Team
107 #
107 #
108 # Distributed under the terms of the BSD License. The full license is in
108 # Distributed under the terms of the BSD License. The full license is in
109 # the file COPYING, distributed as part of this software.
109 # the file COPYING, distributed as part of this software.
110 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
111 #
111 #
112 # This IPython module is written by Pauli Virtanen, based on the autoreload
112 # This IPython module is written by Pauli Virtanen, based on the autoreload
113 # code by Thomas Heller.
113 # code by Thomas Heller.
114
114
115 #-----------------------------------------------------------------------------
115 #-----------------------------------------------------------------------------
116 # Imports
116 # Imports
117 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
118
118
119 import os
119 import os
120 import sys
120 import sys
121 import traceback
121 import traceback
122 import types
122 import types
123 import weakref
123 import weakref
124 import gc
124 import gc
125 from importlib import import_module
125 from importlib import import_module
126 from importlib.util import source_from_cache
126 from importlib.util import source_from_cache
127 from imp import reload
127 from imp import reload
128
128
129 #------------------------------------------------------------------------------
129 #------------------------------------------------------------------------------
130 # Autoreload functionality
130 # Autoreload functionality
131 #------------------------------------------------------------------------------
131 #------------------------------------------------------------------------------
132
132
133 class ModuleReloader(object):
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("[autoreload of %s failed: %s]" % (
262 print("[autoreload of {} failed: {}]".format(
263 modname, traceback.format_exc(10)), file=sys.stderr)
263 modname, traceback.format_exc(10)), file=sys.stderr)
264 self.failed[py_filename] = pymtime
264 self.failed[py_filename] = pymtime
265
265
266 #------------------------------------------------------------------------------
266 #------------------------------------------------------------------------------
267 # superreload
267 # superreload
268 #------------------------------------------------------------------------------
268 #------------------------------------------------------------------------------
269
269
270
270
271 func_attrs = ['__code__', '__defaults__', '__doc__',
271 func_attrs = ['__code__', '__defaults__', '__doc__',
272 '__closure__', '__globals__', '__dict__']
272 '__closure__', '__globals__', '__dict__']
273
273
274
274
275 def update_function(old, new):
275 def update_function(old, new):
276 """Upgrade the code object of a function"""
276 """Upgrade the code object of a function"""
277 for name in func_attrs:
277 for name in func_attrs:
278 try:
278 try:
279 setattr(old, name, getattr(new, name))
279 setattr(old, name, getattr(new, name))
280 except (AttributeError, TypeError):
280 except (AttributeError, TypeError):
281 pass
281 pass
282
282
283
283
284 def update_instances(old, new):
284 def update_instances(old, new):
285 """Use garbage collector to find all instances that refer to the old
285 """Use garbage collector to find all instances that refer to the old
286 class definition and update their __class__ to point to the new class
286 class definition and update their __class__ to point to the new class
287 definition"""
287 definition"""
288
288
289 refs = gc.get_referrers(old)
289 refs = gc.get_referrers(old)
290
290
291 for ref in refs:
291 for ref in refs:
292 if type(ref) is old:
292 if type(ref) is old:
293 ref.__class__ = new
293 ref.__class__ = new
294
294
295
295
296 def update_class(old, new):
296 def update_class(old, new):
297 """Replace stuff in the __dict__ of a class, and upgrade
297 """Replace stuff in the __dict__ of a class, and upgrade
298 method code objects, and add new methods, if any"""
298 method code objects, and add new methods, if any"""
299 for key in list(old.__dict__.keys()):
299 for key in list(old.__dict__.keys()):
300 old_obj = getattr(old, key)
300 old_obj = getattr(old, key)
301 try:
301 try:
302 new_obj = getattr(new, key)
302 new_obj = getattr(new, key)
303 # explicitly checking that comparison returns True to handle
303 # explicitly checking that comparison returns True to handle
304 # cases where `==` doesn't return a boolean.
304 # cases where `==` doesn't return a boolean.
305 if (old_obj == new_obj) is True:
305 if (old_obj == new_obj) is True:
306 continue
306 continue
307 except AttributeError:
307 except AttributeError:
308 # obsolete attribute: remove it
308 # obsolete attribute: remove it
309 try:
309 try:
310 delattr(old, key)
310 delattr(old, key)
311 except (AttributeError, TypeError):
311 except (AttributeError, TypeError):
312 pass
312 pass
313 continue
313 continue
314
314
315 if update_generic(old_obj, new_obj): continue
315 if update_generic(old_obj, new_obj): continue
316
316
317 try:
317 try:
318 setattr(old, key, getattr(new, key))
318 setattr(old, key, getattr(new, key))
319 except (AttributeError, TypeError):
319 except (AttributeError, TypeError):
320 pass # skip non-writable attributes
320 pass # skip non-writable attributes
321
321
322 for key in list(new.__dict__.keys()):
322 for key in list(new.__dict__.keys()):
323 if key not in list(old.__dict__.keys()):
323 if key not in list(old.__dict__.keys()):
324 try:
324 try:
325 setattr(old, key, getattr(new, key))
325 setattr(old, key, getattr(new, key))
326 except (AttributeError, TypeError):
326 except (AttributeError, TypeError):
327 pass # skip non-writable attributes
327 pass # skip non-writable attributes
328
328
329 # update all instances of class
329 # update all instances of class
330 update_instances(old, new)
330 update_instances(old, new)
331
331
332
332
333 def update_property(old, new):
333 def update_property(old, new):
334 """Replace get/set/del functions of a property"""
334 """Replace get/set/del functions of a property"""
335 update_generic(old.fdel, new.fdel)
335 update_generic(old.fdel, new.fdel)
336 update_generic(old.fget, new.fget)
336 update_generic(old.fget, new.fget)
337 update_generic(old.fset, new.fset)
337 update_generic(old.fset, new.fset)
338
338
339
339
340 def isinstance2(a, b, typ):
340 def isinstance2(a, b, typ):
341 return isinstance(a, typ) and isinstance(b, typ)
341 return isinstance(a, typ) and isinstance(b, typ)
342
342
343
343
344 UPDATE_RULES = [
344 UPDATE_RULES = [
345 (lambda a, b: isinstance2(a, b, type),
345 (lambda a, b: isinstance2(a, b, type),
346 update_class),
346 update_class),
347 (lambda a, b: isinstance2(a, b, types.FunctionType),
347 (lambda a, b: isinstance2(a, b, types.FunctionType),
348 update_function),
348 update_function),
349 (lambda a, b: isinstance2(a, b, property),
349 (lambda a, b: isinstance2(a, b, property),
350 update_property),
350 update_property),
351 ]
351 ]
352 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
352 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
353 lambda a, b: update_function(a.__func__, b.__func__)),
353 lambda a, b: update_function(a.__func__, b.__func__)),
354 ])
354 ])
355
355
356
356
357 def update_generic(a, b):
357 def update_generic(a, b):
358 for type_check, update in UPDATE_RULES:
358 for type_check, update in UPDATE_RULES:
359 if type_check(a, b):
359 if type_check(a, b):
360 update(a, b)
360 update(a, b)
361 return True
361 return True
362 return False
362 return False
363
363
364
364
365 class StrongRef(object):
365 class StrongRef:
366 def __init__(self, obj):
366 def __init__(self, obj):
367 self.obj = obj
367 self.obj = obj
368 def __call__(self):
368 def __call__(self):
369 return self.obj
369 return self.obj
370
370
371
371
372 def append_obj(module, d, name, obj, autoload=False):
372 def append_obj(module, d, name, obj, autoload=False):
373 not_in_mod = not hasattr(obj, '__module__') or obj.__module__ != module.__name__
373 not_in_mod = not hasattr(obj, '__module__') or obj.__module__ != module.__name__
374 if autoload:
374 if autoload:
375 # check needed for module global built-ins (int, str, dict,..)
375 # check needed for module global built-ins (int, str, dict,..)
376 if name.startswith('__') and not_in_mod:
376 if name.startswith('__') and not_in_mod:
377 return False
377 return False
378 else:
378 else:
379 if not_in_mod:
379 if not_in_mod:
380 return False
380 return False
381
381
382 key = (module.__name__, name)
382 key = (module.__name__, name)
383 try:
383 try:
384 d.setdefault(key, []).append(weakref.ref(obj))
384 d.setdefault(key, []).append(weakref.ref(obj))
385 except TypeError:
385 except TypeError:
386 pass
386 pass
387 return True
387 return True
388
388
389
389
390 def superreload(module, reload=reload, old_objects=None, shell=None):
390 def superreload(module, reload=reload, old_objects=None, shell=None):
391 """Enhanced version of the builtin reload function.
391 """Enhanced version of the builtin reload function.
392
392
393 superreload remembers objects previously in the module, and
393 superreload remembers objects previously in the module, and
394
394
395 - upgrades the class dictionary of every old class in the module
395 - upgrades the class dictionary of every old class in the module
396 - upgrades the code object of every old function and method
396 - upgrades the code object of every old function and method
397 - clears the module's namespace before reloading
397 - clears the module's namespace before reloading
398
398
399 """
399 """
400 if old_objects is None:
400 if old_objects is None:
401 old_objects = {}
401 old_objects = {}
402
402
403 # collect old objects in the module
403 # collect old objects in the module
404 for name, obj in list(module.__dict__.items()):
404 for name, obj in list(module.__dict__.items()):
405 if not append_obj(module, old_objects, name, obj):
405 if not append_obj(module, old_objects, name, obj):
406 continue
406 continue
407 key = (module.__name__, name)
407 key = (module.__name__, name)
408 try:
408 try:
409 old_objects.setdefault(key, []).append(weakref.ref(obj))
409 old_objects.setdefault(key, []).append(weakref.ref(obj))
410 except TypeError:
410 except TypeError:
411 pass
411 pass
412
412
413 # reload module
413 # reload module
414 try:
414 try:
415 # clear namespace first from old cruft
415 # clear namespace first from old cruft
416 old_dict = module.__dict__.copy()
416 old_dict = module.__dict__.copy()
417 old_name = module.__name__
417 old_name = module.__name__
418 module.__dict__.clear()
418 module.__dict__.clear()
419 module.__dict__['__name__'] = old_name
419 module.__dict__['__name__'] = old_name
420 module.__dict__['__loader__'] = old_dict['__loader__']
420 module.__dict__['__loader__'] = old_dict['__loader__']
421 except (TypeError, AttributeError, KeyError):
421 except (TypeError, AttributeError, KeyError):
422 pass
422 pass
423
423
424 try:
424 try:
425 module = reload(module)
425 module = reload(module)
426 except:
426 except:
427 # restore module dictionary on failed reload
427 # restore module dictionary on failed reload
428 module.__dict__.update(old_dict)
428 module.__dict__.update(old_dict)
429 raise
429 raise
430
430
431 # iterate over all objects and update functions & classes
431 # iterate over all objects and update functions & classes
432 for name, new_obj in list(module.__dict__.items()):
432 for name, new_obj in list(module.__dict__.items()):
433 key = (module.__name__, name)
433 key = (module.__name__, name)
434 if key not in old_objects:
434 if key not in old_objects:
435 # here 'shell' acts both as a flag and as an output var
435 # here 'shell' acts both as a flag and as an output var
436 if (
436 if (
437 shell is None or
437 shell is None or
438 name == 'Enum' or
438 name == 'Enum' or
439 not append_obj(module, old_objects, name, new_obj, True)
439 not append_obj(module, old_objects, name, new_obj, True)
440 ):
440 ):
441 continue
441 continue
442 shell.user_ns[name] = new_obj
442 shell.user_ns[name] = new_obj
443
443
444 new_refs = []
444 new_refs = []
445 for old_ref in old_objects[key]:
445 for old_ref in old_objects[key]:
446 old_obj = old_ref()
446 old_obj = old_ref()
447 if old_obj is None: continue
447 if old_obj is None: continue
448 new_refs.append(old_ref)
448 new_refs.append(old_ref)
449 update_generic(old_obj, new_obj)
449 update_generic(old_obj, new_obj)
450
450
451 if new_refs:
451 if new_refs:
452 old_objects[key] = new_refs
452 old_objects[key] = new_refs
453 else:
453 else:
454 del old_objects[key]
454 del old_objects[key]
455
455
456 return module
456 return module
457
457
458 #------------------------------------------------------------------------------
458 #------------------------------------------------------------------------------
459 # IPython connectivity
459 # IPython connectivity
460 #------------------------------------------------------------------------------
460 #------------------------------------------------------------------------------
461
461
462 from IPython.core.magic import Magics, magics_class, line_magic
462 from IPython.core.magic import Magics, magics_class, line_magic
463
463
464 @magics_class
464 @magics_class
465 class AutoreloadMagics(Magics):
465 class AutoreloadMagics(Magics):
466 def __init__(self, *a, **kw):
466 def __init__(self, *a, **kw):
467 super(AutoreloadMagics, self).__init__(*a, **kw)
467 super().__init__(*a, **kw)
468 self._reloader = ModuleReloader(self.shell)
468 self._reloader = ModuleReloader(self.shell)
469 self._reloader.check_all = False
469 self._reloader.check_all = False
470 self._reloader.autoload_obj = False
470 self._reloader.autoload_obj = False
471 self.loaded_modules = set(sys.modules)
471 self.loaded_modules = set(sys.modules)
472
472
473 @line_magic
473 @line_magic
474 def autoreload(self, parameter_s=''):
474 def autoreload(self, parameter_s=''):
475 r"""%autoreload => Reload modules automatically
475 r"""%autoreload => Reload modules automatically
476
476
477 %autoreload
477 %autoreload
478 Reload all modules (except those excluded by %aimport) automatically
478 Reload all modules (except those excluded by %aimport) automatically
479 now.
479 now.
480
480
481 %autoreload 0
481 %autoreload 0
482 Disable automatic reloading.
482 Disable automatic reloading.
483
483
484 %autoreload 1
484 %autoreload 1
485 Reload all modules imported with %aimport every time before executing
485 Reload all modules imported with %aimport every time before executing
486 the Python code typed.
486 the Python code typed.
487
487
488 %autoreload 2
488 %autoreload 2
489 Reload all modules (except those excluded by %aimport) every time
489 Reload all modules (except those excluded by %aimport) every time
490 before executing the Python code typed.
490 before executing the Python code typed.
491
491
492 Reloading Python modules in a reliable way is in general
492 Reloading Python modules in a reliable way is in general
493 difficult, and unexpected things may occur. %autoreload tries to
493 difficult, and unexpected things may occur. %autoreload tries to
494 work around common pitfalls by replacing function code objects and
494 work around common pitfalls by replacing function code objects and
495 parts of classes previously in the module with new versions. This
495 parts of classes previously in the module with new versions. This
496 makes the following things to work:
496 makes the following things to work:
497
497
498 - Functions and classes imported via 'from xxx import foo' are upgraded
498 - Functions and classes imported via 'from xxx import foo' are upgraded
499 to new versions when 'xxx' is reloaded.
499 to new versions when 'xxx' is reloaded.
500
500
501 - Methods and properties of classes are upgraded on reload, so that
501 - Methods and properties of classes are upgraded on reload, so that
502 calling 'c.foo()' on an object 'c' created before the reload causes
502 calling 'c.foo()' on an object 'c' created before the reload causes
503 the new code for 'foo' to be executed.
503 the new code for 'foo' to be executed.
504
504
505 Some of the known remaining caveats are:
505 Some of the known remaining caveats are:
506
506
507 - Replacing code objects does not always succeed: changing a @property
507 - Replacing code objects does not always succeed: changing a @property
508 in a class to an ordinary method or a method to a member variable
508 in a class to an ordinary method or a method to a member variable
509 can cause problems (but in old objects only).
509 can cause problems (but in old objects only).
510
510
511 - Functions that are removed (eg. via monkey-patching) from a module
511 - Functions that are removed (eg. via monkey-patching) from a module
512 before it is reloaded are not upgraded.
512 before it is reloaded are not upgraded.
513
513
514 - C extension modules cannot be reloaded, and so cannot be
514 - C extension modules cannot be reloaded, and so cannot be
515 autoreloaded.
515 autoreloaded.
516
516
517 """
517 """
518 if parameter_s == '':
518 if parameter_s == '':
519 self._reloader.check(True)
519 self._reloader.check(True)
520 elif parameter_s == '0':
520 elif parameter_s == '0':
521 self._reloader.enabled = False
521 self._reloader.enabled = False
522 elif parameter_s == '1':
522 elif parameter_s == '1':
523 self._reloader.check_all = False
523 self._reloader.check_all = False
524 self._reloader.enabled = True
524 self._reloader.enabled = True
525 elif parameter_s == '2':
525 elif parameter_s == '2':
526 self._reloader.check_all = True
526 self._reloader.check_all = True
527 self._reloader.enabled = True
527 self._reloader.enabled = True
528 self._reloader.enabled = True
528 self._reloader.enabled = True
529 elif parameter_s == '3':
529 elif parameter_s == '3':
530 self._reloader.check_all = True
530 self._reloader.check_all = True
531 self._reloader.enabled = True
531 self._reloader.enabled = True
532 self._reloader.autoload_obj = True
532 self._reloader.autoload_obj = True
533
533
534 @line_magic
534 @line_magic
535 def aimport(self, parameter_s='', stream=None):
535 def aimport(self, parameter_s='', stream=None):
536 """%aimport => Import modules for automatic reloading.
536 """%aimport => Import modules for automatic reloading.
537
537
538 %aimport
538 %aimport
539 List modules to automatically import and not to import.
539 List modules to automatically import and not to import.
540
540
541 %aimport foo
541 %aimport foo
542 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
542 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
543
543
544 %aimport foo, bar
544 %aimport foo, bar
545 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
545 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
546
546
547 %aimport -foo
547 %aimport -foo
548 Mark module 'foo' to not be autoreloaded for %autoreload 1
548 Mark module 'foo' to not be autoreloaded for %autoreload 1
549 """
549 """
550 modname = parameter_s
550 modname = parameter_s
551 if not modname:
551 if not modname:
552 to_reload = sorted(self._reloader.modules.keys())
552 to_reload = sorted(self._reloader.modules.keys())
553 to_skip = sorted(self._reloader.skip_modules.keys())
553 to_skip = sorted(self._reloader.skip_modules.keys())
554 if stream is None:
554 if stream is None:
555 stream = sys.stdout
555 stream = sys.stdout
556 if self._reloader.check_all:
556 if self._reloader.check_all:
557 stream.write("Modules to reload:\nall-except-skipped\n")
557 stream.write("Modules to reload:\nall-except-skipped\n")
558 else:
558 else:
559 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
559 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
560 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
560 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
561 elif modname.startswith('-'):
561 elif modname.startswith('-'):
562 modname = modname[1:]
562 modname = modname[1:]
563 self._reloader.mark_module_skipped(modname)
563 self._reloader.mark_module_skipped(modname)
564 else:
564 else:
565 for _module in ([_.strip() for _ in modname.split(',')]):
565 for _module in ([_.strip() for _ in modname.split(',')]):
566 top_module, top_name = self._reloader.aimport_module(_module)
566 top_module, top_name = self._reloader.aimport_module(_module)
567
567
568 # Inject module to user namespace
568 # Inject module to user namespace
569 self.shell.push({top_name: top_module})
569 self.shell.push({top_name: top_module})
570
570
571 def pre_run_cell(self):
571 def pre_run_cell(self):
572 if self._reloader.enabled:
572 if self._reloader.enabled:
573 try:
573 try:
574 self._reloader.check()
574 self._reloader.check()
575 except:
575 except:
576 pass
576 pass
577
577
578 def post_execute_hook(self):
578 def post_execute_hook(self):
579 """Cache the modification times of any modules imported in this execution
579 """Cache the modification times of any modules imported in this execution
580 """
580 """
581 newly_loaded_modules = set(sys.modules) - self.loaded_modules
581 newly_loaded_modules = set(sys.modules) - self.loaded_modules
582 for modname in newly_loaded_modules:
582 for modname in newly_loaded_modules:
583 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
583 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
584 if pymtime is not None:
584 if pymtime is not None:
585 self._reloader.modules_mtimes[modname] = pymtime
585 self._reloader.modules_mtimes[modname] = pymtime
586
586
587 self.loaded_modules.update(newly_loaded_modules)
587 self.loaded_modules.update(newly_loaded_modules)
588
588
589
589
590 def load_ipython_extension(ip):
590 def load_ipython_extension(ip):
591 """Load the extension in IPython."""
591 """Load the extension in IPython."""
592 auto_reload = AutoreloadMagics(ip)
592 auto_reload = AutoreloadMagics(ip)
593 ip.register_magics(auto_reload)
593 ip.register_magics(auto_reload)
594 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
594 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
595 ip.events.register('post_execute', auto_reload.post_execute_hook)
595 ip.events.register('post_execute', auto_reload.post_execute_hook)
General Comments 0
You need to be logged in to leave comments. Login now