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