Show More
@@ -0,0 +1,185 b'' | |||
|
1 | """Tests for code execution (%run and related), which is particularly tricky. | |
|
2 | ||
|
3 | Because of how %run manages namespaces, and the fact that we are trying here to | |
|
4 | verify subtle object deletion and reference counting issues, the %run tests | |
|
5 | will be kept in this separate file. This makes it easier to aggregate in one | |
|
6 | place the tricks needed to handle it; most other magics are much easier to test | |
|
7 | and we do so in a common test_magic file. | |
|
8 | """ | |
|
9 | from __future__ import absolute_import | |
|
10 | ||
|
11 | #----------------------------------------------------------------------------- | |
|
12 | # Imports | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | # stdlib | |
|
16 | import os | |
|
17 | import sys | |
|
18 | import tempfile | |
|
19 | ||
|
20 | # third-party | |
|
21 | import nose.tools as nt | |
|
22 | ||
|
23 | # our own | |
|
24 | from IPython.utils.platutils import find_cmd | |
|
25 | from IPython.utils import genutils | |
|
26 | from IPython.testing import decorators as dec | |
|
27 | from IPython.testing import tools as tt | |
|
28 | ||
|
29 | #----------------------------------------------------------------------------- | |
|
30 | # Test functions begin | |
|
31 | #----------------------------------------------------------------------------- | |
|
32 | ||
|
33 | def doctest_refbug(): | |
|
34 | """Very nasty problem with references held by multiple runs of a script. | |
|
35 | See: https://bugs.launchpad.net/ipython/+bug/269966 | |
|
36 | ||
|
37 | In [1]: _ip.clear_main_mod_cache() | |
|
38 | # random | |
|
39 | ||
|
40 | In [2]: %run refbug | |
|
41 | ||
|
42 | In [3]: call_f() | |
|
43 | lowercased: hello | |
|
44 | ||
|
45 | In [4]: %run refbug | |
|
46 | ||
|
47 | In [5]: call_f() | |
|
48 | lowercased: hello | |
|
49 | lowercased: hello | |
|
50 | """ | |
|
51 | ||
|
52 | ||
|
53 | def doctest_run_builtins(): | |
|
54 | r"""Check that %run doesn't damage __builtins__. | |
|
55 | ||
|
56 | In [1]: import tempfile | |
|
57 | ||
|
58 | In [2]: bid1 = id(__builtins__) | |
|
59 | ||
|
60 | In [3]: fname = tempfile.mkstemp('.py')[1] | |
|
61 | ||
|
62 | In [3]: f = open(fname,'w') | |
|
63 | ||
|
64 | In [4]: f.write('pass\n') | |
|
65 | ||
|
66 | In [5]: f.flush() | |
|
67 | ||
|
68 | In [6]: t1 = type(__builtins__) | |
|
69 | ||
|
70 | In [7]: %run "$fname" | |
|
71 | ||
|
72 | In [7]: f.close() | |
|
73 | ||
|
74 | In [8]: bid2 = id(__builtins__) | |
|
75 | ||
|
76 | In [9]: t2 = type(__builtins__) | |
|
77 | ||
|
78 | In [10]: t1 == t2 | |
|
79 | Out[10]: True | |
|
80 | ||
|
81 | In [10]: bid1 == bid2 | |
|
82 | Out[10]: True | |
|
83 | ||
|
84 | In [12]: try: | |
|
85 | ....: os.unlink(fname) | |
|
86 | ....: except: | |
|
87 | ....: pass | |
|
88 | ....: | |
|
89 | """ | |
|
90 | ||
|
91 | # For some tests, it will be handy to organize them in a class with a common | |
|
92 | # setup that makes a temp file | |
|
93 | ||
|
94 | class TempFileMixin(object): | |
|
95 | def mktmp(self, src, ext='.py'): | |
|
96 | """Make a valid python temp file.""" | |
|
97 | fname, f = tt.temp_pyfile(src, ext) | |
|
98 | self.tmpfile = f | |
|
99 | self.fname = fname | |
|
100 | ||
|
101 | def teardown(self): | |
|
102 | self.tmpfile.close() | |
|
103 | try: | |
|
104 | os.unlink(self.fname) | |
|
105 | except: | |
|
106 | # On Windows, even though we close the file, we still can't delete | |
|
107 | # it. I have no clue why | |
|
108 | pass | |
|
109 | ||
|
110 | ||
|
111 | class TestMagicRunPass(TempFileMixin): | |
|
112 | ||
|
113 | def setup(self): | |
|
114 | """Make a valid python temp file.""" | |
|
115 | self.mktmp('pass\n') | |
|
116 | ||
|
117 | def run_tmpfile(self): | |
|
118 | _ip = get_ipython() | |
|
119 | # This fails on Windows if self.tmpfile.name has spaces or "~" in it. | |
|
120 | # See below and ticket https://bugs.launchpad.net/bugs/366353 | |
|
121 | _ip.magic('run "%s"' % self.fname) | |
|
122 | ||
|
123 | def test_builtins_id(self): | |
|
124 | """Check that %run doesn't damage __builtins__ """ | |
|
125 | _ip = get_ipython() | |
|
126 | # Test that the id of __builtins__ is not modified by %run | |
|
127 | bid1 = id(_ip.user_ns['__builtins__']) | |
|
128 | self.run_tmpfile() | |
|
129 | bid2 = id(_ip.user_ns['__builtins__']) | |
|
130 | tt.assert_equals(bid1, bid2) | |
|
131 | ||
|
132 | def test_builtins_type(self): | |
|
133 | """Check that the type of __builtins__ doesn't change with %run. | |
|
134 | ||
|
135 | However, the above could pass if __builtins__ was already modified to | |
|
136 | be a dict (it should be a module) by a previous use of %run. So we | |
|
137 | also check explicitly that it really is a module: | |
|
138 | """ | |
|
139 | _ip = get_ipython() | |
|
140 | self.run_tmpfile() | |
|
141 | tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys)) | |
|
142 | ||
|
143 | def test_prompts(self): | |
|
144 | """Test that prompts correctly generate after %run""" | |
|
145 | self.run_tmpfile() | |
|
146 | _ip = get_ipython() | |
|
147 | p2 = str(_ip.outputcache.prompt2).strip() | |
|
148 | nt.assert_equals(p2[:3], '...') | |
|
149 | ||
|
150 | ||
|
151 | class TestMagicRunSimple(TempFileMixin): | |
|
152 | ||
|
153 | def test_simpledef(self): | |
|
154 | """Test that simple class definitions work.""" | |
|
155 | src = ("class foo: pass\n" | |
|
156 | "def f(): return foo()") | |
|
157 | self.mktmp(src) | |
|
158 | _ip.magic('run "%s"' % self.fname) | |
|
159 | _ip.runlines('t = isinstance(f(), foo)') | |
|
160 | nt.assert_true(_ip.user_ns['t']) | |
|
161 | ||
|
162 | def test_obj_del(self): | |
|
163 | """Test that object's __del__ methods are called on exit.""" | |
|
164 | ||
|
165 | # This test is known to fail on win32. | |
|
166 | # See ticket https://bugs.launchpad.net/bugs/366334 | |
|
167 | src = ("class A(object):\n" | |
|
168 | " def __del__(self):\n" | |
|
169 | " print 'object A deleted'\n" | |
|
170 | "a = A()\n") | |
|
171 | self.mktmp(src) | |
|
172 | tt.ipexec_validate(self.fname, 'object A deleted') | |
|
173 | ||
|
174 | def test_tclass(self): | |
|
175 | mydir = os.path.dirname(__file__) | |
|
176 | tc = os.path.join(mydir, 'tclass') | |
|
177 | src = ("%%run '%s' C-first\n" | |
|
178 | "%%run '%s' C-second\n") % (tc, tc) | |
|
179 | self.mktmp(src, '.ipy') | |
|
180 | out = """\ | |
|
181 | ARGV 1-: ['C-first'] | |
|
182 | ARGV 1-: ['C-second'] | |
|
183 | tclass.py: deleting object: C-first | |
|
184 | """ | |
|
185 | tt.ipexec_validate(self.fname, out) |
@@ -3506,6 +3506,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3506 | 3506 | """Reload an IPython extension by its module name.""" |
|
3507 | 3507 | self.reload_extension(module_str) |
|
3508 | 3508 | |
|
3509 | @testdec.skip_doctest | |
|
3509 | 3510 | def magic_install_profiles(self, s): |
|
3510 | 3511 | """Install the default IPython profiles into the .ipython dir. |
|
3511 | 3512 |
@@ -416,7 +416,7 b' class PrefilterManager(Component):' | |||
|
416 | 416 | # print "prefiltered line: %r" % prefiltered |
|
417 | 417 | return prefiltered |
|
418 | 418 | |
|
419 | def prefilter_lines(self, lines, continue_prompt): | |
|
419 | def prefilter_lines(self, lines, continue_prompt=False): | |
|
420 | 420 | """Prefilter multiple input lines of text. |
|
421 | 421 | |
|
422 | 422 | This is the main entry point for prefiltering multiple lines of |
@@ -1,16 +1,12 b'' | |||
|
1 | """Simple script to instantiate a class for testing %run""" | |
|
1 | """Simple script to be run *twice*, to check reference counting bugs. | |
|
2 | 2 |
|
|
3 | import sys | |
|
4 | ||
|
5 | # An external test will check that calls to f() work after %run | |
|
6 | class foo: pass | |
|
3 | See test_run for details.""" | |
|
7 | 4 | |
|
8 | def f(): | |
|
9 | return foo() | |
|
5 | import sys | |
|
10 | 6 | |
|
11 |
# We |
|
|
12 |
# |
|
|
13 |
# a |
|
|
7 | # We want to ensure that while objects remain available for immediate access, | |
|
8 | # objects from *previous* runs of the same script get collected, to avoid | |
|
9 | # accumulating massive amounts of old references. | |
|
14 | 10 | class C(object): |
|
15 | 11 | def __init__(self,name): |
|
16 | 12 | self.name = name |
@@ -18,6 +14,7 b' class C(object):' | |||
|
18 | 14 | def __del__(self): |
|
19 | 15 | print 'tclass.py: deleting object:',self.name |
|
20 | 16 | |
|
17 | ||
|
21 | 18 | try: |
|
22 | 19 | name = sys.argv[1] |
|
23 | 20 | except IndexError: |
@@ -25,3 +22,8 b' except IndexError:' | |||
|
25 | 22 | else: |
|
26 | 23 | if name.startswith('C'): |
|
27 | 24 | c = C(name) |
|
25 | ||
|
26 | #print >> sys.stderr, "ARGV:", sys.argv # dbg | |
|
27 | # This print statement is NOT debugging, we're making the check on a completely | |
|
28 | # separate process so we verify by capturing stdout. | |
|
29 | print 'ARGV 1-:', sys.argv[1:] |
@@ -2,21 +2,31 b'' | |||
|
2 | 2 | |
|
3 | 3 | Needs to be run by nose (to make ipython session available). |
|
4 | 4 | """ |
|
5 | from __future__ import absolute_import | |
|
5 | 6 | |
|
7 | #----------------------------------------------------------------------------- | |
|
8 | # Imports | |
|
9 | #----------------------------------------------------------------------------- | |
|
10 | ||
|
11 | # stdlib | |
|
6 | 12 | import os |
|
7 | 13 | import sys |
|
8 | 14 | import tempfile |
|
9 | 15 | import types |
|
10 | 16 | from cStringIO import StringIO |
|
11 | 17 | |
|
18 | # third-party | |
|
12 | 19 | import nose.tools as nt |
|
13 | 20 | |
|
21 | # our own | |
|
22 | from IPython.utils import genutils | |
|
14 | 23 | from IPython.utils.platutils import find_cmd, get_long_path_name |
|
15 | 24 | from IPython.testing import decorators as dec |
|
16 | 25 | from IPython.testing import tools as tt |
|
17 | 26 | |
|
18 | 27 | #----------------------------------------------------------------------------- |
|
19 | 28 | # Test functions begin |
|
29 | #----------------------------------------------------------------------------- | |
|
20 | 30 | |
|
21 | 31 | def test_rehashx(): |
|
22 | 32 | # clear up everything |
@@ -63,17 +73,6 b' def doctest_hist_r():' | |||
|
63 | 73 | hist -n -r 2 # random |
|
64 | 74 | """ |
|
65 | 75 | |
|
66 | # This test is known to fail on win32. | |
|
67 | # See ticket https://bugs.launchpad.net/bugs/366334 | |
|
68 | def test_obj_del(): | |
|
69 | _ip = get_ipython() | |
|
70 | """Test that object's __del__ methods are called on exit.""" | |
|
71 | test_dir = os.path.dirname(__file__) | |
|
72 | del_file = os.path.join(test_dir,'obj_del.py') | |
|
73 | ipython_cmd = find_cmd('ipython') | |
|
74 | out = _ip.getoutput('%s %s' % (ipython_cmd, del_file)) | |
|
75 | nt.assert_equals(out,'obj_del.py: object A deleted') | |
|
76 | ||
|
77 | 76 | |
|
78 | 77 | def test_shist(): |
|
79 | 78 | # Simple tests of ShadowHist class - test generator. |
@@ -113,161 +112,6 b' def test_numpy_clear_array_undec():' | |||
|
113 | 112 | yield (nt.assert_false, 'a' in _ip.user_ns) |
|
114 | 113 | |
|
115 | 114 | |
|
116 | @dec.skip() | |
|
117 | def test_fail_dec(*a,**k): | |
|
118 | yield nt.assert_true, False | |
|
119 | ||
|
120 | @dec.skip('This one shouldn not run') | |
|
121 | def test_fail_dec2(*a,**k): | |
|
122 | yield nt.assert_true, False | |
|
123 | ||
|
124 | @dec.skipknownfailure | |
|
125 | def test_fail_dec3(*a,**k): | |
|
126 | yield nt.assert_true, False | |
|
127 | ||
|
128 | ||
|
129 | def doctest_refbug(): | |
|
130 | """Very nasty problem with references held by multiple runs of a script. | |
|
131 | See: https://bugs.launchpad.net/ipython/+bug/269966 | |
|
132 | ||
|
133 | In [1]: _ip.clear_main_mod_cache() | |
|
134 | ||
|
135 | In [2]: run refbug | |
|
136 | ||
|
137 | In [3]: call_f() | |
|
138 | lowercased: hello | |
|
139 | ||
|
140 | In [4]: run refbug | |
|
141 | ||
|
142 | In [5]: call_f() | |
|
143 | lowercased: hello | |
|
144 | lowercased: hello | |
|
145 | """ | |
|
146 | ||
|
147 | #----------------------------------------------------------------------------- | |
|
148 | # Tests for %run | |
|
149 | #----------------------------------------------------------------------------- | |
|
150 | ||
|
151 | # %run is critical enough that it's a good idea to have a solid collection of | |
|
152 | # tests for it, some as doctests and some as normal tests. | |
|
153 | ||
|
154 | def doctest_run_ns(): | |
|
155 | """Classes declared %run scripts must be instantiable afterwards. | |
|
156 | ||
|
157 | In [11]: run tclass foo | |
|
158 | ||
|
159 | In [12]: isinstance(f(),foo) | |
|
160 | Out[12]: True | |
|
161 | """ | |
|
162 | ||
|
163 | ||
|
164 | def doctest_run_ns2(): | |
|
165 | """Classes declared %run scripts must be instantiable afterwards. | |
|
166 | ||
|
167 | In [4]: run tclass C-first_pass | |
|
168 | ||
|
169 | In [5]: run tclass C-second_pass | |
|
170 | tclass.py: deleting object: C-first_pass | |
|
171 | """ | |
|
172 | ||
|
173 | def doctest_run_builtins(): | |
|
174 | """Check that %run doesn't damage __builtins__ via a doctest. | |
|
175 | ||
|
176 | This is similar to the test_run_builtins, but I want *both* forms of the | |
|
177 | test to catch any possible glitches in our testing machinery, since that | |
|
178 | modifies %run somewhat. So for this, we have both a normal test (below) | |
|
179 | and a doctest (this one). | |
|
180 | ||
|
181 | In [1]: import tempfile | |
|
182 | ||
|
183 | In [2]: bid1 = id(__builtins__) | |
|
184 | ||
|
185 | In [3]: fname = tempfile.mkstemp()[1] | |
|
186 | ||
|
187 | In [3]: f = open(fname,'w') | |
|
188 | ||
|
189 | In [4]: f.write('pass\\n') | |
|
190 | ||
|
191 | In [5]: f.flush() | |
|
192 | ||
|
193 | In [6]: print type(__builtins__) | |
|
194 | <type 'module'> | |
|
195 | ||
|
196 | In [7]: %run "$fname" | |
|
197 | ||
|
198 | In [7]: f.close() | |
|
199 | ||
|
200 | In [8]: bid2 = id(__builtins__) | |
|
201 | ||
|
202 | In [9]: print type(__builtins__) | |
|
203 | <type 'module'> | |
|
204 | ||
|
205 | In [10]: bid1 == bid2 | |
|
206 | Out[10]: True | |
|
207 | ||
|
208 | In [12]: try: | |
|
209 | ....: os.unlink(fname) | |
|
210 | ....: except: | |
|
211 | ....: pass | |
|
212 | ....: | |
|
213 | """ | |
|
214 | ||
|
215 | # For some tests, it will be handy to organize them in a class with a common | |
|
216 | # setup that makes a temp file | |
|
217 | ||
|
218 | class TestMagicRun(object): | |
|
219 | ||
|
220 | def setup(self): | |
|
221 | """Make a valid python temp file.""" | |
|
222 | fname = tempfile.mkstemp('.py')[1] | |
|
223 | f = open(fname,'w') | |
|
224 | f.write('pass\n') | |
|
225 | f.flush() | |
|
226 | self.tmpfile = f | |
|
227 | self.fname = fname | |
|
228 | ||
|
229 | def run_tmpfile(self): | |
|
230 | _ip = get_ipython() | |
|
231 | # This fails on Windows if self.tmpfile.name has spaces or "~" in it. | |
|
232 | # See below and ticket https://bugs.launchpad.net/bugs/366353 | |
|
233 | _ip.magic('run "%s"' % self.fname) | |
|
234 | ||
|
235 | def test_builtins_id(self): | |
|
236 | """Check that %run doesn't damage __builtins__ """ | |
|
237 | _ip = get_ipython() | |
|
238 | # Test that the id of __builtins__ is not modified by %run | |
|
239 | bid1 = id(_ip.user_ns['__builtins__']) | |
|
240 | self.run_tmpfile() | |
|
241 | bid2 = id(_ip.user_ns['__builtins__']) | |
|
242 | tt.assert_equals(bid1, bid2) | |
|
243 | ||
|
244 | def test_builtins_type(self): | |
|
245 | """Check that the type of __builtins__ doesn't change with %run. | |
|
246 | ||
|
247 | However, the above could pass if __builtins__ was already modified to | |
|
248 | be a dict (it should be a module) by a previous use of %run. So we | |
|
249 | also check explicitly that it really is a module: | |
|
250 | """ | |
|
251 | _ip = get_ipython() | |
|
252 | self.run_tmpfile() | |
|
253 | tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys)) | |
|
254 | ||
|
255 | def test_prompts(self): | |
|
256 | """Test that prompts correctly generate after %run""" | |
|
257 | self.run_tmpfile() | |
|
258 | _ip = get_ipython() | |
|
259 | p2 = str(_ip.outputcache.prompt2).strip() | |
|
260 | nt.assert_equals(p2[:3], '...') | |
|
261 | ||
|
262 | def teardown(self): | |
|
263 | self.tmpfile.close() | |
|
264 | try: | |
|
265 | os.unlink(self.fname) | |
|
266 | except: | |
|
267 | # On Windows, even though we close the file, we still can't delete | |
|
268 | # it. I have no clue why | |
|
269 | pass | |
|
270 | ||
|
271 | 115 | # Multiple tests for clipboard pasting |
|
272 | 116 | @dec.parametric |
|
273 | 117 | def test_paste(): |
@@ -64,6 +64,9 b" if sys.version[0]=='2':" | |||
|
64 | 64 | else: |
|
65 | 65 | from _paramtestpy3 import parametric |
|
66 | 66 | |
|
67 | # Expose the unittest-driven decorators | |
|
68 | from ipunittest import ipdoctest, ipdocstring | |
|
69 | ||
|
67 | 70 | # Grab the numpy-specific decorators which we keep in a file that we |
|
68 | 71 | # occasionally update from upstream: decorators.py is a copy of |
|
69 | 72 | # numpy.testing.decorators, we expose all of it here. |
@@ -16,6 +16,8 b' For now, this script requires that both nose and twisted are installed. This' | |||
|
16 | 16 | will change in the future. |
|
17 | 17 | """ |
|
18 | 18 | |
|
19 | from __future__ import absolute_import | |
|
20 | ||
|
19 | 21 | #----------------------------------------------------------------------------- |
|
20 | 22 | # Module imports |
|
21 | 23 | #----------------------------------------------------------------------------- |
@@ -34,6 +36,8 b' from nose.core import TestProgram' | |||
|
34 | 36 | |
|
35 | 37 | from IPython.utils import genutils |
|
36 | 38 | from IPython.utils.platutils import find_cmd, FindCmdError |
|
39 | from . import globalipapp | |
|
40 | from .plugin.ipdoctest import IPythonDoctest | |
|
37 | 41 | |
|
38 | 42 | pjoin = path.join |
|
39 | 43 | |
@@ -76,7 +80,11 b' def make_exclude():' | |||
|
76 | 80 | # cause testing problems. We should strive to minimize the number of |
|
77 | 81 | # skipped modules, since this means untested code. As the testing |
|
78 | 82 | # machinery solidifies, this list should eventually become empty. |
|
79 | EXCLUDE = [pjoin('IPython', 'external'), | |
|
83 | ||
|
84 | # Note that these exclusions only mean that the docstrings are not analyzed | |
|
85 | # for examples to be run as tests, if there are other test functions in | |
|
86 | # those modules, they do get run. | |
|
87 | exclusions = [pjoin('IPython', 'external'), | |
|
80 | 88 | pjoin('IPython', 'frontend', 'process', 'winprocess.py'), |
|
81 | 89 | pjoin('IPython_doctest_plugin'), |
|
82 | 90 | pjoin('IPython', 'quarantine'), |
@@ -88,58 +96,58 b' def make_exclude():' | |||
|
88 | 96 | ] |
|
89 | 97 | |
|
90 | 98 | if not have_wx: |
|
91 |
|
|
|
92 |
|
|
|
93 |
|
|
|
99 | exclusions.append(pjoin('IPython', 'gui')) | |
|
100 | exclusions.append(pjoin('IPython', 'frontend', 'wx')) | |
|
101 | exclusions.append(pjoin('IPython', 'lib', 'inputhookwx')) | |
|
94 | 102 | |
|
95 | 103 | if not have_gtk or not have_gobject: |
|
96 |
|
|
|
104 | exclusions.append(pjoin('IPython', 'lib', 'inputhookgtk')) | |
|
97 | 105 | |
|
98 | 106 | if not have_wx_aui: |
|
99 |
|
|
|
107 | exclusions.append(pjoin('IPython', 'gui', 'wx', 'wxIPython')) | |
|
100 | 108 | |
|
101 | 109 | if not have_objc: |
|
102 |
|
|
|
110 | exclusions.append(pjoin('IPython', 'frontend', 'cocoa')) | |
|
103 | 111 | |
|
104 | 112 | if not sys.platform == 'win32': |
|
105 |
|
|
|
113 | exclusions.append(pjoin('IPython', 'utils', 'platutils_win32')) | |
|
106 | 114 | |
|
107 | 115 | # These have to be skipped on win32 because the use echo, rm, cd, etc. |
|
108 | 116 | # See ticket https://bugs.launchpad.net/bugs/366982 |
|
109 | 117 | if sys.platform == 'win32': |
|
110 |
|
|
|
111 |
|
|
|
118 | exclusions.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip')) | |
|
119 | exclusions.append(pjoin('IPython', 'testing', 'plugin', 'dtexample')) | |
|
112 | 120 | |
|
113 | 121 | if not os.name == 'posix': |
|
114 |
|
|
|
122 | exclusions.append(pjoin('IPython', 'utils', 'platutils_posix')) | |
|
115 | 123 | |
|
116 | 124 | if not have_pexpect: |
|
117 |
|
|
|
125 | exclusions.append(pjoin('IPython', 'scripts', 'irunner')) | |
|
118 | 126 | |
|
119 | 127 | # This is scary. We still have things in frontend and testing that |
|
120 | 128 | # are being tested by nose that use twisted. We need to rethink |
|
121 | 129 | # how we are isolating dependencies in testing. |
|
122 | 130 | if not (have_twisted and have_zi and have_foolscap): |
|
123 |
|
|
|
124 |
|
|
|
125 |
|
|
|
126 |
|
|
|
127 |
|
|
|
131 | exclusions.append(pjoin('IPython', 'frontend', 'asyncfrontendbase')) | |
|
132 | exclusions.append(pjoin('IPython', 'frontend', 'prefilterfrontend')) | |
|
133 | exclusions.append(pjoin('IPython', 'frontend', 'frontendbase')) | |
|
134 | exclusions.append(pjoin('IPython', 'frontend', 'linefrontendbase')) | |
|
135 | exclusions.append(pjoin('IPython', 'frontend', 'tests', | |
|
128 | 136 | 'test_linefrontend')) |
|
129 |
|
|
|
137 | exclusions.append(pjoin('IPython', 'frontend', 'tests', | |
|
130 | 138 | 'test_frontendbase')) |
|
131 |
|
|
|
139 | exclusions.append(pjoin('IPython', 'frontend', 'tests', | |
|
132 | 140 | 'test_prefilterfrontend')) |
|
133 |
|
|
|
141 | exclusions.append(pjoin('IPython', 'frontend', 'tests', | |
|
134 | 142 | 'test_asyncfrontendbase')), |
|
135 |
|
|
|
136 |
|
|
|
143 | exclusions.append(pjoin('IPython', 'testing', 'parametric')) | |
|
144 | exclusions.append(pjoin('IPython', 'testing', 'util')) | |
|
137 | 145 | |
|
138 | 146 | # This is needed for the reg-exp to match on win32 in the ipdoctest plugin. |
|
139 | 147 | if sys.platform == 'win32': |
|
140 |
|
|
|
148 | exclusions = [s.replace('\\','\\\\') for s in exclusions] | |
|
141 | 149 | |
|
142 | return EXCLUDE | |
|
150 | return exclusions | |
|
143 | 151 | |
|
144 | 152 | |
|
145 | 153 | #----------------------------------------------------------------------------- |
@@ -163,16 +171,16 b' class IPTester(object):' | |||
|
163 | 171 | if runner == 'iptest': |
|
164 | 172 | # Find our own 'iptest' script OS-level entry point |
|
165 | 173 | try: |
|
166 | iptest_path = find_cmd('iptest') | |
|
174 | iptest_path = os.path.abspath(find_cmd('iptest')) | |
|
167 | 175 | except FindCmdError: |
|
168 | 176 | # Script not installed (may be the case for testing situations |
|
169 | 177 | # that are running from a source tree only), pull from internal |
|
170 | 178 | # path: |
|
171 | 179 | iptest_path = pjoin(genutils.get_ipython_package_dir(), |
|
172 | 180 | 'scripts','iptest') |
|
173 | self.runner = [iptest_path,'-v'] | |
|
181 | self.runner = ['python', iptest_path, '-v'] | |
|
174 | 182 | else: |
|
175 | self.runner = [find_cmd('trial')] | |
|
183 | self.runner = ['python', os.path.abspath(find_cmd('trial'))] | |
|
176 | 184 | if params is None: |
|
177 | 185 | params = [] |
|
178 | 186 | if isinstance(params,str): |
@@ -238,11 +246,13 b' def make_runners():' | |||
|
238 | 246 | nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib', |
|
239 | 247 | 'scripts', 'testing', 'utils'] |
|
240 | 248 | trial_packages = ['kernel'] |
|
241 | #trial_packages = [] # dbg | |
|
242 | 249 | |
|
243 | 250 | if have_wx: |
|
244 | 251 | nose_packages.append('gui') |
|
245 | 252 | |
|
253 | #nose_packages = ['core'] # dbg | |
|
254 | #trial_packages = [] # dbg | |
|
255 | ||
|
246 | 256 | nose_packages = ['IPython.%s' % m for m in nose_packages ] |
|
247 | 257 | trial_packages = ['IPython.%s' % m for m in trial_packages ] |
|
248 | 258 | |
@@ -268,15 +278,14 b' def run_iptest():' | |||
|
268 | 278 | warnings.filterwarnings('ignore', |
|
269 | 279 | 'This will be removed soon. Use IPython.testing.util instead') |
|
270 | 280 | |
|
271 | argv = sys.argv + [ | |
|
272 |
# Loading ipdoctest causes problems with Twisted |
|
|
273 | # I am removing this as a temporary fix to get the | |
|
274 | # test suite back into working shape. Our nose | |
|
275 | # plugin needs to be gone through with a fine | |
|
276 | # toothed comb to find what is causing the problem. | |
|
277 |
|
|
|
278 | # '--ipdoctest-tests','--ipdoctest-extension=txt', | |
|
279 | # '--detailed-errors', | |
|
281 | argv = sys.argv + [ '--detailed-errors', | |
|
282 | # Loading ipdoctest causes problems with Twisted, but | |
|
283 | # our test suite runner now separates things and runs | |
|
284 | # all Twisted tests with trial. | |
|
285 | '--with-ipdoctest', | |
|
286 | '--ipdoctest-tests','--ipdoctest-extension=txt', | |
|
287 | ||
|
288 | #'-x','-s', # dbg | |
|
280 | 289 | |
|
281 | 290 | # We add --exe because of setuptools' imbecility (it |
|
282 | 291 | # blindly does chmod +x on ALL files). Nose does the |
@@ -300,17 +309,18 b' def run_iptest():' | |||
|
300 | 309 | if not has_tests: |
|
301 | 310 | argv.append('IPython') |
|
302 | 311 | |
|
303 | # Construct list of plugins, omitting the existing doctest plugin, which | |
|
304 | # ours replaces (and extends). | |
|
305 | EXCLUDE = make_exclude() | |
|
306 | plugins = [] | |
|
307 | # plugins = [IPythonDoctest(EXCLUDE)] | |
|
312 | ## # Construct list of plugins, omitting the existing doctest plugin, which | |
|
313 | ## # ours replaces (and extends). | |
|
314 | plugins = [IPythonDoctest(make_exclude())] | |
|
308 | 315 | for p in nose.plugins.builtin.plugins: |
|
309 | 316 | plug = p() |
|
310 | 317 | if plug.name == 'doctest': |
|
311 | 318 | continue |
|
312 | 319 | plugins.append(plug) |
|
313 | 320 | |
|
321 | # We need a global ipython running in this process | |
|
322 | globalipapp.start_ipython() | |
|
323 | # Now nose can run | |
|
314 | 324 | TestProgram(argv=argv,plugins=plugins) |
|
315 | 325 | |
|
316 | 326 |
@@ -22,6 +22,8 b' Authors' | |||
|
22 | 22 | - Fernando Perez <Fernando.Perez@berkeley.edu> |
|
23 | 23 | """ |
|
24 | 24 | |
|
25 | from __future__ import absolute_import | |
|
26 | ||
|
25 | 27 | #----------------------------------------------------------------------------- |
|
26 | 28 | # Copyright (C) 2009 The IPython Development Team |
|
27 | 29 | # |
@@ -40,14 +42,16 b' import sys' | |||
|
40 | 42 | import unittest |
|
41 | 43 | from doctest import DocTestFinder, DocTestRunner, TestResults |
|
42 | 44 | |
|
43 | # Our own | |
|
44 | import nosepatch | |
|
45 | # Our own, a nose monkeypatch | |
|
46 | from . import nosepatch | |
|
45 | 47 | |
|
46 | 48 | # We already have python3-compliant code for parametric tests |
|
47 | 49 | if sys.version[0]=='2': |
|
48 | from _paramtestpy2 import ParametricTestCase | |
|
50 | from ._paramtestpy2 import ParametricTestCase | |
|
49 | 51 | else: |
|
50 | from _paramtestpy3 import ParametricTestCase | |
|
52 | from ._paramtestpy3 import ParametricTestCase | |
|
53 | ||
|
54 | from . import globalipapp | |
|
51 | 55 | |
|
52 | 56 | #----------------------------------------------------------------------------- |
|
53 | 57 | # Classes and functions |
@@ -68,9 +72,13 b' class IPython2PythonConverter(object):' | |||
|
68 | 72 | implementation, but for now it only does prompt convertion.""" |
|
69 | 73 | |
|
70 | 74 | def __init__(self): |
|
71 | self.ps1 = re.compile(r'In\ \[\d+\]: ') | |
|
72 | self.ps2 = re.compile(r'\ \ \ \.\.\.+: ') | |
|
73 | self.out = re.compile(r'Out\[\d+\]: \s*?\n?') | |
|
75 | self.rps1 = re.compile(r'In\ \[\d+\]: ') | |
|
76 | self.rps2 = re.compile(r'\ \ \ \.\.\.+: ') | |
|
77 | self.rout = re.compile(r'Out\[\d+\]: \s*?\n?') | |
|
78 | self.pyps1 = '>>> ' | |
|
79 | self.pyps2 = '... ' | |
|
80 | self.rpyps1 = re.compile ('(\s*%s)(.*)$' % self.pyps1) | |
|
81 | self.rpyps2 = re.compile ('(\s*%s)(.*)$' % self.pyps2) | |
|
74 | 82 | |
|
75 | 83 | def __call__(self, ds): |
|
76 | 84 | """Convert IPython prompts to python ones in a string.""" |
@@ -79,10 +87,34 b' class IPython2PythonConverter(object):' | |||
|
79 | 87 | pyout = '' |
|
80 | 88 | |
|
81 | 89 | dnew = ds |
|
82 | dnew = self.ps1.sub(pyps1, dnew) | |
|
83 | dnew = self.ps2.sub(pyps2, dnew) | |
|
84 | dnew = self.out.sub(pyout, dnew) | |
|
85 | return dnew | |
|
90 | dnew = self.rps1.sub(pyps1, dnew) | |
|
91 | dnew = self.rps2.sub(pyps2, dnew) | |
|
92 | dnew = self.rout.sub(pyout, dnew) | |
|
93 | ip = globalipapp.get_ipython() | |
|
94 | ||
|
95 | # Convert input IPython source into valid Python. | |
|
96 | out = [] | |
|
97 | newline = out.append | |
|
98 | for line in dnew.splitlines(): | |
|
99 | ||
|
100 | mps1 = self.rpyps1.match(line) | |
|
101 | if mps1 is not None: | |
|
102 | prompt, text = mps1.groups() | |
|
103 | newline(prompt+ip.prefilter(text, False)) | |
|
104 | continue | |
|
105 | ||
|
106 | mps2 = self.rpyps2.match(line) | |
|
107 | if mps2 is not None: | |
|
108 | prompt, text = mps2.groups() | |
|
109 | newline(prompt+ip.prefilter(text, True)) | |
|
110 | continue | |
|
111 | ||
|
112 | newline(line) | |
|
113 | newline('') # ensure a closing newline, needed by doctest | |
|
114 | #print "PYSRC:", '\n'.join(out) # dbg | |
|
115 | return '\n'.join(out) | |
|
116 | ||
|
117 | #return dnew | |
|
86 | 118 | |
|
87 | 119 | |
|
88 | 120 | class Doc2UnitTester(object): |
@@ -49,183 +49,14 b' from nose.util import anyp, getpackage, test_address, resolve_name, tolist' | |||
|
49 | 49 | |
|
50 | 50 | #----------------------------------------------------------------------------- |
|
51 | 51 | # Module globals and other constants |
|
52 | #----------------------------------------------------------------------------- | |
|
52 | 53 | |
|
53 | 54 | log = logging.getLogger(__name__) |
|
54 | 55 | |
|
55 | ########################################################################### | |
|
56 | # *** HACK *** | |
|
57 | # We must start our own ipython object and heavily muck with it so that all the | |
|
58 | # modifications IPython makes to system behavior don't send the doctest | |
|
59 | # machinery into a fit. This code should be considered a gross hack, but it | |
|
60 | # gets the job done. | |
|
61 | ||
|
62 | def default_argv(): | |
|
63 | """Return a valid default argv for creating testing instances of ipython""" | |
|
64 | ||
|
65 | # Get the install directory for the user configuration and tell ipython to | |
|
66 | # use the default profile from there. | |
|
67 | from IPython.config import default | |
|
68 | ipcdir = os.path.dirname(default.__file__) | |
|
69 | ipconf = os.path.join(ipcdir,'ipython_config.py') | |
|
70 | #print 'conf:',ipconf # dbg | |
|
71 | return ['--colors=NoColor', '--no-term-title','--no-banner', | |
|
72 | '--config-file=%s' % ipconf] | |
|
73 | ||
|
74 | ||
|
75 | # Hack to modify the %run command so we can sync the user's namespace with the | |
|
76 | # test globals. Once we move over to a clean magic system, this will be done | |
|
77 | # with much less ugliness. | |
|
78 | ||
|
79 | class py_file_finder(object): | |
|
80 | def __init__(self,test_filename): | |
|
81 | self.test_filename = test_filename | |
|
82 | ||
|
83 | def __call__(self,name): | |
|
84 | from IPython.utils.genutils import get_py_filename | |
|
85 | try: | |
|
86 | return get_py_filename(name) | |
|
87 | except IOError: | |
|
88 | test_dir = os.path.dirname(self.test_filename) | |
|
89 | new_path = os.path.join(test_dir,name) | |
|
90 | return get_py_filename(new_path) | |
|
91 | ||
|
92 | ||
|
93 | def _run_ns_sync(self,arg_s,runner=None): | |
|
94 | """Modified version of %run that syncs testing namespaces. | |
|
95 | ||
|
96 | This is strictly needed for running doctests that call %run. | |
|
97 | """ | |
|
98 | ||
|
99 | # When tests call %run directly (not via doctest) these function attributes | |
|
100 | # are not set | |
|
101 | try: | |
|
102 | fname = _run_ns_sync.test_filename | |
|
103 | except AttributeError: | |
|
104 | fname = arg_s | |
|
105 | ||
|
106 | finder = py_file_finder(fname) | |
|
107 | out = _ip.magic_run_ori(arg_s,runner,finder) | |
|
108 | ||
|
109 | # Simliarly, there is no test_globs when a test is NOT a doctest | |
|
110 | if hasattr(_run_ns_sync,'test_globs'): | |
|
111 | _run_ns_sync.test_globs.update(_ip.user_ns) | |
|
112 | return out | |
|
113 | ||
|
114 | ||
|
115 | class ipnsdict(dict): | |
|
116 | """A special subclass of dict for use as an IPython namespace in doctests. | |
|
117 | ||
|
118 | This subclass adds a simple checkpointing capability so that when testing | |
|
119 | machinery clears it (we use it as the test execution context), it doesn't | |
|
120 | get completely destroyed. | |
|
121 | """ | |
|
122 | ||
|
123 | def __init__(self,*a): | |
|
124 | dict.__init__(self,*a) | |
|
125 | self._savedict = {} | |
|
126 | ||
|
127 | def clear(self): | |
|
128 | dict.clear(self) | |
|
129 | self.update(self._savedict) | |
|
130 | ||
|
131 | def _checkpoint(self): | |
|
132 | self._savedict.clear() | |
|
133 | self._savedict.update(self) | |
|
134 | ||
|
135 | def update(self,other): | |
|
136 | self._checkpoint() | |
|
137 | dict.update(self,other) | |
|
138 | ||
|
139 | # If '_' is in the namespace, python won't set it when executing code, | |
|
140 | # and we have examples that test it. So we ensure that the namespace | |
|
141 | # is always 'clean' of it before it's used for test code execution. | |
|
142 | self.pop('_',None) | |
|
143 | ||
|
144 | # The builtins namespace must *always* be the real __builtin__ module, | |
|
145 | # else weird stuff happens. The main ipython code does have provisions | |
|
146 | # to ensure this after %run, but since in this class we do some | |
|
147 | # aggressive low-level cleaning of the execution namespace, we need to | |
|
148 | # correct for that ourselves, to ensure consitency with the 'real' | |
|
149 | # ipython. | |
|
150 | self['__builtins__'] = __builtin__ | |
|
151 | ||
|
152 | ||
|
153 | def start_ipython(): | |
|
154 | """Start a global IPython shell, which we need for IPython-specific syntax. | |
|
155 | """ | |
|
156 | ||
|
157 | # This function should only ever run once! | |
|
158 | if hasattr(start_ipython,'already_called'): | |
|
159 | return | |
|
160 | start_ipython.already_called = True | |
|
161 | ||
|
162 | # Ok, first time we're called, go ahead | |
|
163 | import new | |
|
164 | ||
|
165 | import IPython | |
|
166 | from IPython.core import ipapp, iplib | |
|
167 | ||
|
168 | def xsys(cmd): | |
|
169 | """Execute a command and print its output. | |
|
170 | ||
|
171 | This is just a convenience function to replace the IPython system call | |
|
172 | with one that is more doctest-friendly. | |
|
173 | """ | |
|
174 | cmd = _ip.var_expand(cmd,depth=1) | |
|
175 | sys.stdout.write(commands.getoutput(cmd)) | |
|
176 | sys.stdout.flush() | |
|
177 | ||
|
178 | # Store certain global objects that IPython modifies | |
|
179 | _displayhook = sys.displayhook | |
|
180 | _excepthook = sys.excepthook | |
|
181 | _main = sys.modules.get('__main__') | |
|
182 | ||
|
183 | argv = default_argv() | |
|
184 | ||
|
185 | # Start IPython instance. We customize it to start with minimal frills. | |
|
186 | user_ns,global_ns = iplib.make_user_namespaces(ipnsdict(),{}) | |
|
187 | ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns) | |
|
188 | ip.initialize() | |
|
189 | ip.shell.builtin_trap.set() | |
|
190 | ||
|
191 | # Deactivate the various python system hooks added by ipython for | |
|
192 | # interactive convenience so we don't confuse the doctest system | |
|
193 | sys.modules['__main__'] = _main | |
|
194 | sys.displayhook = _displayhook | |
|
195 | sys.excepthook = _excepthook | |
|
196 | ||
|
197 | # So that ipython magics and aliases can be doctested (they work by making | |
|
198 | # a call into a global _ip object) | |
|
199 | __builtin__._ip = ip.shell | |
|
200 | ||
|
201 | # Modify the IPython system call with one that uses getoutput, so that we | |
|
202 | # can capture subcommands and print them to Python's stdout, otherwise the | |
|
203 | # doctest machinery would miss them. | |
|
204 | ip.shell.system = xsys | |
|
205 | ||
|
206 | # Also patch our %run function in. | |
|
207 | im = new.instancemethod(_run_ns_sync,_ip, _ip.__class__) | |
|
208 | ip.shell.magic_run_ori = _ip.magic_run | |
|
209 | ip.shell.magic_run = im | |
|
210 | ||
|
211 | # XXX - For some very bizarre reason, the loading of %history by default is | |
|
212 | # failing. This needs to be fixed later, but for now at least this ensures | |
|
213 | # that tests that use %hist run to completion. | |
|
214 | from IPython.core import history | |
|
215 | history.init_ipython(ip.shell) | |
|
216 | if not hasattr(ip.shell,'magic_history'): | |
|
217 | raise RuntimeError("Can't load magics, aborting") | |
|
218 | ||
|
219 | ||
|
220 | # The start call MUST be made here. I'm not sure yet why it doesn't work if | |
|
221 | # it is made later, at plugin initialization time, but in all my tests, that's | |
|
222 | # the case. | |
|
223 | start_ipython() | |
|
224 | ||
|
225 | # *** END HACK *** | |
|
226 | ########################################################################### | |
|
227 | 56 | |
|
57 | #----------------------------------------------------------------------------- | |
|
228 | 58 | # Classes and functions |
|
59 | #----------------------------------------------------------------------------- | |
|
229 | 60 | |
|
230 | 61 | def is_extension_module(filename): |
|
231 | 62 | """Return whether the given filename is an extension module. |
@@ -288,7 +119,7 b' class DocTestFinder(doctest.DocTestFinder):' | |||
|
288 | 119 | Find tests for the given object and any contained objects, and |
|
289 | 120 | add them to `tests`. |
|
290 | 121 | """ |
|
291 | ||
|
122 | #print '_find for:', obj, name, module # dbg | |
|
292 | 123 | if hasattr(obj,"skip_doctest"): |
|
293 | 124 | #print 'SKIPPING DOCTEST FOR:',obj # dbg |
|
294 | 125 | obj = DocTestSkip(obj) |
@@ -396,8 +227,9 b' class DocTestCase(doctests.DocTestCase):' | |||
|
396 | 227 | self._dt_runner = runner |
|
397 | 228 | |
|
398 | 229 | |
|
399 |
# Each doctest should remember |
|
|
400 | self._ori_dir = os.getcwd() | |
|
230 | # Each doctest should remember the directory it was loaded from, so | |
|
231 | # things like %run work without too many contortions | |
|
232 | self._ori_dir = os.path.dirname(test.filename) | |
|
401 | 233 | |
|
402 | 234 | # Modified runTest from the default stdlib |
|
403 | 235 | def runTest(self): |
@@ -418,6 +250,7 b' class DocTestCase(doctests.DocTestCase):' | |||
|
418 | 250 | # test was originally created, in case another doctest did a |
|
419 | 251 | # directory change. We'll restore this in the finally clause. |
|
420 | 252 | curdir = os.getcwd() |
|
253 | #print 'runTest in dir:', self._ori_dir # dbg | |
|
421 | 254 | os.chdir(self._ori_dir) |
|
422 | 255 | |
|
423 | 256 | runner.DIVIDER = "-"*70 |
@@ -432,7 +265,7 b' class DocTestCase(doctests.DocTestCase):' | |||
|
432 | 265 | |
|
433 | 266 | def setUp(self): |
|
434 | 267 | """Modified test setup that syncs with ipython namespace""" |
|
435 | ||
|
268 | #print "setUp test", self._dt_test.examples # dbg | |
|
436 | 269 | if isinstance(self._dt_test.examples[0],IPExample): |
|
437 | 270 | # for IPython examples *only*, we swap the globals with the ipython |
|
438 | 271 | # namespace, after updating it with the globals (which doctest |
@@ -731,8 +564,10 b' class IPDocTestRunner(doctest.DocTestRunner,object):' | |||
|
731 | 564 | # attribute. Our new %run will then only make the namespace update |
|
732 | 565 | # when called (rather than unconconditionally updating test.globs here |
|
733 | 566 | # for all examples, most of which won't be calling %run anyway). |
|
734 |
|
|
|
735 |
|
|
|
567 | #_ip._ipdoctest_test_globs = test.globs | |
|
568 | #_ip._ipdoctest_test_filename = test.filename | |
|
569 | ||
|
570 | test.globs.update(_ip.user_ns) | |
|
736 | 571 | |
|
737 | 572 | return super(IPDocTestRunner,self).run(test, |
|
738 | 573 | compileflags,out,clear_globs) |
@@ -846,6 +681,7 b' class ExtensionDoctest(doctests.Doctest):' | |||
|
846 | 681 | |
|
847 | 682 | |
|
848 | 683 | def loadTestsFromFile(self, filename): |
|
684 | #print "ipdoctest - from file", filename # dbg | |
|
849 | 685 | if is_extension_module(filename): |
|
850 | 686 | for t in self.loadTestsFromExtensionModule(filename): |
|
851 | 687 | yield t |
@@ -895,6 +731,7 b' class IPythonDoctest(ExtensionDoctest):' | |||
|
895 | 731 | """Look for doctests in the given object, which will be a |
|
896 | 732 | function, method or class. |
|
897 | 733 | """ |
|
734 | #print 'Plugin analyzing:', obj, parent # dbg | |
|
898 | 735 | # always use whitespace and ellipsis options |
|
899 | 736 | optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS |
|
900 | 737 | |
@@ -909,6 +746,7 b' class IPythonDoctest(ExtensionDoctest):' | |||
|
909 | 746 | checker=self.checker) |
|
910 | 747 | |
|
911 | 748 | def options(self, parser, env=os.environ): |
|
749 | #print "Options for nose plugin:", self.name # dbg | |
|
912 | 750 | Plugin.options(self, parser, env) |
|
913 | 751 | parser.add_option('--ipdoctest-tests', action='store_true', |
|
914 | 752 | dest='ipdoctest_tests', |
@@ -929,6 +767,7 b' class IPythonDoctest(ExtensionDoctest):' | |||
|
929 | 767 | parser.set_defaults(ipdoctest_extension=tolist(env_setting)) |
|
930 | 768 | |
|
931 | 769 | def configure(self, options, config): |
|
770 | #print "Configuring nose plugin:", self.name # dbg | |
|
932 | 771 | Plugin.configure(self, options, config) |
|
933 | 772 | self.doctest_tests = options.ipdoctest_tests |
|
934 | 773 | self.extension = tolist(options.ipdoctest_extension) |
@@ -29,10 +29,11 b' Authors' | |||
|
29 | 29 | import os |
|
30 | 30 | import re |
|
31 | 31 | import sys |
|
32 | import tempfile | |
|
32 | 33 | |
|
33 | 34 | import nose.tools as nt |
|
34 | 35 | |
|
35 | from IPython.utils import genutils | |
|
36 | from IPython.utils import genutils, platutils | |
|
36 | 37 | |
|
37 | 38 | #----------------------------------------------------------------------------- |
|
38 | 39 | # Globals |
@@ -128,5 +129,93 b' def parse_test_output(txt):' | |||
|
128 | 129 | # If the input didn't match any of these forms, assume no error/failures |
|
129 | 130 | return 0, 0 |
|
130 | 131 | |
|
132 | ||
|
131 | 133 | # So nose doesn't think this is a test |
|
132 | 134 | parse_test_output.__test__ = False |
|
135 | ||
|
136 | ||
|
137 | def temp_pyfile(src, ext='.py'): | |
|
138 | """Make a temporary python file, return filename and filehandle. | |
|
139 | ||
|
140 | Parameters | |
|
141 | ---------- | |
|
142 | src : string or list of strings (no need for ending newlines if list) | |
|
143 | Source code to be written to the file. | |
|
144 | ||
|
145 | ext : optional, string | |
|
146 | Extension for the generated file. | |
|
147 | ||
|
148 | Returns | |
|
149 | ------- | |
|
150 | (filename, open filehandle) | |
|
151 | It is the caller's responsibility to close the open file and unlink it. | |
|
152 | """ | |
|
153 | fname = tempfile.mkstemp(ext)[1] | |
|
154 | f = open(fname,'w') | |
|
155 | f.write(src) | |
|
156 | f.flush() | |
|
157 | return fname, f | |
|
158 | ||
|
159 | ||
|
160 | def default_argv(): | |
|
161 | """Return a valid default argv for creating testing instances of ipython""" | |
|
162 | ||
|
163 | # Get the install directory for the user configuration and tell ipython to | |
|
164 | # use the default profile from there. | |
|
165 | from IPython.config import default | |
|
166 | ipcdir = os.path.dirname(default.__file__) | |
|
167 | ipconf = os.path.join(ipcdir,'ipython_config.py') | |
|
168 | #print 'conf:',ipconf # dbg | |
|
169 | return ['--colors=NoColor', '--no-term-title','--no-banner', | |
|
170 | '--config-file=%s' % ipconf, '--autocall=0', '--quick'] | |
|
171 | ||
|
172 | ||
|
173 | def ipexec(fname): | |
|
174 | """Utility to call 'ipython filename'. | |
|
175 | ||
|
176 | Starts IPython witha minimal and safe configuration to make startup as fast | |
|
177 | as possible. | |
|
178 | ||
|
179 | Note that this starts IPython in a subprocess! | |
|
180 | ||
|
181 | Parameters | |
|
182 | ---------- | |
|
183 | fname : str | |
|
184 | Name of file to be executed (should have .py or .ipy extension). | |
|
185 | ||
|
186 | Returns | |
|
187 | ------- | |
|
188 | (stdout, stderr) of ipython subprocess. | |
|
189 | """ | |
|
190 | _ip = get_ipython() | |
|
191 | test_dir = os.path.dirname(__file__) | |
|
192 | full_fname = os.path.join(test_dir, fname) | |
|
193 | ipython_cmd = platutils.find_cmd('ipython') | |
|
194 | cmdargs = ' '.join(default_argv()) | |
|
195 | return genutils.getoutputerror('%s %s' % (ipython_cmd, full_fname)) | |
|
196 | ||
|
197 | ||
|
198 | def ipexec_validate(fname, expected_out, expected_err=None): | |
|
199 | """Utility to call 'ipython filename' and validate output/error. | |
|
200 | ||
|
201 | This function raises an AssertionError if the validation fails. | |
|
202 | ||
|
203 | Note that this starts IPython in a subprocess! | |
|
204 | ||
|
205 | Parameters | |
|
206 | ---------- | |
|
207 | fname : str | |
|
208 | Name of the file to be executed (should have .py or .ipy extension). | |
|
209 | ||
|
210 | expected_out : str | |
|
211 | Expected stdout of the process. | |
|
212 | ||
|
213 | Returns | |
|
214 | ------- | |
|
215 | None | |
|
216 | """ | |
|
217 | ||
|
218 | out, err = ipexec(fname) | |
|
219 | nt.assert_equals(out.strip(), expected_out.strip()) | |
|
220 | if expected_err: | |
|
221 | nt.assert_equals(err.strip(), expected_err.strip()) |
General Comments 0
You need to be logged in to leave comments.
Login now