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