##// END OF EJS Templates
Remove now-superfluous code in autoreload extension.
Thomas Kluyver -
Show More
@@ -1,507 +1,504
1 """
1 """
2 ``autoreload`` is an IPython extension that reloads modules
2 ``autoreload`` is an IPython extension that reloads modules
3 automatically before executing the line of code typed.
3 automatically before executing the line of code typed.
4
4
5 This makes for example the following workflow possible:
5 This makes for example the following workflow possible:
6
6
7 .. sourcecode:: ipython
7 .. sourcecode:: ipython
8
8
9 In [1]: %load_ext autoreload
9 In [1]: %load_ext autoreload
10
10
11 In [2]: %autoreload 2
11 In [2]: %autoreload 2
12
12
13 In [3]: from foo import some_function
13 In [3]: from foo import some_function
14
14
15 In [4]: some_function()
15 In [4]: some_function()
16 Out[4]: 42
16 Out[4]: 42
17
17
18 In [5]: # open foo.py in an editor and change some_function to return 43
18 In [5]: # open foo.py in an editor and change some_function to return 43
19
19
20 In [6]: some_function()
20 In [6]: some_function()
21 Out[6]: 43
21 Out[6]: 43
22
22
23 The module was reloaded without reloading it explicitly, and the
23 The module was reloaded without reloading it explicitly, and the
24 object imported with ``from foo import ...`` was also updated.
24 object imported with ``from foo import ...`` was also updated.
25
25
26 Usage
26 Usage
27 =====
27 =====
28
28
29 The following magic commands are provided:
29 The following magic commands are provided:
30
30
31 ``%autoreload``
31 ``%autoreload``
32
32
33 Reload all modules (except those excluded by ``%aimport``)
33 Reload all modules (except those excluded by ``%aimport``)
34 automatically now.
34 automatically now.
35
35
36 ``%autoreload 0``
36 ``%autoreload 0``
37
37
38 Disable automatic reloading.
38 Disable automatic reloading.
39
39
40 ``%autoreload 1``
40 ``%autoreload 1``
41
41
42 Reload all modules imported with ``%aimport`` every time before
42 Reload all modules imported with ``%aimport`` every time before
43 executing the Python code typed.
43 executing the Python code typed.
44
44
45 ``%autoreload 2``
45 ``%autoreload 2``
46
46
47 Reload all modules (except those excluded by ``%aimport``) every
47 Reload all modules (except those excluded by ``%aimport``) every
48 time before executing the Python code typed.
48 time before executing the Python code typed.
49
49
50 ``%aimport``
50 ``%aimport``
51
51
52 List modules which are to be automatically imported or not to be imported.
52 List modules which are to be automatically imported or not to be imported.
53
53
54 ``%aimport foo``
54 ``%aimport foo``
55
55
56 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
56 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
57
57
58 ``%aimport -foo``
58 ``%aimport -foo``
59
59
60 Mark module 'foo' to not be autoreloaded.
60 Mark module 'foo' to not be autoreloaded.
61
61
62 Caveats
62 Caveats
63 =======
63 =======
64
64
65 Reloading Python modules in a reliable way is in general difficult,
65 Reloading Python modules in a reliable way is in general difficult,
66 and unexpected things may occur. ``%autoreload`` tries to work around
66 and unexpected things may occur. ``%autoreload`` tries to work around
67 common pitfalls by replacing function code objects and parts of
67 common pitfalls by replacing function code objects and parts of
68 classes previously in the module with new versions. This makes the
68 classes previously in the module with new versions. This makes the
69 following things to work:
69 following things to work:
70
70
71 - Functions and classes imported via 'from xxx import foo' are upgraded
71 - Functions and classes imported via 'from xxx import foo' are upgraded
72 to new versions when 'xxx' is reloaded.
72 to new versions when 'xxx' is reloaded.
73
73
74 - Methods and properties of classes are upgraded on reload, so that
74 - Methods and properties of classes are upgraded on reload, so that
75 calling 'c.foo()' on an object 'c' created before the reload causes
75 calling 'c.foo()' on an object 'c' created before the reload causes
76 the new code for 'foo' to be executed.
76 the new code for 'foo' to be executed.
77
77
78 Some of the known remaining caveats are:
78 Some of the known remaining caveats are:
79
79
80 - Replacing code objects does not always succeed: changing a @property
80 - Replacing code objects does not always succeed: changing a @property
81 in a class to an ordinary method or a method to a member variable
81 in a class to an ordinary method or a method to a member variable
82 can cause problems (but in old objects only).
82 can cause problems (but in old objects only).
83
83
84 - Functions that are removed (eg. via monkey-patching) from a module
84 - Functions that are removed (eg. via monkey-patching) from a module
85 before it is reloaded are not upgraded.
85 before it is reloaded are not upgraded.
86
86
87 - C extension modules cannot be reloaded, and so cannot be
87 - C extension modules cannot be reloaded, and so cannot be
88 autoreloaded.
88 autoreloaded.
89
89
90 """
90 """
91
91
92 skip_doctest = True
92 skip_doctest = True
93
93
94 # Pauli Virtanen <pav@iki.fi>, 2008.
94 # Pauli Virtanen <pav@iki.fi>, 2008.
95 # Thomas Heller, 2000.
95 # Thomas Heller, 2000.
96 #
96 #
97 # This IPython module is written by Pauli Virtanen, based on the autoreload
97 # This IPython module is written by Pauli Virtanen, based on the autoreload
98 # code by Thomas Heller.
98 # code by Thomas Heller.
99
99
100 #------------------------------------------------------------------------------
100 #------------------------------------------------------------------------------
101 # Autoreload functionality
101 # Autoreload functionality
102 #------------------------------------------------------------------------------
102 #------------------------------------------------------------------------------
103
103
104 import time, os, threading, sys, types, imp, inspect, traceback, atexit
104 import time, os, threading, sys, types, imp, inspect, traceback, atexit
105 import weakref
105 import weakref
106 try:
106 try:
107 reload
107 reload
108 except NameError:
108 except NameError:
109 from imp import reload
109 from imp import reload
110
110
111 from IPython.utils import pyfile
111 from IPython.utils import pyfile
112 from IPython.utils.py3compat import PY3
112 from IPython.utils.py3compat import PY3
113
113
114 def _get_compiled_ext():
114 def _get_compiled_ext():
115 """Official way to get the extension of compiled files (.pyc or .pyo)"""
115 """Official way to get the extension of compiled files (.pyc or .pyo)"""
116 for ext, mode, typ in imp.get_suffixes():
116 for ext, mode, typ in imp.get_suffixes():
117 if typ == imp.PY_COMPILED:
117 if typ == imp.PY_COMPILED:
118 return ext
118 return ext
119
119
120 PY_COMPILED_EXT = _get_compiled_ext()
120 PY_COMPILED_EXT = _get_compiled_ext()
121
121
122 class ModuleReloader(object):
122 class ModuleReloader(object):
123 enabled = False
123 enabled = False
124 """Whether this reloader is enabled"""
124 """Whether this reloader is enabled"""
125
125
126 failed = {}
126 failed = {}
127 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
127 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
128
128
129 modules = {}
129 modules = {}
130 """Modules specially marked as autoreloadable."""
130 """Modules specially marked as autoreloadable."""
131
131
132 skip_modules = {}
132 skip_modules = {}
133 """Modules specially marked as not autoreloadable."""
133 """Modules specially marked as not autoreloadable."""
134
134
135 check_all = True
135 check_all = True
136 """Autoreload all modules, not just those listed in 'modules'"""
136 """Autoreload all modules, not just those listed in 'modules'"""
137
137
138 old_objects = {}
138 old_objects = {}
139 """(module-name, name) -> weakref, for replacing old code objects"""
139 """(module-name, name) -> weakref, for replacing old code objects"""
140
140
141 def mark_module_skipped(self, module_name):
141 def mark_module_skipped(self, module_name):
142 """Skip reloading the named module in the future"""
142 """Skip reloading the named module in the future"""
143 try:
143 try:
144 del self.modules[module_name]
144 del self.modules[module_name]
145 except KeyError:
145 except KeyError:
146 pass
146 pass
147 self.skip_modules[module_name] = True
147 self.skip_modules[module_name] = True
148
148
149 def mark_module_reloadable(self, module_name):
149 def mark_module_reloadable(self, module_name):
150 """Reload the named module in the future (if it is imported)"""
150 """Reload the named module in the future (if it is imported)"""
151 try:
151 try:
152 del self.skip_modules[module_name]
152 del self.skip_modules[module_name]
153 except KeyError:
153 except KeyError:
154 pass
154 pass
155 self.modules[module_name] = True
155 self.modules[module_name] = True
156
156
157 def aimport_module(self, module_name):
157 def aimport_module(self, module_name):
158 """Import a module, and mark it reloadable
158 """Import a module, and mark it reloadable
159
159
160 Returns
160 Returns
161 -------
161 -------
162 top_module : module
162 top_module : module
163 The imported module if it is top-level, or the top-level
163 The imported module if it is top-level, or the top-level
164 top_name : module
164 top_name : module
165 Name of top_module
165 Name of top_module
166
166
167 """
167 """
168 self.mark_module_reloadable(module_name)
168 self.mark_module_reloadable(module_name)
169
169
170 __import__(module_name)
170 __import__(module_name)
171 top_name = module_name.split('.')[0]
171 top_name = module_name.split('.')[0]
172 top_module = sys.modules[top_name]
172 top_module = sys.modules[top_name]
173 return top_module, top_name
173 return top_module, top_name
174
174
175 def check(self, check_all=False):
175 def check(self, check_all=False):
176 """Check whether some modules need to be reloaded."""
176 """Check whether some modules need to be reloaded."""
177
177
178 if not self.enabled and not check_all:
178 if not self.enabled and not check_all:
179 return
179 return
180
180
181 if check_all or self.check_all:
181 if check_all or self.check_all:
182 modules = sys.modules.keys()
182 modules = sys.modules.keys()
183 else:
183 else:
184 modules = self.modules.keys()
184 modules = self.modules.keys()
185
185
186 for modname in modules:
186 for modname in modules:
187 m = sys.modules.get(modname, None)
187 m = sys.modules.get(modname, None)
188
188
189 if modname in self.skip_modules:
189 if modname in self.skip_modules:
190 continue
190 continue
191
191
192 if not hasattr(m, '__file__'):
192 if not hasattr(m, '__file__'):
193 continue
193 continue
194
194
195 if m.__name__ == '__main__':
195 if m.__name__ == '__main__':
196 # we cannot reload(__main__)
196 # we cannot reload(__main__)
197 continue
197 continue
198
198
199 filename = m.__file__
199 filename = m.__file__
200 path, ext = os.path.splitext(filename)
200 path, ext = os.path.splitext(filename)
201
201
202 if ext.lower() == '.py':
202 if ext.lower() == '.py':
203 ext = PY_COMPILED_EXT
203 ext = PY_COMPILED_EXT
204 pyc_filename = pyfile.cache_from_source(filename)
204 pyc_filename = pyfile.cache_from_source(filename)
205 py_filename = filename
205 py_filename = filename
206 else:
206 else:
207 pyc_filename = filename
207 pyc_filename = filename
208 try:
208 try:
209 py_filename = pyfile.source_from_cache(filename)
209 py_filename = pyfile.source_from_cache(filename)
210 except ValueError:
210 except ValueError:
211 continue
211 continue
212
212
213 if ext != PY_COMPILED_EXT:
214 continue
215
216 try:
213 try:
217 pymtime = os.stat(py_filename).st_mtime
214 pymtime = os.stat(py_filename).st_mtime
218 if pymtime <= os.stat(pyc_filename).st_mtime:
215 if pymtime <= os.stat(pyc_filename).st_mtime:
219 continue
216 continue
220 if self.failed.get(py_filename, None) == pymtime:
217 if self.failed.get(py_filename, None) == pymtime:
221 continue
218 continue
222 except OSError:
219 except OSError:
223 continue
220 continue
224
221
225 try:
222 try:
226 superreload(m, reload, self.old_objects)
223 superreload(m, reload, self.old_objects)
227 if py_filename in self.failed:
224 if py_filename in self.failed:
228 del self.failed[py_filename]
225 del self.failed[py_filename]
229 except:
226 except:
230 print >> sys.stderr, "[autoreload of %s failed: %s]" % (
227 print >> sys.stderr, "[autoreload of %s failed: %s]" % (
231 modname, traceback.format_exc(1))
228 modname, traceback.format_exc(1))
232 self.failed[py_filename] = pymtime
229 self.failed[py_filename] = pymtime
233
230
234 #------------------------------------------------------------------------------
231 #------------------------------------------------------------------------------
235 # superreload
232 # superreload
236 #------------------------------------------------------------------------------
233 #------------------------------------------------------------------------------
237
234
238 if PY3:
235 if PY3:
239 func_attrs = ['__code__', '__defaults__', '__doc__',
236 func_attrs = ['__code__', '__defaults__', '__doc__',
240 '__closure__', '__globals__', '__dict__']
237 '__closure__', '__globals__', '__dict__']
241 else:
238 else:
242 func_attrs = ['func_code', 'func_defaults', 'func_doc',
239 func_attrs = ['func_code', 'func_defaults', 'func_doc',
243 'func_closure', 'func_globals', 'func_dict']
240 'func_closure', 'func_globals', 'func_dict']
244
241
245 def update_function(old, new):
242 def update_function(old, new):
246 """Upgrade the code object of a function"""
243 """Upgrade the code object of a function"""
247 for name in func_attrs:
244 for name in func_attrs:
248 try:
245 try:
249 setattr(old, name, getattr(new, name))
246 setattr(old, name, getattr(new, name))
250 except (AttributeError, TypeError):
247 except (AttributeError, TypeError):
251 pass
248 pass
252
249
253 def update_class(old, new):
250 def update_class(old, new):
254 """Replace stuff in the __dict__ of a class, and upgrade
251 """Replace stuff in the __dict__ of a class, and upgrade
255 method code objects"""
252 method code objects"""
256 for key in old.__dict__.keys():
253 for key in old.__dict__.keys():
257 old_obj = getattr(old, key)
254 old_obj = getattr(old, key)
258
255
259 try:
256 try:
260 new_obj = getattr(new, key)
257 new_obj = getattr(new, key)
261 except AttributeError:
258 except AttributeError:
262 # obsolete attribute: remove it
259 # obsolete attribute: remove it
263 try:
260 try:
264 delattr(old, key)
261 delattr(old, key)
265 except (AttributeError, TypeError):
262 except (AttributeError, TypeError):
266 pass
263 pass
267 continue
264 continue
268
265
269 if update_generic(old_obj, new_obj): continue
266 if update_generic(old_obj, new_obj): continue
270
267
271 try:
268 try:
272 setattr(old, key, getattr(new, key))
269 setattr(old, key, getattr(new, key))
273 except (AttributeError, TypeError):
270 except (AttributeError, TypeError):
274 pass # skip non-writable attributes
271 pass # skip non-writable attributes
275
272
276 def update_property(old, new):
273 def update_property(old, new):
277 """Replace get/set/del functions of a property"""
274 """Replace get/set/del functions of a property"""
278 update_generic(old.fdel, new.fdel)
275 update_generic(old.fdel, new.fdel)
279 update_generic(old.fget, new.fget)
276 update_generic(old.fget, new.fget)
280 update_generic(old.fset, new.fset)
277 update_generic(old.fset, new.fset)
281
278
282 def isinstance2(a, b, typ):
279 def isinstance2(a, b, typ):
283 return isinstance(a, typ) and isinstance(b, typ)
280 return isinstance(a, typ) and isinstance(b, typ)
284
281
285 UPDATE_RULES = [
282 UPDATE_RULES = [
286 (lambda a, b: isinstance2(a, b, type),
283 (lambda a, b: isinstance2(a, b, type),
287 update_class),
284 update_class),
288 (lambda a, b: isinstance2(a, b, types.FunctionType),
285 (lambda a, b: isinstance2(a, b, types.FunctionType),
289 update_function),
286 update_function),
290 (lambda a, b: isinstance2(a, b, property),
287 (lambda a, b: isinstance2(a, b, property),
291 update_property),
288 update_property),
292 ]
289 ]
293
290
294 if PY3:
291 if PY3:
295 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
292 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
296 lambda a, b: update_function(a.__func__, b.__func__)),
293 lambda a, b: update_function(a.__func__, b.__func__)),
297 ])
294 ])
298 else:
295 else:
299 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
296 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
300 update_class),
297 update_class),
301 (lambda a, b: isinstance2(a, b, types.MethodType),
298 (lambda a, b: isinstance2(a, b, types.MethodType),
302 lambda a, b: update_function(a.im_func, b.im_func)),
299 lambda a, b: update_function(a.im_func, b.im_func)),
303 ])
300 ])
304
301
305
302
306 def update_generic(a, b):
303 def update_generic(a, b):
307 for type_check, update in UPDATE_RULES:
304 for type_check, update in UPDATE_RULES:
308 if type_check(a, b):
305 if type_check(a, b):
309 update(a, b)
306 update(a, b)
310 return True
307 return True
311 return False
308 return False
312
309
313 class StrongRef(object):
310 class StrongRef(object):
314 def __init__(self, obj):
311 def __init__(self, obj):
315 self.obj = obj
312 self.obj = obj
316 def __call__(self):
313 def __call__(self):
317 return self.obj
314 return self.obj
318
315
319 def superreload(module, reload=reload, old_objects={}):
316 def superreload(module, reload=reload, old_objects={}):
320 """Enhanced version of the builtin reload function.
317 """Enhanced version of the builtin reload function.
321
318
322 superreload remembers objects previously in the module, and
319 superreload remembers objects previously in the module, and
323
320
324 - upgrades the class dictionary of every old class in the module
321 - upgrades the class dictionary of every old class in the module
325 - upgrades the code object of every old function and method
322 - upgrades the code object of every old function and method
326 - clears the module's namespace before reloading
323 - clears the module's namespace before reloading
327
324
328 """
325 """
329
326
330 # collect old objects in the module
327 # collect old objects in the module
331 for name, obj in module.__dict__.items():
328 for name, obj in module.__dict__.items():
332 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
329 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
333 continue
330 continue
334 key = (module.__name__, name)
331 key = (module.__name__, name)
335 try:
332 try:
336 old_objects.setdefault(key, []).append(weakref.ref(obj))
333 old_objects.setdefault(key, []).append(weakref.ref(obj))
337 except TypeError:
334 except TypeError:
338 # weakref doesn't work for all types;
335 # weakref doesn't work for all types;
339 # create strong references for 'important' cases
336 # create strong references for 'important' cases
340 if not PY3 and isinstance(obj, types.ClassType):
337 if not PY3 and isinstance(obj, types.ClassType):
341 old_objects.setdefault(key, []).append(StrongRef(obj))
338 old_objects.setdefault(key, []).append(StrongRef(obj))
342
339
343 # reload module
340 # reload module
344 try:
341 try:
345 # clear namespace first from old cruft
342 # clear namespace first from old cruft
346 old_dict = module.__dict__.copy()
343 old_dict = module.__dict__.copy()
347 old_name = module.__name__
344 old_name = module.__name__
348 module.__dict__.clear()
345 module.__dict__.clear()
349 module.__dict__['__name__'] = old_name
346 module.__dict__['__name__'] = old_name
350 except (TypeError, AttributeError, KeyError):
347 except (TypeError, AttributeError, KeyError):
351 pass
348 pass
352
349
353 try:
350 try:
354 module = reload(module)
351 module = reload(module)
355 except:
352 except:
356 # restore module dictionary on failed reload
353 # restore module dictionary on failed reload
357 module.__dict__.update(old_dict)
354 module.__dict__.update(old_dict)
358 raise
355 raise
359
356
360 # iterate over all objects and update functions & classes
357 # iterate over all objects and update functions & classes
361 for name, new_obj in module.__dict__.items():
358 for name, new_obj in module.__dict__.items():
362 key = (module.__name__, name)
359 key = (module.__name__, name)
363 if key not in old_objects: continue
360 if key not in old_objects: continue
364
361
365 new_refs = []
362 new_refs = []
366 for old_ref in old_objects[key]:
363 for old_ref in old_objects[key]:
367 old_obj = old_ref()
364 old_obj = old_ref()
368 if old_obj is None: continue
365 if old_obj is None: continue
369 new_refs.append(old_ref)
366 new_refs.append(old_ref)
370 update_generic(old_obj, new_obj)
367 update_generic(old_obj, new_obj)
371
368
372 if new_refs:
369 if new_refs:
373 old_objects[key] = new_refs
370 old_objects[key] = new_refs
374 else:
371 else:
375 del old_objects[key]
372 del old_objects[key]
376
373
377 return module
374 return module
378
375
379 #------------------------------------------------------------------------------
376 #------------------------------------------------------------------------------
380 # IPython connectivity
377 # IPython connectivity
381 #------------------------------------------------------------------------------
378 #------------------------------------------------------------------------------
382
379
383 from IPython.core.plugin import Plugin
380 from IPython.core.plugin import Plugin
384 from IPython.core.hooks import TryNext
381 from IPython.core.hooks import TryNext
385
382
386 class AutoreloadInterface(object):
383 class AutoreloadInterface(object):
387 def __init__(self, *a, **kw):
384 def __init__(self, *a, **kw):
388 super(AutoreloadInterface, self).__init__(*a, **kw)
385 super(AutoreloadInterface, self).__init__(*a, **kw)
389 self._reloader = ModuleReloader()
386 self._reloader = ModuleReloader()
390 self._reloader.check_all = False
387 self._reloader.check_all = False
391
388
392 def magic_autoreload(self, ipself, parameter_s=''):
389 def magic_autoreload(self, ipself, parameter_s=''):
393 r"""%autoreload => Reload modules automatically
390 r"""%autoreload => Reload modules automatically
394
391
395 %autoreload
392 %autoreload
396 Reload all modules (except those excluded by %aimport) automatically
393 Reload all modules (except those excluded by %aimport) automatically
397 now.
394 now.
398
395
399 %autoreload 0
396 %autoreload 0
400 Disable automatic reloading.
397 Disable automatic reloading.
401
398
402 %autoreload 1
399 %autoreload 1
403 Reload all modules imported with %aimport every time before executing
400 Reload all modules imported with %aimport every time before executing
404 the Python code typed.
401 the Python code typed.
405
402
406 %autoreload 2
403 %autoreload 2
407 Reload all modules (except those excluded by %aimport) every time
404 Reload all modules (except those excluded by %aimport) every time
408 before executing the Python code typed.
405 before executing the Python code typed.
409
406
410 Reloading Python modules in a reliable way is in general
407 Reloading Python modules in a reliable way is in general
411 difficult, and unexpected things may occur. %autoreload tries to
408 difficult, and unexpected things may occur. %autoreload tries to
412 work around common pitfalls by replacing function code objects and
409 work around common pitfalls by replacing function code objects and
413 parts of classes previously in the module with new versions. This
410 parts of classes previously in the module with new versions. This
414 makes the following things to work:
411 makes the following things to work:
415
412
416 - Functions and classes imported via 'from xxx import foo' are upgraded
413 - Functions and classes imported via 'from xxx import foo' are upgraded
417 to new versions when 'xxx' is reloaded.
414 to new versions when 'xxx' is reloaded.
418
415
419 - Methods and properties of classes are upgraded on reload, so that
416 - Methods and properties of classes are upgraded on reload, so that
420 calling 'c.foo()' on an object 'c' created before the reload causes
417 calling 'c.foo()' on an object 'c' created before the reload causes
421 the new code for 'foo' to be executed.
418 the new code for 'foo' to be executed.
422
419
423 Some of the known remaining caveats are:
420 Some of the known remaining caveats are:
424
421
425 - Replacing code objects does not always succeed: changing a @property
422 - Replacing code objects does not always succeed: changing a @property
426 in a class to an ordinary method or a method to a member variable
423 in a class to an ordinary method or a method to a member variable
427 can cause problems (but in old objects only).
424 can cause problems (but in old objects only).
428
425
429 - Functions that are removed (eg. via monkey-patching) from a module
426 - Functions that are removed (eg. via monkey-patching) from a module
430 before it is reloaded are not upgraded.
427 before it is reloaded are not upgraded.
431
428
432 - C extension modules cannot be reloaded, and so cannot be
429 - C extension modules cannot be reloaded, and so cannot be
433 autoreloaded.
430 autoreloaded.
434
431
435 """
432 """
436 if parameter_s == '':
433 if parameter_s == '':
437 self._reloader.check(True)
434 self._reloader.check(True)
438 elif parameter_s == '0':
435 elif parameter_s == '0':
439 self._reloader.enabled = False
436 self._reloader.enabled = False
440 elif parameter_s == '1':
437 elif parameter_s == '1':
441 self._reloader.check_all = False
438 self._reloader.check_all = False
442 self._reloader.enabled = True
439 self._reloader.enabled = True
443 elif parameter_s == '2':
440 elif parameter_s == '2':
444 self._reloader.check_all = True
441 self._reloader.check_all = True
445 self._reloader.enabled = True
442 self._reloader.enabled = True
446
443
447 def magic_aimport(self, ipself, parameter_s='', stream=None):
444 def magic_aimport(self, ipself, parameter_s='', stream=None):
448 """%aimport => Import modules for automatic reloading.
445 """%aimport => Import modules for automatic reloading.
449
446
450 %aimport
447 %aimport
451 List modules to automatically import and not to import.
448 List modules to automatically import and not to import.
452
449
453 %aimport foo
450 %aimport foo
454 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
451 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
455
452
456 %aimport -foo
453 %aimport -foo
457 Mark module 'foo' to not be autoreloaded for %autoreload 1
454 Mark module 'foo' to not be autoreloaded for %autoreload 1
458
455
459 """
456 """
460
457
461 modname = parameter_s
458 modname = parameter_s
462 if not modname:
459 if not modname:
463 to_reload = self._reloader.modules.keys()
460 to_reload = self._reloader.modules.keys()
464 to_reload.sort()
461 to_reload.sort()
465 to_skip = self._reloader.skip_modules.keys()
462 to_skip = self._reloader.skip_modules.keys()
466 to_skip.sort()
463 to_skip.sort()
467 if stream is None:
464 if stream is None:
468 stream = sys.stdout
465 stream = sys.stdout
469 if self._reloader.check_all:
466 if self._reloader.check_all:
470 stream.write("Modules to reload:\nall-except-skipped\n")
467 stream.write("Modules to reload:\nall-except-skipped\n")
471 else:
468 else:
472 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
469 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
473 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
470 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
474 elif modname.startswith('-'):
471 elif modname.startswith('-'):
475 modname = modname[1:]
472 modname = modname[1:]
476 self._reloader.mark_module_skipped(modname)
473 self._reloader.mark_module_skipped(modname)
477 else:
474 else:
478 top_module, top_name = self._reloader.aimport_module(modname)
475 top_module, top_name = self._reloader.aimport_module(modname)
479
476
480 # Inject module to user namespace
477 # Inject module to user namespace
481 ipself.push({top_name: top_module})
478 ipself.push({top_name: top_module})
482
479
483 def pre_run_code_hook(self, ipself):
480 def pre_run_code_hook(self, ipself):
484 if not self._reloader.enabled:
481 if not self._reloader.enabled:
485 raise TryNext
482 raise TryNext
486 try:
483 try:
487 self._reloader.check()
484 self._reloader.check()
488 except:
485 except:
489 pass
486 pass
490
487
491 class AutoreloadPlugin(AutoreloadInterface, Plugin):
488 class AutoreloadPlugin(AutoreloadInterface, Plugin):
492 def __init__(self, shell=None, config=None):
489 def __init__(self, shell=None, config=None):
493 super(AutoreloadPlugin, self).__init__(shell=shell, config=config)
490 super(AutoreloadPlugin, self).__init__(shell=shell, config=config)
494
491
495 self.shell.define_magic('autoreload', self.magic_autoreload)
492 self.shell.define_magic('autoreload', self.magic_autoreload)
496 self.shell.define_magic('aimport', self.magic_aimport)
493 self.shell.define_magic('aimport', self.magic_aimport)
497 self.shell.set_hook('pre_run_code_hook', self.pre_run_code_hook)
494 self.shell.set_hook('pre_run_code_hook', self.pre_run_code_hook)
498
495
499 _loaded = False
496 _loaded = False
500
497
501 def load_ipython_extension(ip):
498 def load_ipython_extension(ip):
502 """Load the extension in IPython."""
499 """Load the extension in IPython."""
503 global _loaded
500 global _loaded
504 if not _loaded:
501 if not _loaded:
505 plugin = AutoreloadPlugin(shell=ip, config=ip.config)
502 plugin = AutoreloadPlugin(shell=ip, config=ip.config)
506 ip.plugin_manager.register_plugin('autoreload', plugin)
503 ip.plugin_manager.register_plugin('autoreload', plugin)
507 _loaded = True
504 _loaded = True
@@ -1,301 +1,300
1 import os
1 import os
2 import sys
2 import sys
3 import tempfile
3 import tempfile
4 import shutil
4 import shutil
5 import random
5 import random
6 import time
6 import time
7 from StringIO import StringIO
7 from StringIO import StringIO
8
8
9 import nose.tools as nt
9 import nose.tools as nt
10 import IPython.testing.tools as tt
10 import IPython.testing.tools as tt
11
11
12 from IPython.extensions.autoreload import AutoreloadInterface
12 from IPython.extensions.autoreload import AutoreloadInterface
13 from IPython.core.hooks import TryNext
13 from IPython.core.hooks import TryNext
14 from IPython.testing.decorators import knownfailureif
15
14
16 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
17 # Test fixture
16 # Test fixture
18 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
19
18
20 class FakeShell(object):
19 class FakeShell(object):
21 def __init__(self):
20 def __init__(self):
22 self.ns = {}
21 self.ns = {}
23 self.reloader = AutoreloadInterface()
22 self.reloader = AutoreloadInterface()
24
23
25 def run_code(self, code):
24 def run_code(self, code):
26 try:
25 try:
27 self.reloader.pre_run_code_hook(self)
26 self.reloader.pre_run_code_hook(self)
28 except TryNext:
27 except TryNext:
29 pass
28 pass
30 exec code in self.ns
29 exec code in self.ns
31
30
32 def push(self, items):
31 def push(self, items):
33 self.ns.update(items)
32 self.ns.update(items)
34
33
35 def magic_autoreload(self, parameter):
34 def magic_autoreload(self, parameter):
36 self.reloader.magic_autoreload(self, parameter)
35 self.reloader.magic_autoreload(self, parameter)
37
36
38 def magic_aimport(self, parameter, stream=None):
37 def magic_aimport(self, parameter, stream=None):
39 self.reloader.magic_aimport(self, parameter, stream=stream)
38 self.reloader.magic_aimport(self, parameter, stream=stream)
40
39
41
40
42 class Fixture(object):
41 class Fixture(object):
43 """Fixture for creating test module files"""
42 """Fixture for creating test module files"""
44
43
45 test_dir = None
44 test_dir = None
46 old_sys_path = None
45 old_sys_path = None
47 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
46 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
48
47
49 def setUp(self):
48 def setUp(self):
50 self.test_dir = tempfile.mkdtemp()
49 self.test_dir = tempfile.mkdtemp()
51 self.old_sys_path = list(sys.path)
50 self.old_sys_path = list(sys.path)
52 sys.path.insert(0, self.test_dir)
51 sys.path.insert(0, self.test_dir)
53 self.shell = FakeShell()
52 self.shell = FakeShell()
54
53
55 def tearDown(self):
54 def tearDown(self):
56 shutil.rmtree(self.test_dir)
55 shutil.rmtree(self.test_dir)
57 sys.path = self.old_sys_path
56 sys.path = self.old_sys_path
58 self.shell.reloader.enabled = False
57 self.shell.reloader.enabled = False
59
58
60 self.test_dir = None
59 self.test_dir = None
61 self.old_sys_path = None
60 self.old_sys_path = None
62 self.shell = None
61 self.shell = None
63
62
64 def get_module(self):
63 def get_module(self):
65 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
64 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
66 if module_name in sys.modules:
65 if module_name in sys.modules:
67 del sys.modules[module_name]
66 del sys.modules[module_name]
68 file_name = os.path.join(self.test_dir, module_name + ".py")
67 file_name = os.path.join(self.test_dir, module_name + ".py")
69 return module_name, file_name
68 return module_name, file_name
70
69
71 def write_file(self, filename, content):
70 def write_file(self, filename, content):
72 """
71 """
73 Write a file, and force a timestamp difference of at least one second
72 Write a file, and force a timestamp difference of at least one second
74
73
75 Notes
74 Notes
76 -----
75 -----
77 Python's .pyc files record the timestamp of their compilation
76 Python's .pyc files record the timestamp of their compilation
78 with a time resolution of one second.
77 with a time resolution of one second.
79
78
80 Therefore, we need to force a timestamp difference between .py
79 Therefore, we need to force a timestamp difference between .py
81 and .pyc, without having the .py file be timestamped in the
80 and .pyc, without having the .py file be timestamped in the
82 future, and without changing the timestamp of the .pyc file
81 future, and without changing the timestamp of the .pyc file
83 (because that is stored in the file). The only reliable way
82 (because that is stored in the file). The only reliable way
84 to achieve this seems to be to sleep.
83 to achieve this seems to be to sleep.
85
84
86 """
85 """
87
86
88 # Sleep one second + eps
87 # Sleep one second + eps
89 time.sleep(1.05)
88 time.sleep(1.05)
90
89
91 # Write
90 # Write
92 f = open(filename, 'w')
91 f = open(filename, 'w')
93 try:
92 try:
94 f.write(content)
93 f.write(content)
95 finally:
94 finally:
96 f.close()
95 f.close()
97
96
98 def new_module(self, code):
97 def new_module(self, code):
99 mod_name, mod_fn = self.get_module()
98 mod_name, mod_fn = self.get_module()
100 f = open(mod_fn, 'w')
99 f = open(mod_fn, 'w')
101 try:
100 try:
102 f.write(code)
101 f.write(code)
103 finally:
102 finally:
104 f.close()
103 f.close()
105 return mod_name, mod_fn
104 return mod_name, mod_fn
106
105
107 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
108 # Test automatic reloading
107 # Test automatic reloading
109 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
110
109
111 class TestAutoreload(Fixture):
110 class TestAutoreload(Fixture):
112 def _check_smoketest(self, use_aimport=True):
111 def _check_smoketest(self, use_aimport=True):
113 """
112 """
114 Functional test for the automatic reloader using either
113 Functional test for the automatic reloader using either
115 '%autoreload 1' or '%autoreload 2'
114 '%autoreload 1' or '%autoreload 2'
116 """
115 """
117
116
118 mod_name, mod_fn = self.new_module("""
117 mod_name, mod_fn = self.new_module("""
119 x = 9
118 x = 9
120
119
121 z = 123 # this item will be deleted
120 z = 123 # this item will be deleted
122
121
123 def foo(y):
122 def foo(y):
124 return y + 3
123 return y + 3
125
124
126 class Baz(object):
125 class Baz(object):
127 def __init__(self, x):
126 def __init__(self, x):
128 self.x = x
127 self.x = x
129 def bar(self, y):
128 def bar(self, y):
130 return self.x + y
129 return self.x + y
131 @property
130 @property
132 def quux(self):
131 def quux(self):
133 return 42
132 return 42
134 def zzz(self):
133 def zzz(self):
135 '''This method will be deleted below'''
134 '''This method will be deleted below'''
136 return 99
135 return 99
137
136
138 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
137 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
139 def foo(self):
138 def foo(self):
140 return 1
139 return 1
141 """)
140 """)
142
141
143 #
142 #
144 # Import module, and mark for reloading
143 # Import module, and mark for reloading
145 #
144 #
146 if use_aimport:
145 if use_aimport:
147 self.shell.magic_autoreload("1")
146 self.shell.magic_autoreload("1")
148 self.shell.magic_aimport(mod_name)
147 self.shell.magic_aimport(mod_name)
149 stream = StringIO()
148 stream = StringIO()
150 self.shell.magic_aimport("", stream=stream)
149 self.shell.magic_aimport("", stream=stream)
151 nt.assert_true(("Modules to reload:\n%s" % mod_name) in
150 nt.assert_true(("Modules to reload:\n%s" % mod_name) in
152 stream.getvalue())
151 stream.getvalue())
153
152
154 nt.assert_raises(
153 nt.assert_raises(
155 ImportError,
154 ImportError,
156 self.shell.magic_aimport, "tmpmod_as318989e89ds")
155 self.shell.magic_aimport, "tmpmod_as318989e89ds")
157 else:
156 else:
158 self.shell.magic_autoreload("2")
157 self.shell.magic_autoreload("2")
159 self.shell.run_code("import %s" % mod_name)
158 self.shell.run_code("import %s" % mod_name)
160 stream = StringIO()
159 stream = StringIO()
161 self.shell.magic_aimport("", stream=stream)
160 self.shell.magic_aimport("", stream=stream)
162 nt.assert_true("Modules to reload:\nall-except-skipped" in
161 nt.assert_true("Modules to reload:\nall-except-skipped" in
163 stream.getvalue())
162 stream.getvalue())
164 nt.assert_true(mod_name in self.shell.ns)
163 nt.assert_true(mod_name in self.shell.ns)
165
164
166 mod = sys.modules[mod_name]
165 mod = sys.modules[mod_name]
167
166
168 #
167 #
169 # Test module contents
168 # Test module contents
170 #
169 #
171 old_foo = mod.foo
170 old_foo = mod.foo
172 old_obj = mod.Baz(9)
171 old_obj = mod.Baz(9)
173 old_obj2 = mod.Bar()
172 old_obj2 = mod.Bar()
174
173
175 def check_module_contents():
174 def check_module_contents():
176 nt.assert_equal(mod.x, 9)
175 nt.assert_equal(mod.x, 9)
177 nt.assert_equal(mod.z, 123)
176 nt.assert_equal(mod.z, 123)
178
177
179 nt.assert_equal(old_foo(0), 3)
178 nt.assert_equal(old_foo(0), 3)
180 nt.assert_equal(mod.foo(0), 3)
179 nt.assert_equal(mod.foo(0), 3)
181
180
182 obj = mod.Baz(9)
181 obj = mod.Baz(9)
183 nt.assert_equal(old_obj.bar(1), 10)
182 nt.assert_equal(old_obj.bar(1), 10)
184 nt.assert_equal(obj.bar(1), 10)
183 nt.assert_equal(obj.bar(1), 10)
185 nt.assert_equal(obj.quux, 42)
184 nt.assert_equal(obj.quux, 42)
186 nt.assert_equal(obj.zzz(), 99)
185 nt.assert_equal(obj.zzz(), 99)
187
186
188 obj2 = mod.Bar()
187 obj2 = mod.Bar()
189 nt.assert_equal(old_obj2.foo(), 1)
188 nt.assert_equal(old_obj2.foo(), 1)
190 nt.assert_equal(obj2.foo(), 1)
189 nt.assert_equal(obj2.foo(), 1)
191
190
192 check_module_contents()
191 check_module_contents()
193
192
194 #
193 #
195 # Simulate a failed reload: no reload should occur and exactly
194 # Simulate a failed reload: no reload should occur and exactly
196 # one error message should be printed
195 # one error message should be printed
197 #
196 #
198 self.write_file(mod_fn, """
197 self.write_file(mod_fn, """
199 a syntax error
198 a syntax error
200 """)
199 """)
201
200
202 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
201 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
203 self.shell.run_code("pass") # trigger reload
202 self.shell.run_code("pass") # trigger reload
204 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
203 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
205 self.shell.run_code("pass") # trigger another reload
204 self.shell.run_code("pass") # trigger another reload
206 check_module_contents()
205 check_module_contents()
207
206
208 #
207 #
209 # Rewrite module (this time reload should succeed)
208 # Rewrite module (this time reload should succeed)
210 #
209 #
211 self.write_file(mod_fn, """
210 self.write_file(mod_fn, """
212 x = 10
211 x = 10
213
212
214 def foo(y):
213 def foo(y):
215 return y + 4
214 return y + 4
216
215
217 class Baz(object):
216 class Baz(object):
218 def __init__(self, x):
217 def __init__(self, x):
219 self.x = x
218 self.x = x
220 def bar(self, y):
219 def bar(self, y):
221 return self.x + y + 1
220 return self.x + y + 1
222 @property
221 @property
223 def quux(self):
222 def quux(self):
224 return 43
223 return 43
225
224
226 class Bar: # old-style class
225 class Bar: # old-style class
227 def foo(self):
226 def foo(self):
228 return 2
227 return 2
229 """)
228 """)
230
229
231 def check_module_contents():
230 def check_module_contents():
232 nt.assert_equal(mod.x, 10)
231 nt.assert_equal(mod.x, 10)
233 nt.assert_false(hasattr(mod, 'z'))
232 nt.assert_false(hasattr(mod, 'z'))
234
233
235 nt.assert_equal(old_foo(0), 4) # superreload magic!
234 nt.assert_equal(old_foo(0), 4) # superreload magic!
236 nt.assert_equal(mod.foo(0), 4)
235 nt.assert_equal(mod.foo(0), 4)
237
236
238 obj = mod.Baz(9)
237 obj = mod.Baz(9)
239 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
238 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
240 nt.assert_equal(obj.bar(1), 11)
239 nt.assert_equal(obj.bar(1), 11)
241
240
242 nt.assert_equal(old_obj.quux, 43)
241 nt.assert_equal(old_obj.quux, 43)
243 nt.assert_equal(obj.quux, 43)
242 nt.assert_equal(obj.quux, 43)
244
243
245 nt.assert_false(hasattr(old_obj, 'zzz'))
244 nt.assert_false(hasattr(old_obj, 'zzz'))
246 nt.assert_false(hasattr(obj, 'zzz'))
245 nt.assert_false(hasattr(obj, 'zzz'))
247
246
248 obj2 = mod.Bar()
247 obj2 = mod.Bar()
249 nt.assert_equal(old_obj2.foo(), 2)
248 nt.assert_equal(old_obj2.foo(), 2)
250 nt.assert_equal(obj2.foo(), 2)
249 nt.assert_equal(obj2.foo(), 2)
251
250
252 self.shell.run_code("pass") # trigger reload
251 self.shell.run_code("pass") # trigger reload
253 check_module_contents()
252 check_module_contents()
254
253
255 #
254 #
256 # Another failure case: deleted file (shouldn't reload)
255 # Another failure case: deleted file (shouldn't reload)
257 #
256 #
258 os.unlink(mod_fn)
257 os.unlink(mod_fn)
259
258
260 self.shell.run_code("pass") # trigger reload
259 self.shell.run_code("pass") # trigger reload
261 check_module_contents()
260 check_module_contents()
262
261
263 #
262 #
264 # Disable autoreload and rewrite module: no reload should occur
263 # Disable autoreload and rewrite module: no reload should occur
265 #
264 #
266 if use_aimport:
265 if use_aimport:
267 self.shell.magic_aimport("-" + mod_name)
266 self.shell.magic_aimport("-" + mod_name)
268 stream = StringIO()
267 stream = StringIO()
269 self.shell.magic_aimport("", stream=stream)
268 self.shell.magic_aimport("", stream=stream)
270 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
269 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
271 stream.getvalue())
270 stream.getvalue())
272
271
273 # This should succeed, although no such module exists
272 # This should succeed, although no such module exists
274 self.shell.magic_aimport("-tmpmod_as318989e89ds")
273 self.shell.magic_aimport("-tmpmod_as318989e89ds")
275 else:
274 else:
276 self.shell.magic_autoreload("0")
275 self.shell.magic_autoreload("0")
277
276
278 self.write_file(mod_fn, """
277 self.write_file(mod_fn, """
279 x = -99
278 x = -99
280 """)
279 """)
281
280
282 self.shell.run_code("pass") # trigger reload
281 self.shell.run_code("pass") # trigger reload
283 self.shell.run_code("pass")
282 self.shell.run_code("pass")
284 check_module_contents()
283 check_module_contents()
285
284
286 #
285 #
287 # Re-enable autoreload: reload should now occur
286 # Re-enable autoreload: reload should now occur
288 #
287 #
289 if use_aimport:
288 if use_aimport:
290 self.shell.magic_aimport(mod_name)
289 self.shell.magic_aimport(mod_name)
291 else:
290 else:
292 self.shell.magic_autoreload("")
291 self.shell.magic_autoreload("")
293
292
294 self.shell.run_code("pass") # trigger reload
293 self.shell.run_code("pass") # trigger reload
295 nt.assert_equal(mod.x, -99)
294 nt.assert_equal(mod.x, -99)
296
295
297 def test_smoketest_aimport(self):
296 def test_smoketest_aimport(self):
298 self._check_smoketest(use_aimport=True)
297 self._check_smoketest(use_aimport=True)
299
298
300 def test_smoketest_autoreload(self):
299 def test_smoketest_autoreload(self):
301 self._check_smoketest(use_aimport=False)
300 self._check_smoketest(use_aimport=False)
General Comments 0
You need to be logged in to leave comments. Login now