test_run.py
242 lines
| 7.3 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r2414 | """Tests for code execution (%run and related), which is particularly tricky. | ||
Because of how %run manages namespaces, and the fact that we are trying here to | ||||
verify subtle object deletion and reference counting issues, the %run tests | ||||
will be kept in this separate file. This makes it easier to aggregate in one | ||||
place the tricks needed to handle it; most other magics are much easier to test | ||||
and we do so in a common test_magic file. | ||||
""" | ||||
from __future__ import absolute_import | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import os | ||||
import sys | ||||
import tempfile | ||||
import nose.tools as nt | ||||
Min RK
|
r4105 | from nose import SkipTest | ||
Fernando Perez
|
r2414 | |||
from IPython.testing import decorators as dec | ||||
from IPython.testing import tools as tt | ||||
Thomas Kluyver
|
r4896 | from IPython.utils import py3compat | ||
Fernando Perez
|
r2414 | |||
#----------------------------------------------------------------------------- | ||||
# Test functions begin | ||||
#----------------------------------------------------------------------------- | ||||
def doctest_refbug(): | ||||
"""Very nasty problem with references held by multiple runs of a script. | ||||
Thomas Kluyver
|
r3917 | See: https://github.com/ipython/ipython/issues/141 | ||
Fernando Perez
|
r2414 | |||
In [1]: _ip.clear_main_mod_cache() | ||||
# random | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2414 | In [2]: %run refbug | ||
In [3]: call_f() | ||||
lowercased: hello | ||||
In [4]: %run refbug | ||||
In [5]: call_f() | ||||
lowercased: hello | ||||
lowercased: hello | ||||
""" | ||||
def doctest_run_builtins(): | ||||
r"""Check that %run doesn't damage __builtins__. | ||||
In [1]: import tempfile | ||||
In [2]: bid1 = id(__builtins__) | ||||
In [3]: fname = tempfile.mkstemp('.py')[1] | ||||
In [3]: f = open(fname,'w') | ||||
Thomas Kluyver
|
r4895 | In [4]: dummy= f.write('pass\n') | ||
Fernando Perez
|
r2414 | |||
In [5]: f.flush() | ||||
In [6]: t1 = type(__builtins__) | ||||
Fernando Perez
|
r2484 | In [7]: %run $fname | ||
Fernando Perez
|
r2414 | |||
In [7]: f.close() | ||||
In [8]: bid2 = id(__builtins__) | ||||
In [9]: t2 = type(__builtins__) | ||||
In [10]: t1 == t2 | ||||
Out[10]: True | ||||
In [10]: bid1 == bid2 | ||||
Out[10]: True | ||||
In [12]: try: | ||||
....: os.unlink(fname) | ||||
....: except: | ||||
....: pass | ||||
Bernardo B. Marques
|
r4872 | ....: | ||
Fernando Perez
|
r2414 | """ | ||
Fernando Perez
|
r3107 | |||
Thomas Kluyver
|
r4896 | @py3compat.doctest_refactor_print | ||
Fernando Perez
|
r3107 | def doctest_reset_del(): | ||
"""Test that resetting doesn't cause errors in __del__ methods. | ||||
In [2]: class A(object): | ||||
...: def __del__(self): | ||||
Thomas Kluyver
|
r3156 | ...: print str("Hi") | ||
Bernardo B. Marques
|
r4872 | ...: | ||
Fernando Perez
|
r3107 | |||
In [3]: a = A() | ||||
In [4]: get_ipython().reset() | ||||
Thomas Kluyver
|
r3156 | Hi | ||
Fernando Perez
|
r3107 | |||
In [5]: 1+1 | ||||
Out[5]: 2 | ||||
""" | ||||
Fernando Perez
|
r2414 | |||
# For some tests, it will be handy to organize them in a class with a common | ||||
# setup that makes a temp file | ||||
Fernando Perez
|
r2415 | class TestMagicRunPass(tt.TempFileMixin): | ||
Fernando Perez
|
r2414 | |||
def setup(self): | ||||
"""Make a valid python temp file.""" | ||||
self.mktmp('pass\n') | ||||
Alcides
|
r5238 | |||
Fernando Perez
|
r2414 | def run_tmpfile(self): | ||
_ip = get_ipython() | ||||
# This fails on Windows if self.tmpfile.name has spaces or "~" in it. | ||||
# See below and ticket https://bugs.launchpad.net/bugs/366353 | ||||
Fernando Perez
|
r2484 | _ip.magic('run %s' % self.fname) | ||
Alcides
|
r5238 | |||
def run_tmpfile_p(self): | ||||
_ip = get_ipython() | ||||
# This fails on Windows if self.tmpfile.name has spaces or "~" in it. | ||||
# See below and ticket https://bugs.launchpad.net/bugs/366353 | ||||
_ip.magic('run -p %s' % self.fname) | ||||
Fernando Perez
|
r2414 | |||
def test_builtins_id(self): | ||||
"""Check that %run doesn't damage __builtins__ """ | ||||
_ip = get_ipython() | ||||
# Test that the id of __builtins__ is not modified by %run | ||||
bid1 = id(_ip.user_ns['__builtins__']) | ||||
self.run_tmpfile() | ||||
bid2 = id(_ip.user_ns['__builtins__']) | ||||
tt.assert_equals(bid1, bid2) | ||||
def test_builtins_type(self): | ||||
"""Check that the type of __builtins__ doesn't change with %run. | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2414 | However, the above could pass if __builtins__ was already modified to | ||
be a dict (it should be a module) by a previous use of %run. So we | ||||
also check explicitly that it really is a module: | ||||
""" | ||||
_ip = get_ipython() | ||||
self.run_tmpfile() | ||||
tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys)) | ||||
def test_prompts(self): | ||||
"""Test that prompts correctly generate after %run""" | ||||
self.run_tmpfile() | ||||
_ip = get_ipython() | ||||
Brian Granger
|
r2781 | p2 = str(_ip.displayhook.prompt2).strip() | ||
Fernando Perez
|
r2414 | nt.assert_equals(p2[:3], '...') | ||
Alcides
|
r5238 | |||
def test_run_profile( self ): | ||||
"""Test that the option -p, which invokes the profiler, do not | ||||
crash by invoking execfile""" | ||||
_ip = get_ipython() | ||||
self.run_tmpfile_p() | ||||
Fernando Perez
|
r2414 | |||
Fernando Perez
|
r2415 | class TestMagicRunSimple(tt.TempFileMixin): | ||
Fernando Perez
|
r2414 | |||
def test_simpledef(self): | ||||
"""Test that simple class definitions work.""" | ||||
src = ("class foo: pass\n" | ||||
"def f(): return foo()") | ||||
self.mktmp(src) | ||||
Fernando Perez
|
r2451 | _ip.magic('run %s' % self.fname) | ||
Thomas Kluyver
|
r3752 | _ip.run_cell('t = isinstance(f(), foo)') | ||
Fernando Perez
|
r2414 | nt.assert_true(_ip.user_ns['t']) | ||
Fernando Perez
|
r2446 | |||
Fernando Perez
|
r2414 | def test_obj_del(self): | ||
"""Test that object's __del__ methods are called on exit.""" | ||||
Min RK
|
r4105 | if sys.platform == 'win32': | ||
try: | ||||
import win32api | ||||
except ImportError: | ||||
raise SkipTest("Test requires pywin32") | ||||
Fernando Perez
|
r2414 | src = ("class A(object):\n" | ||
" def __del__(self):\n" | ||||
" print 'object A deleted'\n" | ||||
"a = A()\n") | ||||
Thomas Kluyver
|
r4896 | self.mktmp(py3compat.doctest_refactor_print(src)) | ||
MinRK
|
r5147 | if dec.module_not_available('sqlite3'): | ||
err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' | ||||
else: | ||||
err = None | ||||
tt.ipexec_validate(self.fname, 'object A deleted', err) | ||||
@dec.skip_known_failure | ||||
Paul Ivanov
|
r3502 | def test_aggressive_namespace_cleanup(self): | ||
"""Test that namespace cleanup is not too aggressive GH-238 | ||||
Paul Ivanov
|
r3503 | |||
Returning from another run magic deletes the namespace""" | ||||
Paul Ivanov
|
r3502 | # see ticket https://github.com/ipython/ipython/issues/238 | ||
class secondtmp(tt.TempFileMixin): pass | ||||
empty = secondtmp() | ||||
empty.mktmp('') | ||||
src = ("ip = get_ipython()\n" | ||||
"for i in range(5):\n" | ||||
" try:\n" | ||||
" ip.magic('run %s')\n" | ||||
" except NameError, e:\n" | ||||
" print i;break\n" % empty.fname) | ||||
Thomas Kluyver
|
r4896 | self.mktmp(py3compat.doctest_refactor_print(src)) | ||
Paul Ivanov
|
r3502 | _ip.magic('run %s' % self.fname) | ||
Thomas Kluyver
|
r3752 | _ip.run_cell('ip == get_ipython()') | ||
Paul Ivanov
|
r3502 | tt.assert_equals(_ip.user_ns['i'], 5) | ||
Fernando Perez
|
r2414 | |||
Fernando Perez
|
r2446 | @dec.skip_win32 | ||
Fernando Perez
|
r2414 | def test_tclass(self): | ||
mydir = os.path.dirname(__file__) | ||||
tc = os.path.join(mydir, 'tclass') | ||||
src = ("%%run '%s' C-first\n" | ||||
Thomas Kluyver
|
r3763 | "%%run '%s' C-second\n" | ||
"%%run '%s' C-third\n") % (tc, tc, tc) | ||||
Fernando Perez
|
r2414 | self.mktmp(src, '.ipy') | ||
out = """\ | ||||
MinRK
|
r5126 | ARGV 1-: ['C-first'] | ||
ARGV 1-: ['C-second'] | ||||
Fernando Perez
|
r2414 | tclass.py: deleting object: C-first | ||
MinRK
|
r5126 | ARGV 1-: ['C-third'] | ||
Thomas Kluyver
|
r3762 | tclass.py: deleting object: C-second | ||
Thomas Kluyver
|
r3763 | tclass.py: deleting object: C-third | ||
Fernando Perez
|
r2414 | """ | ||
MinRK
|
r5147 | if dec.module_not_available('sqlite3'): | ||
err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' | ||||
else: | ||||
err = None | ||||
tt.ipexec_validate(self.fname, out, err) | ||||
Thomas Kluyver
|
r5466 | |||
def test_run_i_after_reset(self): | ||||
"""Check that %run -i still works after %reset (gh-693)""" | ||||
src = "yy = zz\n" | ||||
self.mktmp(src) | ||||
_ip.run_cell("zz = 23") | ||||
_ip.magic('run -i %s' % self.fname) | ||||
tt.assert_equals(_ip.user_ns['yy'], 23) | ||||
_ip.magic('reset -f') | ||||
_ip.run_cell("zz = 23") | ||||
_ip.magic('run -i %s' % self.fname) | ||||
tt.assert_equals(_ip.user_ns['yy'], 23) | ||||