Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

TST: extensions/autoreload: add tests for the autoreload extension

  • Loading branch information...
commit 7fd3f133b8052f322fbc59d2d11e5d96c8cfee16 1 parent cdc9b79
@pv authored
Showing with 307 additions and 0 deletions.
  1. +307 −0 IPython/extensions/tests/test_autoreload.py
View
307 IPython/extensions/tests/test_autoreload.py
@@ -0,0 +1,307 @@
+import os
+import sys
+import tempfile
+import shutil
+import random
+import time
+from StringIO import StringIO
+
+import nose.tools as nt
+
+from IPython.extensions.autoreload import AutoreloadInterface
+from IPython.core.hooks import TryNext
+
+#-----------------------------------------------------------------------------
+# Test fixture
+#-----------------------------------------------------------------------------
+
+class FakeShell(object):
+ def __init__(self):
+ self.ns = {}
+ self.reloader = AutoreloadInterface()
+
+ def run_code(self, code):
+ try:
+ self.reloader.pre_run_code_hook(self)
+ except TryNext:
+ pass
+ exec code in self.ns
+
+ def push(self, items):
+ self.ns.update(items)
+
+ def magic_autoreload(self, parameter):
+ self.reloader.magic_autoreload(self, parameter)
+
+ def magic_aimport(self, parameter, stream=None):
+ self.reloader.magic_aimport(self, parameter, stream=stream)
+
+
+class Fixture(object):
+ """Fixture for creating test module files"""
+
+ test_dir = None
+ old_sys_path = None
+ filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
+
+ def setUp(self):
+ self.test_dir = tempfile.mkdtemp()
+ self.old_sys_path = list(sys.path)
+ sys.path.insert(0, self.test_dir)
+ self.shell = FakeShell()
+
+ def tearDown(self):
+ shutil.rmtree(self.test_dir)
+ sys.path = self.old_sys_path
+ self.shell.reloader.enabled = False
+
+ self.test_dir = None
+ self.old_sys_path = None
+ self.shell = None
+
+ def get_module(self):
+ module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
+ if module_name in sys.modules:
+ del sys.modules[module_name]
+ file_name = os.path.join(self.test_dir, module_name + ".py")
+ return module_name, file_name
+
+ def write_file(self, filename, content):
+ """
+ Write a file, and force a timestamp difference of at least one second
+
+ Notes
+ -----
+ Python's .pyc files record the timestamp of their compilation
+ with a time resolution of one second.
+
+ Therefore, we need to force a timestamp difference between .py
+ and .pyc, without having the .py file be timestamped in the
+ future, and without changing the timestamp of the .pyc file
+ (because that is stored in the file). The only reliable way
+ to achieve this seems to be to sleep.
+
+ """
+
+ # Sleep one second + eps
+ time.sleep(1.05)
+
+ # Write
+ f = open(filename, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+
+ def new_module(self, code):
+ mod_name, mod_fn = self.get_module()
+ f = open(mod_fn, 'w')
+ try:
+ f.write(code)
+ finally:
+ f.close()
+ return mod_name, mod_fn
+
+#-----------------------------------------------------------------------------
+# Test automatic reloading
+#-----------------------------------------------------------------------------
+
+class TestAutoreload(Fixture):
+ def _check_smoketest(self, use_aimport=True):
+ """
+ Functional test for the automatic reloader using either
+ '%autoreload 1' or '%autoreload 2'
+ """
+
+ mod_name, mod_fn = self.new_module("""
+x = 9
+
+z = 123 # this item will be deleted
+
+def foo(y):
+ return y + 3
+
+class Baz(object):
+ def __init__(self, x):
+ self.x = x
+ def bar(self, y):
+ return self.x + y
+ @property
+ def quux(self):
+ return 42
+ def zzz(self):
+ '''This method will be deleted below'''
+ return 99
+
+class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
+ def foo(self):
+ return 1
+""")
+
+ #
+ # Import module, and mark for reloading
+ #
+ if use_aimport:
+ self.shell.magic_autoreload("1")
+ self.shell.magic_aimport(mod_name)
+ stream = StringIO()
+ self.shell.magic_aimport("", stream=stream)
+ nt.assert_true(("Modules to reload:\n%s" % mod_name) in
+ stream.getvalue())
+
+ nt.assert_raises(
+ ImportError,
+ self.shell.magic_aimport, "tmpmod_as318989e89ds")
+ else:
+ self.shell.magic_autoreload("2")
+ self.shell.run_code("import %s" % mod_name)
+ stream = StringIO()
+ self.shell.magic_aimport("", stream=stream)
+ nt.assert_true("Modules to reload:\nall-except-skipped" in
+ stream.getvalue())
+ nt.assert_true(mod_name in self.shell.ns)
+
+ mod = sys.modules[mod_name]
+
+ #
+ # Test module contents
+ #
+ old_foo = mod.foo
+ old_obj = mod.Baz(9)
+ old_obj2 = mod.Bar()
+
+ def check_module_contents():
+ nt.assert_equal(mod.x, 9)
+ nt.assert_equal(mod.z, 123)
+
+ nt.assert_equal(old_foo(0), 3)
+ nt.assert_equal(mod.foo(0), 3)
+
+ obj = mod.Baz(9)
+ nt.assert_equal(old_obj.bar(1), 10)
+ nt.assert_equal(obj.bar(1), 10)
+ nt.assert_equal(obj.quux, 42)
+ nt.assert_equal(obj.zzz(), 99)
+
+ obj2 = mod.Bar()
+ nt.assert_equal(old_obj2.foo(), 1)
+ nt.assert_equal(obj2.foo(), 1)
+
+ check_module_contents()
+
+ #
+ # Simulate a failed reload: no reload should occur and exactly
+ # one error message should be printed
+ #
+ self.write_file(mod_fn, """
+a syntax error
+""")
+
+ old_stderr = sys.stderr
+ new_stderr = StringIO()
+ sys.stderr = new_stderr
+ try:
+ self.shell.run_code("pass") # trigger reload
+ self.shell.run_code("pass") # trigger another reload
+ check_module_contents()
+ finally:
+ sys.stderr = old_stderr
+
+ nt.assert_true(('[autoreload of %s failed:' % mod_name) in
+ new_stderr.getvalue())
+ nt.assert_equal(new_stderr.getvalue().count('[autoreload of'), 1)
+
+ #
+ # Rewrite module (this time reload should succeed)
+ #
+ self.write_file(mod_fn, """
+x = 10
+
+def foo(y):
+ return y + 4
+
+class Baz(object):
+ def __init__(self, x):
+ self.x = x
+ def bar(self, y):
+ return self.x + y + 1
+ @property
+ def quux(self):
+ return 43
+
+class Bar: # old-style class
+ def foo(self):
+ return 2
+""")
+
+ def check_module_contents():
+ nt.assert_equal(mod.x, 10)
+ nt.assert_false(hasattr(mod, 'z'))
+
+ nt.assert_equal(old_foo(0), 4) # superreload magic!
+ nt.assert_equal(mod.foo(0), 4)
+
+ obj = mod.Baz(9)
+ nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
+ nt.assert_equal(obj.bar(1), 11)
+
+ nt.assert_equal(old_obj.quux, 43)
+ nt.assert_equal(obj.quux, 43)
+
+ nt.assert_false(hasattr(old_obj, 'zzz'))
+ nt.assert_false(hasattr(obj, 'zzz'))
+
+ obj2 = mod.Bar()
+ nt.assert_equal(old_obj2.foo(), 2)
+ nt.assert_equal(obj2.foo(), 2)
+
+ self.shell.run_code("pass") # trigger reload
+ check_module_contents()
+
+ #
+ # Another failure case: deleted file (shouldn't reload)
+ #
+ os.unlink(mod_fn)
+
+ self.shell.run_code("pass") # trigger reload
+ check_module_contents()
+
+ #
+ # Disable autoreload and rewrite module: no reload should occur
+ #
+ if use_aimport:
+ self.shell.magic_aimport("-" + mod_name)
+ stream = StringIO()
+ self.shell.magic_aimport("", stream=stream)
+ nt.assert_true(("Modules to skip:\n%s" % mod_name) in
+ stream.getvalue())
+
+ # This should succeed, although no such module exists
+ self.shell.magic_aimport("-tmpmod_as318989e89ds")
+ else:
+ self.shell.magic_autoreload("0")
+
+ self.write_file(mod_fn, """
+x = -99
+""")
+
+ self.shell.run_code("pass") # trigger reload
+ self.shell.run_code("pass")
+ check_module_contents()
+
+ #
+ # Re-enable autoreload: reload should now occur
+ #
+ if use_aimport:
+ self.shell.magic_aimport(mod_name)
+ else:
+ self.shell.magic_autoreload("")
+
+ self.shell.run_code("pass") # trigger reload
+ nt.assert_equal(mod.x, -99)
+
+ def test_smoketest_aimport(self):
+ self._check_smoketest(use_aimport=True)
+
+ def test_smoketest_autoreload(self):
+ self._check_smoketest(use_aimport=False)
Please sign in to comment.
Something went wrong with that request. Please try again.