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