123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- """Tests for autoreload extension.
- """
- #-----------------------------------------------------------------------------
- # Copyright (c) 2012 IPython Development Team.
- #
- # Distributed under the terms of the Modified BSD License.
- #
- # The full license is in the file COPYING.txt, distributed with this software.
- #-----------------------------------------------------------------------------
- #-----------------------------------------------------------------------------
- # Imports
- #-----------------------------------------------------------------------------
- import os
- import sys
- import tempfile
- import shutil
- import random
- import time
- import nose.tools as nt
- import IPython.testing.tools as tt
- from IPython.extensions.autoreload import AutoreloadMagics
- from IPython.core.events import EventManager, pre_run_cell
- from IPython.utils.py3compat import PY3
- if PY3:
- from io import StringIO
- else:
- from StringIO import StringIO
- #-----------------------------------------------------------------------------
- # Test fixture
- #-----------------------------------------------------------------------------
- noop = lambda *a, **kw: None
- class FakeShell(object):
- def __init__(self):
- self.ns = {}
- self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
- self.auto_magics = AutoreloadMagics(shell=self)
- self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
- register_magics = set_hook = noop
- def run_code(self, code):
- self.events.trigger('pre_run_cell')
- exec(code, self.ns)
- self.auto_magics.post_execute_hook()
- def push(self, items):
- self.ns.update(items)
- def magic_autoreload(self, parameter):
- self.auto_magics.autoreload(parameter)
- def magic_aimport(self, parameter, stream=None):
- self.auto_magics.aimport(parameter, stream=stream)
- self.auto_magics.post_execute_hook()
- 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.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_in(("Modules to reload:\n%s" % mod_name), stream.getvalue())
- with 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_in(mod_name, 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
- """)
- with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
- self.shell.run_code("pass") # trigger reload
- with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
- self.shell.run_code("pass") # trigger another reload
- check_module_contents()
- #
- # 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)
|