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