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