Show More
@@ -1,512 +1,517 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """Tests for code execution (%run and related), which is particularly tricky. |
|
2 | """Tests for code execution (%run and related), which is particularly tricky. | |
3 |
|
3 | |||
4 | Because of how %run manages namespaces, and the fact that we are trying here to |
|
4 | Because of how %run manages namespaces, and the fact that we are trying here to | |
5 | verify subtle object deletion and reference counting issues, the %run tests |
|
5 | verify subtle object deletion and reference counting issues, the %run tests | |
6 | will be kept in this separate file. This makes it easier to aggregate in one |
|
6 | will be kept in this separate file. This makes it easier to aggregate in one | |
7 | place the tricks needed to handle it; most other magics are much easier to test |
|
7 | place the tricks needed to handle it; most other magics are much easier to test | |
8 | and we do so in a common test_magic file. |
|
8 | and we do so in a common test_magic file. | |
9 | """ |
|
9 | """ | |
10 |
|
10 | |||
11 | # Copyright (c) IPython Development Team. |
|
11 | # Copyright (c) IPython Development Team. | |
12 | # Distributed under the terms of the Modified BSD License. |
|
12 | # Distributed under the terms of the Modified BSD License. | |
13 |
|
13 | |||
14 | from __future__ import absolute_import |
|
14 | from __future__ import absolute_import | |
15 |
|
15 | |||
16 |
|
16 | |||
17 | import functools |
|
17 | import functools | |
18 | import os |
|
18 | import os | |
19 | from os.path import join as pjoin |
|
19 | from os.path import join as pjoin | |
20 | import random |
|
20 | import random | |
21 | import sys |
|
21 | import sys | |
22 | import tempfile |
|
22 | import tempfile | |
23 | import textwrap |
|
23 | import textwrap | |
24 | import unittest |
|
24 | import unittest | |
25 |
|
25 | |||
|
26 | try: | |||
|
27 | from unittest.mock import patch | |||
|
28 | except ImportError: | |||
|
29 | from mock import patch | |||
|
30 | ||||
26 | import nose.tools as nt |
|
31 | import nose.tools as nt | |
27 | from nose import SkipTest |
|
32 | from nose import SkipTest | |
28 |
|
33 | |||
29 | from IPython.testing import decorators as dec |
|
34 | from IPython.testing import decorators as dec | |
30 | from IPython.testing import tools as tt |
|
35 | from IPython.testing import tools as tt | |
31 | from IPython.utils import py3compat |
|
36 | from IPython.utils import py3compat | |
32 | from IPython.utils.io import capture_output |
|
37 | from IPython.utils.io import capture_output | |
33 | from IPython.utils.tempdir import TemporaryDirectory |
|
38 | from IPython.utils.tempdir import TemporaryDirectory | |
34 | from IPython.core import debugger |
|
39 | from IPython.core import debugger | |
35 |
|
40 | |||
36 |
|
41 | |||
37 | def doctest_refbug(): |
|
42 | def doctest_refbug(): | |
38 | """Very nasty problem with references held by multiple runs of a script. |
|
43 | """Very nasty problem with references held by multiple runs of a script. | |
39 | See: https://github.com/ipython/ipython/issues/141 |
|
44 | See: https://github.com/ipython/ipython/issues/141 | |
40 |
|
45 | |||
41 | In [1]: _ip.clear_main_mod_cache() |
|
46 | In [1]: _ip.clear_main_mod_cache() | |
42 | # random |
|
47 | # random | |
43 |
|
48 | |||
44 | In [2]: %run refbug |
|
49 | In [2]: %run refbug | |
45 |
|
50 | |||
46 | In [3]: call_f() |
|
51 | In [3]: call_f() | |
47 | lowercased: hello |
|
52 | lowercased: hello | |
48 |
|
53 | |||
49 | In [4]: %run refbug |
|
54 | In [4]: %run refbug | |
50 |
|
55 | |||
51 | In [5]: call_f() |
|
56 | In [5]: call_f() | |
52 | lowercased: hello |
|
57 | lowercased: hello | |
53 | lowercased: hello |
|
58 | lowercased: hello | |
54 | """ |
|
59 | """ | |
55 |
|
60 | |||
56 |
|
61 | |||
57 | def doctest_run_builtins(): |
|
62 | def doctest_run_builtins(): | |
58 | r"""Check that %run doesn't damage __builtins__. |
|
63 | r"""Check that %run doesn't damage __builtins__. | |
59 |
|
64 | |||
60 | In [1]: import tempfile |
|
65 | In [1]: import tempfile | |
61 |
|
66 | |||
62 | In [2]: bid1 = id(__builtins__) |
|
67 | In [2]: bid1 = id(__builtins__) | |
63 |
|
68 | |||
64 | In [3]: fname = tempfile.mkstemp('.py')[1] |
|
69 | In [3]: fname = tempfile.mkstemp('.py')[1] | |
65 |
|
70 | |||
66 | In [3]: f = open(fname,'w') |
|
71 | In [3]: f = open(fname,'w') | |
67 |
|
72 | |||
68 | In [4]: dummy= f.write('pass\n') |
|
73 | In [4]: dummy= f.write('pass\n') | |
69 |
|
74 | |||
70 | In [5]: f.flush() |
|
75 | In [5]: f.flush() | |
71 |
|
76 | |||
72 | In [6]: t1 = type(__builtins__) |
|
77 | In [6]: t1 = type(__builtins__) | |
73 |
|
78 | |||
74 | In [7]: %run $fname |
|
79 | In [7]: %run $fname | |
75 |
|
80 | |||
76 | In [7]: f.close() |
|
81 | In [7]: f.close() | |
77 |
|
82 | |||
78 | In [8]: bid2 = id(__builtins__) |
|
83 | In [8]: bid2 = id(__builtins__) | |
79 |
|
84 | |||
80 | In [9]: t2 = type(__builtins__) |
|
85 | In [9]: t2 = type(__builtins__) | |
81 |
|
86 | |||
82 | In [10]: t1 == t2 |
|
87 | In [10]: t1 == t2 | |
83 | Out[10]: True |
|
88 | Out[10]: True | |
84 |
|
89 | |||
85 | In [10]: bid1 == bid2 |
|
90 | In [10]: bid1 == bid2 | |
86 | Out[10]: True |
|
91 | Out[10]: True | |
87 |
|
92 | |||
88 | In [12]: try: |
|
93 | In [12]: try: | |
89 | ....: os.unlink(fname) |
|
94 | ....: os.unlink(fname) | |
90 | ....: except: |
|
95 | ....: except: | |
91 | ....: pass |
|
96 | ....: pass | |
92 | ....: |
|
97 | ....: | |
93 | """ |
|
98 | """ | |
94 |
|
99 | |||
95 |
|
100 | |||
96 | def doctest_run_option_parser(): |
|
101 | def doctest_run_option_parser(): | |
97 | r"""Test option parser in %run. |
|
102 | r"""Test option parser in %run. | |
98 |
|
103 | |||
99 | In [1]: %run print_argv.py |
|
104 | In [1]: %run print_argv.py | |
100 | [] |
|
105 | [] | |
101 |
|
106 | |||
102 | In [2]: %run print_argv.py print*.py |
|
107 | In [2]: %run print_argv.py print*.py | |
103 | ['print_argv.py'] |
|
108 | ['print_argv.py'] | |
104 |
|
109 | |||
105 | In [3]: %run -G print_argv.py print*.py |
|
110 | In [3]: %run -G print_argv.py print*.py | |
106 | ['print*.py'] |
|
111 | ['print*.py'] | |
107 |
|
112 | |||
108 | """ |
|
113 | """ | |
109 |
|
114 | |||
110 |
|
115 | |||
111 | @dec.skip_win32 |
|
116 | @dec.skip_win32 | |
112 | def doctest_run_option_parser_for_posix(): |
|
117 | def doctest_run_option_parser_for_posix(): | |
113 | r"""Test option parser in %run (Linux/OSX specific). |
|
118 | r"""Test option parser in %run (Linux/OSX specific). | |
114 |
|
119 | |||
115 | You need double quote to escape glob in POSIX systems: |
|
120 | You need double quote to escape glob in POSIX systems: | |
116 |
|
121 | |||
117 | In [1]: %run print_argv.py print\\*.py |
|
122 | In [1]: %run print_argv.py print\\*.py | |
118 | ['print*.py'] |
|
123 | ['print*.py'] | |
119 |
|
124 | |||
120 | You can't use quote to escape glob in POSIX systems: |
|
125 | You can't use quote to escape glob in POSIX systems: | |
121 |
|
126 | |||
122 | In [2]: %run print_argv.py 'print*.py' |
|
127 | In [2]: %run print_argv.py 'print*.py' | |
123 | ['print_argv.py'] |
|
128 | ['print_argv.py'] | |
124 |
|
129 | |||
125 | """ |
|
130 | """ | |
126 |
|
131 | |||
127 |
|
132 | |||
128 | @dec.skip_if_not_win32 |
|
133 | @dec.skip_if_not_win32 | |
129 | def doctest_run_option_parser_for_windows(): |
|
134 | def doctest_run_option_parser_for_windows(): | |
130 | r"""Test option parser in %run (Windows specific). |
|
135 | r"""Test option parser in %run (Windows specific). | |
131 |
|
136 | |||
132 | In Windows, you can't escape ``*` `by backslash: |
|
137 | In Windows, you can't escape ``*` `by backslash: | |
133 |
|
138 | |||
134 | In [1]: %run print_argv.py print\\*.py |
|
139 | In [1]: %run print_argv.py print\\*.py | |
135 | ['print\\*.py'] |
|
140 | ['print\\*.py'] | |
136 |
|
141 | |||
137 | You can use quote to escape glob: |
|
142 | You can use quote to escape glob: | |
138 |
|
143 | |||
139 | In [2]: %run print_argv.py 'print*.py' |
|
144 | In [2]: %run print_argv.py 'print*.py' | |
140 | ['print*.py'] |
|
145 | ['print*.py'] | |
141 |
|
146 | |||
142 | """ |
|
147 | """ | |
143 |
|
148 | |||
144 |
|
149 | |||
145 | @py3compat.doctest_refactor_print |
|
150 | @py3compat.doctest_refactor_print | |
146 | def doctest_reset_del(): |
|
151 | def doctest_reset_del(): | |
147 | """Test that resetting doesn't cause errors in __del__ methods. |
|
152 | """Test that resetting doesn't cause errors in __del__ methods. | |
148 |
|
153 | |||
149 | In [2]: class A(object): |
|
154 | In [2]: class A(object): | |
150 | ...: def __del__(self): |
|
155 | ...: def __del__(self): | |
151 | ...: print str("Hi") |
|
156 | ...: print str("Hi") | |
152 | ...: |
|
157 | ...: | |
153 |
|
158 | |||
154 | In [3]: a = A() |
|
159 | In [3]: a = A() | |
155 |
|
160 | |||
156 | In [4]: get_ipython().reset() |
|
161 | In [4]: get_ipython().reset() | |
157 | Hi |
|
162 | Hi | |
158 |
|
163 | |||
159 | In [5]: 1+1 |
|
164 | In [5]: 1+1 | |
160 | Out[5]: 2 |
|
165 | Out[5]: 2 | |
161 | """ |
|
166 | """ | |
162 |
|
167 | |||
163 | # For some tests, it will be handy to organize them in a class with a common |
|
168 | # For some tests, it will be handy to organize them in a class with a common | |
164 | # setup that makes a temp file |
|
169 | # setup that makes a temp file | |
165 |
|
170 | |||
166 | class TestMagicRunPass(tt.TempFileMixin): |
|
171 | class TestMagicRunPass(tt.TempFileMixin): | |
167 |
|
172 | |||
168 | def setup(self): |
|
173 | def setup(self): | |
169 | """Make a valid python temp file.""" |
|
174 | """Make a valid python temp file.""" | |
170 | self.mktmp('pass\n') |
|
175 | self.mktmp('pass\n') | |
171 |
|
176 | |||
172 | def run_tmpfile(self): |
|
177 | def run_tmpfile(self): | |
173 | _ip = get_ipython() |
|
178 | _ip = get_ipython() | |
174 | # This fails on Windows if self.tmpfile.name has spaces or "~" in it. |
|
179 | # This fails on Windows if self.tmpfile.name has spaces or "~" in it. | |
175 | # See below and ticket https://bugs.launchpad.net/bugs/366353 |
|
180 | # See below and ticket https://bugs.launchpad.net/bugs/366353 | |
176 | _ip.magic('run %s' % self.fname) |
|
181 | _ip.magic('run %s' % self.fname) | |
177 |
|
182 | |||
178 | def run_tmpfile_p(self): |
|
183 | def run_tmpfile_p(self): | |
179 | _ip = get_ipython() |
|
184 | _ip = get_ipython() | |
180 | # This fails on Windows if self.tmpfile.name has spaces or "~" in it. |
|
185 | # This fails on Windows if self.tmpfile.name has spaces or "~" in it. | |
181 | # See below and ticket https://bugs.launchpad.net/bugs/366353 |
|
186 | # See below and ticket https://bugs.launchpad.net/bugs/366353 | |
182 | _ip.magic('run -p %s' % self.fname) |
|
187 | _ip.magic('run -p %s' % self.fname) | |
183 |
|
188 | |||
184 | def test_builtins_id(self): |
|
189 | def test_builtins_id(self): | |
185 | """Check that %run doesn't damage __builtins__ """ |
|
190 | """Check that %run doesn't damage __builtins__ """ | |
186 | _ip = get_ipython() |
|
191 | _ip = get_ipython() | |
187 | # Test that the id of __builtins__ is not modified by %run |
|
192 | # Test that the id of __builtins__ is not modified by %run | |
188 | bid1 = id(_ip.user_ns['__builtins__']) |
|
193 | bid1 = id(_ip.user_ns['__builtins__']) | |
189 | self.run_tmpfile() |
|
194 | self.run_tmpfile() | |
190 | bid2 = id(_ip.user_ns['__builtins__']) |
|
195 | bid2 = id(_ip.user_ns['__builtins__']) | |
191 | nt.assert_equal(bid1, bid2) |
|
196 | nt.assert_equal(bid1, bid2) | |
192 |
|
197 | |||
193 | def test_builtins_type(self): |
|
198 | def test_builtins_type(self): | |
194 | """Check that the type of __builtins__ doesn't change with %run. |
|
199 | """Check that the type of __builtins__ doesn't change with %run. | |
195 |
|
200 | |||
196 | However, the above could pass if __builtins__ was already modified to |
|
201 | However, the above could pass if __builtins__ was already modified to | |
197 | be a dict (it should be a module) by a previous use of %run. So we |
|
202 | be a dict (it should be a module) by a previous use of %run. So we | |
198 | also check explicitly that it really is a module: |
|
203 | also check explicitly that it really is a module: | |
199 | """ |
|
204 | """ | |
200 | _ip = get_ipython() |
|
205 | _ip = get_ipython() | |
201 | self.run_tmpfile() |
|
206 | self.run_tmpfile() | |
202 | nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys)) |
|
207 | nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys)) | |
203 |
|
208 | |||
204 | def test_prompts(self): |
|
209 | def test_prompts(self): | |
205 | """Test that prompts correctly generate after %run""" |
|
210 | """Test that prompts correctly generate after %run""" | |
206 | self.run_tmpfile() |
|
211 | self.run_tmpfile() | |
207 | _ip = get_ipython() |
|
212 | _ip = get_ipython() | |
208 | p2 = _ip.prompt_manager.render('in2').strip() |
|
213 | p2 = _ip.prompt_manager.render('in2').strip() | |
209 | nt.assert_equal(p2[:3], '...') |
|
214 | nt.assert_equal(p2[:3], '...') | |
210 |
|
215 | |||
211 | def test_run_profile( self ): |
|
216 | def test_run_profile( self ): | |
212 | """Test that the option -p, which invokes the profiler, do not |
|
217 | """Test that the option -p, which invokes the profiler, do not | |
213 | crash by invoking execfile""" |
|
218 | crash by invoking execfile""" | |
214 | _ip = get_ipython() |
|
219 | _ip = get_ipython() | |
215 | self.run_tmpfile_p() |
|
220 | self.run_tmpfile_p() | |
216 |
|
221 | |||
217 |
|
222 | |||
218 | class TestMagicRunSimple(tt.TempFileMixin): |
|
223 | class TestMagicRunSimple(tt.TempFileMixin): | |
219 |
|
224 | |||
220 | def test_simpledef(self): |
|
225 | def test_simpledef(self): | |
221 | """Test that simple class definitions work.""" |
|
226 | """Test that simple class definitions work.""" | |
222 | src = ("class foo: pass\n" |
|
227 | src = ("class foo: pass\n" | |
223 | "def f(): return foo()") |
|
228 | "def f(): return foo()") | |
224 | self.mktmp(src) |
|
229 | self.mktmp(src) | |
225 | _ip.magic('run %s' % self.fname) |
|
230 | _ip.magic('run %s' % self.fname) | |
226 | _ip.run_cell('t = isinstance(f(), foo)') |
|
231 | _ip.run_cell('t = isinstance(f(), foo)') | |
227 | nt.assert_true(_ip.user_ns['t']) |
|
232 | nt.assert_true(_ip.user_ns['t']) | |
228 |
|
233 | |||
229 | def test_obj_del(self): |
|
234 | def test_obj_del(self): | |
230 | """Test that object's __del__ methods are called on exit.""" |
|
235 | """Test that object's __del__ methods are called on exit.""" | |
231 | if sys.platform == 'win32': |
|
236 | if sys.platform == 'win32': | |
232 | try: |
|
237 | try: | |
233 | import win32api |
|
238 | import win32api | |
234 | except ImportError: |
|
239 | except ImportError: | |
235 | raise SkipTest("Test requires pywin32") |
|
240 | raise SkipTest("Test requires pywin32") | |
236 | src = ("class A(object):\n" |
|
241 | src = ("class A(object):\n" | |
237 | " def __del__(self):\n" |
|
242 | " def __del__(self):\n" | |
238 | " print 'object A deleted'\n" |
|
243 | " print 'object A deleted'\n" | |
239 | "a = A()\n") |
|
244 | "a = A()\n") | |
240 | self.mktmp(py3compat.doctest_refactor_print(src)) |
|
245 | self.mktmp(py3compat.doctest_refactor_print(src)) | |
241 | if dec.module_not_available('sqlite3'): |
|
246 | if dec.module_not_available('sqlite3'): | |
242 | err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' |
|
247 | err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' | |
243 | else: |
|
248 | else: | |
244 | err = None |
|
249 | err = None | |
245 | tt.ipexec_validate(self.fname, 'object A deleted', err) |
|
250 | tt.ipexec_validate(self.fname, 'object A deleted', err) | |
246 |
|
251 | |||
247 | def test_aggressive_namespace_cleanup(self): |
|
252 | def test_aggressive_namespace_cleanup(self): | |
248 | """Test that namespace cleanup is not too aggressive GH-238 |
|
253 | """Test that namespace cleanup is not too aggressive GH-238 | |
249 |
|
254 | |||
250 | Returning from another run magic deletes the namespace""" |
|
255 | Returning from another run magic deletes the namespace""" | |
251 | # see ticket https://github.com/ipython/ipython/issues/238 |
|
256 | # see ticket https://github.com/ipython/ipython/issues/238 | |
252 | class secondtmp(tt.TempFileMixin): pass |
|
257 | class secondtmp(tt.TempFileMixin): pass | |
253 | empty = secondtmp() |
|
258 | empty = secondtmp() | |
254 | empty.mktmp('') |
|
259 | empty.mktmp('') | |
255 | # On Windows, the filename will have \users in it, so we need to use the |
|
260 | # On Windows, the filename will have \users in it, so we need to use the | |
256 | # repr so that the \u becomes \\u. |
|
261 | # repr so that the \u becomes \\u. | |
257 | src = ("ip = get_ipython()\n" |
|
262 | src = ("ip = get_ipython()\n" | |
258 | "for i in range(5):\n" |
|
263 | "for i in range(5):\n" | |
259 | " try:\n" |
|
264 | " try:\n" | |
260 | " ip.magic(%r)\n" |
|
265 | " ip.magic(%r)\n" | |
261 | " except NameError as e:\n" |
|
266 | " except NameError as e:\n" | |
262 | " print(i)\n" |
|
267 | " print(i)\n" | |
263 | " break\n" % ('run ' + empty.fname)) |
|
268 | " break\n" % ('run ' + empty.fname)) | |
264 | self.mktmp(src) |
|
269 | self.mktmp(src) | |
265 | _ip.magic('run %s' % self.fname) |
|
270 | _ip.magic('run %s' % self.fname) | |
266 | _ip.run_cell('ip == get_ipython()') |
|
271 | _ip.run_cell('ip == get_ipython()') | |
267 | nt.assert_equal(_ip.user_ns['i'], 4) |
|
272 | nt.assert_equal(_ip.user_ns['i'], 4) | |
268 |
|
273 | |||
269 | def test_run_second(self): |
|
274 | def test_run_second(self): | |
270 | """Test that running a second file doesn't clobber the first, gh-3547 |
|
275 | """Test that running a second file doesn't clobber the first, gh-3547 | |
271 | """ |
|
276 | """ | |
272 | self.mktmp("avar = 1\n" |
|
277 | self.mktmp("avar = 1\n" | |
273 | "def afunc():\n" |
|
278 | "def afunc():\n" | |
274 | " return avar\n") |
|
279 | " return avar\n") | |
275 |
|
280 | |||
276 | empty = tt.TempFileMixin() |
|
281 | empty = tt.TempFileMixin() | |
277 | empty.mktmp("") |
|
282 | empty.mktmp("") | |
278 |
|
283 | |||
279 | _ip.magic('run %s' % self.fname) |
|
284 | _ip.magic('run %s' % self.fname) | |
280 | _ip.magic('run %s' % empty.fname) |
|
285 | _ip.magic('run %s' % empty.fname) | |
281 | nt.assert_equal(_ip.user_ns['afunc'](), 1) |
|
286 | nt.assert_equal(_ip.user_ns['afunc'](), 1) | |
282 |
|
287 | |||
283 | @dec.skip_win32 |
|
288 | @dec.skip_win32 | |
284 | def test_tclass(self): |
|
289 | def test_tclass(self): | |
285 | mydir = os.path.dirname(__file__) |
|
290 | mydir = os.path.dirname(__file__) | |
286 | tc = os.path.join(mydir, 'tclass') |
|
291 | tc = os.path.join(mydir, 'tclass') | |
287 | src = ("%%run '%s' C-first\n" |
|
292 | src = ("%%run '%s' C-first\n" | |
288 | "%%run '%s' C-second\n" |
|
293 | "%%run '%s' C-second\n" | |
289 | "%%run '%s' C-third\n") % (tc, tc, tc) |
|
294 | "%%run '%s' C-third\n") % (tc, tc, tc) | |
290 | self.mktmp(src, '.ipy') |
|
295 | self.mktmp(src, '.ipy') | |
291 | out = """\ |
|
296 | out = """\ | |
292 | ARGV 1-: ['C-first'] |
|
297 | ARGV 1-: ['C-first'] | |
293 | ARGV 1-: ['C-second'] |
|
298 | ARGV 1-: ['C-second'] | |
294 | tclass.py: deleting object: C-first |
|
299 | tclass.py: deleting object: C-first | |
295 | ARGV 1-: ['C-third'] |
|
300 | ARGV 1-: ['C-third'] | |
296 | tclass.py: deleting object: C-second |
|
301 | tclass.py: deleting object: C-second | |
297 | tclass.py: deleting object: C-third |
|
302 | tclass.py: deleting object: C-third | |
298 | """ |
|
303 | """ | |
299 | if dec.module_not_available('sqlite3'): |
|
304 | if dec.module_not_available('sqlite3'): | |
300 | err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' |
|
305 | err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' | |
301 | else: |
|
306 | else: | |
302 | err = None |
|
307 | err = None | |
303 | tt.ipexec_validate(self.fname, out, err) |
|
308 | tt.ipexec_validate(self.fname, out, err) | |
304 |
|
309 | |||
305 | def test_run_i_after_reset(self): |
|
310 | def test_run_i_after_reset(self): | |
306 | """Check that %run -i still works after %reset (gh-693)""" |
|
311 | """Check that %run -i still works after %reset (gh-693)""" | |
307 | src = "yy = zz\n" |
|
312 | src = "yy = zz\n" | |
308 | self.mktmp(src) |
|
313 | self.mktmp(src) | |
309 | _ip.run_cell("zz = 23") |
|
314 | _ip.run_cell("zz = 23") | |
310 | _ip.magic('run -i %s' % self.fname) |
|
315 | _ip.magic('run -i %s' % self.fname) | |
311 | nt.assert_equal(_ip.user_ns['yy'], 23) |
|
316 | nt.assert_equal(_ip.user_ns['yy'], 23) | |
312 | _ip.magic('reset -f') |
|
317 | _ip.magic('reset -f') | |
313 | _ip.run_cell("zz = 23") |
|
318 | _ip.run_cell("zz = 23") | |
314 | _ip.magic('run -i %s' % self.fname) |
|
319 | _ip.magic('run -i %s' % self.fname) | |
315 | nt.assert_equal(_ip.user_ns['yy'], 23) |
|
320 | nt.assert_equal(_ip.user_ns['yy'], 23) | |
316 |
|
321 | |||
317 | def test_unicode(self): |
|
322 | def test_unicode(self): | |
318 | """Check that files in odd encodings are accepted.""" |
|
323 | """Check that files in odd encodings are accepted.""" | |
319 | mydir = os.path.dirname(__file__) |
|
324 | mydir = os.path.dirname(__file__) | |
320 | na = os.path.join(mydir, 'nonascii.py') |
|
325 | na = os.path.join(mydir, 'nonascii.py') | |
321 | _ip.magic('run "%s"' % na) |
|
326 | _ip.magic('run "%s"' % na) | |
322 | nt.assert_equal(_ip.user_ns['u'], u'ΠΡβΠ€') |
|
327 | nt.assert_equal(_ip.user_ns['u'], u'ΠΡβΠ€') | |
323 |
|
328 | |||
324 | def test_run_py_file_attribute(self): |
|
329 | def test_run_py_file_attribute(self): | |
325 | """Test handling of `__file__` attribute in `%run <file>.py`.""" |
|
330 | """Test handling of `__file__` attribute in `%run <file>.py`.""" | |
326 | src = "t = __file__\n" |
|
331 | src = "t = __file__\n" | |
327 | self.mktmp(src) |
|
332 | self.mktmp(src) | |
328 | _missing = object() |
|
333 | _missing = object() | |
329 | file1 = _ip.user_ns.get('__file__', _missing) |
|
334 | file1 = _ip.user_ns.get('__file__', _missing) | |
330 | _ip.magic('run %s' % self.fname) |
|
335 | _ip.magic('run %s' % self.fname) | |
331 | file2 = _ip.user_ns.get('__file__', _missing) |
|
336 | file2 = _ip.user_ns.get('__file__', _missing) | |
332 |
|
337 | |||
333 | # Check that __file__ was equal to the filename in the script's |
|
338 | # Check that __file__ was equal to the filename in the script's | |
334 | # namespace. |
|
339 | # namespace. | |
335 | nt.assert_equal(_ip.user_ns['t'], self.fname) |
|
340 | nt.assert_equal(_ip.user_ns['t'], self.fname) | |
336 |
|
341 | |||
337 | # Check that __file__ was not leaked back into user_ns. |
|
342 | # Check that __file__ was not leaked back into user_ns. | |
338 | nt.assert_equal(file1, file2) |
|
343 | nt.assert_equal(file1, file2) | |
339 |
|
344 | |||
340 | def test_run_ipy_file_attribute(self): |
|
345 | def test_run_ipy_file_attribute(self): | |
341 | """Test handling of `__file__` attribute in `%run <file.ipy>`.""" |
|
346 | """Test handling of `__file__` attribute in `%run <file.ipy>`.""" | |
342 | src = "t = __file__\n" |
|
347 | src = "t = __file__\n" | |
343 | self.mktmp(src, ext='.ipy') |
|
348 | self.mktmp(src, ext='.ipy') | |
344 | _missing = object() |
|
349 | _missing = object() | |
345 | file1 = _ip.user_ns.get('__file__', _missing) |
|
350 | file1 = _ip.user_ns.get('__file__', _missing) | |
346 | _ip.magic('run %s' % self.fname) |
|
351 | _ip.magic('run %s' % self.fname) | |
347 | file2 = _ip.user_ns.get('__file__', _missing) |
|
352 | file2 = _ip.user_ns.get('__file__', _missing) | |
348 |
|
353 | |||
349 | # Check that __file__ was equal to the filename in the script's |
|
354 | # Check that __file__ was equal to the filename in the script's | |
350 | # namespace. |
|
355 | # namespace. | |
351 | nt.assert_equal(_ip.user_ns['t'], self.fname) |
|
356 | nt.assert_equal(_ip.user_ns['t'], self.fname) | |
352 |
|
357 | |||
353 | # Check that __file__ was not leaked back into user_ns. |
|
358 | # Check that __file__ was not leaked back into user_ns. | |
354 | nt.assert_equal(file1, file2) |
|
359 | nt.assert_equal(file1, file2) | |
355 |
|
360 | |||
356 | def test_run_formatting(self): |
|
361 | def test_run_formatting(self): | |
357 | """ Test that %run -t -N<N> does not raise a TypeError for N > 1.""" |
|
362 | """ Test that %run -t -N<N> does not raise a TypeError for N > 1.""" | |
358 | src = "pass" |
|
363 | src = "pass" | |
359 | self.mktmp(src) |
|
364 | self.mktmp(src) | |
360 | _ip.magic('run -t -N 1 %s' % self.fname) |
|
365 | _ip.magic('run -t -N 1 %s' % self.fname) | |
361 | _ip.magic('run -t -N 10 %s' % self.fname) |
|
366 | _ip.magic('run -t -N 10 %s' % self.fname) | |
362 |
|
367 | |||
363 | def test_ignore_sys_exit(self): |
|
368 | def test_ignore_sys_exit(self): | |
364 | """Test the -e option to ignore sys.exit()""" |
|
369 | """Test the -e option to ignore sys.exit()""" | |
365 | src = "import sys; sys.exit(1)" |
|
370 | src = "import sys; sys.exit(1)" | |
366 | self.mktmp(src) |
|
371 | self.mktmp(src) | |
367 | with tt.AssertPrints('SystemExit'): |
|
372 | with tt.AssertPrints('SystemExit'): | |
368 | _ip.magic('run %s' % self.fname) |
|
373 | _ip.magic('run %s' % self.fname) | |
369 |
|
374 | |||
370 | with tt.AssertNotPrints('SystemExit'): |
|
375 | with tt.AssertNotPrints('SystemExit'): | |
371 | _ip.magic('run -e %s' % self.fname) |
|
376 | _ip.magic('run -e %s' % self.fname) | |
372 |
|
377 | |||
373 | @dec.skip_without('IPython.nbformat') # Requires jsonschema |
|
378 | @dec.skip_without('IPython.nbformat') # Requires jsonschema | |
374 | def test_run_nb(self): |
|
379 | def test_run_nb(self): | |
375 | """Test %run notebook.ipynb""" |
|
380 | """Test %run notebook.ipynb""" | |
376 | from IPython.nbformat import v4, writes |
|
381 | from IPython.nbformat import v4, writes | |
377 | nb = v4.new_notebook( |
|
382 | nb = v4.new_notebook( | |
378 | cells=[ |
|
383 | cells=[ | |
379 | v4.new_markdown_cell("The Ultimate Question of Everything"), |
|
384 | v4.new_markdown_cell("The Ultimate Question of Everything"), | |
380 | v4.new_code_cell("answer=42") |
|
385 | v4.new_code_cell("answer=42") | |
381 | ] |
|
386 | ] | |
382 | ) |
|
387 | ) | |
383 | src = writes(nb, version=4) |
|
388 | src = writes(nb, version=4) | |
384 | self.mktmp(src, ext='.ipynb') |
|
389 | self.mktmp(src, ext='.ipynb') | |
385 |
|
390 | |||
386 | _ip.magic("run %s" % self.fname) |
|
391 | _ip.magic("run %s" % self.fname) | |
387 |
|
392 | |||
388 | nt.assert_equal(_ip.user_ns['answer'], 42) |
|
393 | nt.assert_equal(_ip.user_ns['answer'], 42) | |
389 |
|
394 | |||
390 |
|
395 | |||
391 |
|
396 | |||
392 | class TestMagicRunWithPackage(unittest.TestCase): |
|
397 | class TestMagicRunWithPackage(unittest.TestCase): | |
393 |
|
398 | |||
394 | def writefile(self, name, content): |
|
399 | def writefile(self, name, content): | |
395 | path = os.path.join(self.tempdir.name, name) |
|
400 | path = os.path.join(self.tempdir.name, name) | |
396 | d = os.path.dirname(path) |
|
401 | d = os.path.dirname(path) | |
397 | if not os.path.isdir(d): |
|
402 | if not os.path.isdir(d): | |
398 | os.makedirs(d) |
|
403 | os.makedirs(d) | |
399 | with open(path, 'w') as f: |
|
404 | with open(path, 'w') as f: | |
400 | f.write(textwrap.dedent(content)) |
|
405 | f.write(textwrap.dedent(content)) | |
401 |
|
406 | |||
402 | def setUp(self): |
|
407 | def setUp(self): | |
403 | self.package = package = 'tmp{0}'.format(repr(random.random())[2:]) |
|
408 | self.package = package = 'tmp{0}'.format(repr(random.random())[2:]) | |
404 | """Temporary valid python package name.""" |
|
409 | """Temporary valid python package name.""" | |
405 |
|
410 | |||
406 | self.value = int(random.random() * 10000) |
|
411 | self.value = int(random.random() * 10000) | |
407 |
|
412 | |||
408 | self.tempdir = TemporaryDirectory() |
|
413 | self.tempdir = TemporaryDirectory() | |
409 | self.__orig_cwd = py3compat.getcwd() |
|
414 | self.__orig_cwd = py3compat.getcwd() | |
410 | sys.path.insert(0, self.tempdir.name) |
|
415 | sys.path.insert(0, self.tempdir.name) | |
411 |
|
416 | |||
412 | self.writefile(os.path.join(package, '__init__.py'), '') |
|
417 | self.writefile(os.path.join(package, '__init__.py'), '') | |
413 | self.writefile(os.path.join(package, 'sub.py'), """ |
|
418 | self.writefile(os.path.join(package, 'sub.py'), """ | |
414 | x = {0!r} |
|
419 | x = {0!r} | |
415 | """.format(self.value)) |
|
420 | """.format(self.value)) | |
416 | self.writefile(os.path.join(package, 'relative.py'), """ |
|
421 | self.writefile(os.path.join(package, 'relative.py'), """ | |
417 | from .sub import x |
|
422 | from .sub import x | |
418 | """) |
|
423 | """) | |
419 | self.writefile(os.path.join(package, 'absolute.py'), """ |
|
424 | self.writefile(os.path.join(package, 'absolute.py'), """ | |
420 | from {0}.sub import x |
|
425 | from {0}.sub import x | |
421 | """.format(package)) |
|
426 | """.format(package)) | |
422 |
|
427 | |||
423 | def tearDown(self): |
|
428 | def tearDown(self): | |
424 | os.chdir(self.__orig_cwd) |
|
429 | os.chdir(self.__orig_cwd) | |
425 | sys.path[:] = [p for p in sys.path if p != self.tempdir.name] |
|
430 | sys.path[:] = [p for p in sys.path if p != self.tempdir.name] | |
426 | self.tempdir.cleanup() |
|
431 | self.tempdir.cleanup() | |
427 |
|
432 | |||
428 | def check_run_submodule(self, submodule, opts=''): |
|
433 | def check_run_submodule(self, submodule, opts=''): | |
429 | _ip.user_ns.pop('x', None) |
|
434 | _ip.user_ns.pop('x', None) | |
430 | _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts)) |
|
435 | _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts)) | |
431 | self.assertEqual(_ip.user_ns['x'], self.value, |
|
436 | self.assertEqual(_ip.user_ns['x'], self.value, | |
432 | 'Variable `x` is not loaded from module `{0}`.' |
|
437 | 'Variable `x` is not loaded from module `{0}`.' | |
433 | .format(submodule)) |
|
438 | .format(submodule)) | |
434 |
|
439 | |||
435 | def test_run_submodule_with_absolute_import(self): |
|
440 | def test_run_submodule_with_absolute_import(self): | |
436 | self.check_run_submodule('absolute') |
|
441 | self.check_run_submodule('absolute') | |
437 |
|
442 | |||
438 | def test_run_submodule_with_relative_import(self): |
|
443 | def test_run_submodule_with_relative_import(self): | |
439 | """Run submodule that has a relative import statement (#2727).""" |
|
444 | """Run submodule that has a relative import statement (#2727).""" | |
440 | self.check_run_submodule('relative') |
|
445 | self.check_run_submodule('relative') | |
441 |
|
446 | |||
442 | def test_prun_submodule_with_absolute_import(self): |
|
447 | def test_prun_submodule_with_absolute_import(self): | |
443 | self.check_run_submodule('absolute', '-p') |
|
448 | self.check_run_submodule('absolute', '-p') | |
444 |
|
449 | |||
445 | def test_prun_submodule_with_relative_import(self): |
|
450 | def test_prun_submodule_with_relative_import(self): | |
446 | self.check_run_submodule('relative', '-p') |
|
451 | self.check_run_submodule('relative', '-p') | |
447 |
|
452 | |||
448 | def with_fake_debugger(func): |
|
453 | def with_fake_debugger(func): | |
449 | @functools.wraps(func) |
|
454 | @functools.wraps(func) | |
450 | def wrapper(*args, **kwds): |
|
455 | def wrapper(*args, **kwds): | |
451 |
with |
|
456 | with patch.object(debugger.Pdb, 'run', staticmethod(eval)): | |
452 | return func(*args, **kwds) |
|
457 | return func(*args, **kwds) | |
453 | return wrapper |
|
458 | return wrapper | |
454 |
|
459 | |||
455 | @with_fake_debugger |
|
460 | @with_fake_debugger | |
456 | def test_debug_run_submodule_with_absolute_import(self): |
|
461 | def test_debug_run_submodule_with_absolute_import(self): | |
457 | self.check_run_submodule('absolute', '-d') |
|
462 | self.check_run_submodule('absolute', '-d') | |
458 |
|
463 | |||
459 | @with_fake_debugger |
|
464 | @with_fake_debugger | |
460 | def test_debug_run_submodule_with_relative_import(self): |
|
465 | def test_debug_run_submodule_with_relative_import(self): | |
461 | self.check_run_submodule('relative', '-d') |
|
466 | self.check_run_submodule('relative', '-d') | |
462 |
|
467 | |||
463 | def test_run__name__(): |
|
468 | def test_run__name__(): | |
464 | with TemporaryDirectory() as td: |
|
469 | with TemporaryDirectory() as td: | |
465 | path = pjoin(td, 'foo.py') |
|
470 | path = pjoin(td, 'foo.py') | |
466 | with open(path, 'w') as f: |
|
471 | with open(path, 'w') as f: | |
467 | f.write("q = __name__") |
|
472 | f.write("q = __name__") | |
468 |
|
473 | |||
469 | _ip.user_ns.pop('q', None) |
|
474 | _ip.user_ns.pop('q', None) | |
470 | _ip.magic('run {}'.format(path)) |
|
475 | _ip.magic('run {}'.format(path)) | |
471 | nt.assert_equal(_ip.user_ns.pop('q'), '__main__') |
|
476 | nt.assert_equal(_ip.user_ns.pop('q'), '__main__') | |
472 |
|
477 | |||
473 | _ip.magic('run -n {}'.format(path)) |
|
478 | _ip.magic('run -n {}'.format(path)) | |
474 | nt.assert_equal(_ip.user_ns.pop('q'), 'foo') |
|
479 | nt.assert_equal(_ip.user_ns.pop('q'), 'foo') | |
475 |
|
480 | |||
476 | def test_run_tb(): |
|
481 | def test_run_tb(): | |
477 | """Test traceback offset in %run""" |
|
482 | """Test traceback offset in %run""" | |
478 | with TemporaryDirectory() as td: |
|
483 | with TemporaryDirectory() as td: | |
479 | path = pjoin(td, 'foo.py') |
|
484 | path = pjoin(td, 'foo.py') | |
480 | with open(path, 'w') as f: |
|
485 | with open(path, 'w') as f: | |
481 | f.write('\n'.join([ |
|
486 | f.write('\n'.join([ | |
482 | "def foo():", |
|
487 | "def foo():", | |
483 | " return bar()", |
|
488 | " return bar()", | |
484 | "def bar():", |
|
489 | "def bar():", | |
485 | " raise RuntimeError('hello!')", |
|
490 | " raise RuntimeError('hello!')", | |
486 | "foo()", |
|
491 | "foo()", | |
487 | ])) |
|
492 | ])) | |
488 | with capture_output() as io: |
|
493 | with capture_output() as io: | |
489 | _ip.magic('run {}'.format(path)) |
|
494 | _ip.magic('run {}'.format(path)) | |
490 | out = io.stdout |
|
495 | out = io.stdout | |
491 | nt.assert_not_in("execfile", out) |
|
496 | nt.assert_not_in("execfile", out) | |
492 | nt.assert_in("RuntimeError", out) |
|
497 | nt.assert_in("RuntimeError", out) | |
493 | nt.assert_equal(out.count("---->"), 3) |
|
498 | nt.assert_equal(out.count("---->"), 3) | |
494 |
|
499 | |||
495 | @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows") |
|
500 | @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows") | |
496 | def test_script_tb(): |
|
501 | def test_script_tb(): | |
497 | """Test traceback offset in `ipython script.py`""" |
|
502 | """Test traceback offset in `ipython script.py`""" | |
498 | with TemporaryDirectory() as td: |
|
503 | with TemporaryDirectory() as td: | |
499 | path = pjoin(td, 'foo.py') |
|
504 | path = pjoin(td, 'foo.py') | |
500 | with open(path, 'w') as f: |
|
505 | with open(path, 'w') as f: | |
501 | f.write('\n'.join([ |
|
506 | f.write('\n'.join([ | |
502 | "def foo():", |
|
507 | "def foo():", | |
503 | " return bar()", |
|
508 | " return bar()", | |
504 | "def bar():", |
|
509 | "def bar():", | |
505 | " raise RuntimeError('hello!')", |
|
510 | " raise RuntimeError('hello!')", | |
506 | "foo()", |
|
511 | "foo()", | |
507 | ])) |
|
512 | ])) | |
508 | out, err = tt.ipexec(path) |
|
513 | out, err = tt.ipexec(path) | |
509 | nt.assert_not_in("execfile", out) |
|
514 | nt.assert_not_in("execfile", out) | |
510 | nt.assert_in("RuntimeError", out) |
|
515 | nt.assert_in("RuntimeError", out) | |
511 | nt.assert_equal(out.count("---->"), 3) |
|
516 | nt.assert_equal(out.count("---->"), 3) | |
512 |
|
517 |
@@ -1,691 +1,693 | |||||
1 | """Test interact and interactive.""" |
|
1 | """Test interact and interactive.""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
7 |
|
7 | |||
8 | from collections import OrderedDict |
|
8 | try: | |
|
9 | from unittest.mock import patch | |||
|
10 | except ImportError: | |||
|
11 | from mock import patch | |||
9 |
|
12 | |||
10 | import nose.tools as nt |
|
13 | import nose.tools as nt | |
11 | import IPython.testing.tools as tt |
|
|||
12 |
|
14 | |||
13 | from IPython.kernel.comm import Comm |
|
15 | from IPython.kernel.comm import Comm | |
14 | from IPython.html import widgets |
|
16 | from IPython.html import widgets | |
15 | from IPython.html.widgets import interact, interactive, Widget, interaction |
|
17 | from IPython.html.widgets import interact, interactive, Widget, interaction | |
16 | from IPython.utils.py3compat import annotate |
|
18 | from IPython.utils.py3compat import annotate | |
17 |
|
19 | |||
18 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
19 | # Utility stuff |
|
21 | # Utility stuff | |
20 | #----------------------------------------------------------------------------- |
|
22 | #----------------------------------------------------------------------------- | |
21 |
|
23 | |||
22 | class DummyComm(Comm): |
|
24 | class DummyComm(Comm): | |
23 | comm_id = 'a-b-c-d' |
|
25 | comm_id = 'a-b-c-d' | |
24 |
|
26 | |||
25 | def open(self, *args, **kwargs): |
|
27 | def open(self, *args, **kwargs): | |
26 | pass |
|
28 | pass | |
27 |
|
29 | |||
28 | def send(self, *args, **kwargs): |
|
30 | def send(self, *args, **kwargs): | |
29 | pass |
|
31 | pass | |
30 |
|
32 | |||
31 | def close(self, *args, **kwargs): |
|
33 | def close(self, *args, **kwargs): | |
32 | pass |
|
34 | pass | |
33 |
|
35 | |||
34 | _widget_attrs = {} |
|
36 | _widget_attrs = {} | |
35 | displayed = [] |
|
37 | displayed = [] | |
36 | undefined = object() |
|
38 | undefined = object() | |
37 |
|
39 | |||
38 | def setup(): |
|
40 | def setup(): | |
39 | _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined) |
|
41 | _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined) | |
40 | Widget._comm_default = lambda self: DummyComm() |
|
42 | Widget._comm_default = lambda self: DummyComm() | |
41 | _widget_attrs['_ipython_display_'] = Widget._ipython_display_ |
|
43 | _widget_attrs['_ipython_display_'] = Widget._ipython_display_ | |
42 | def raise_not_implemented(*args, **kwargs): |
|
44 | def raise_not_implemented(*args, **kwargs): | |
43 | raise NotImplementedError() |
|
45 | raise NotImplementedError() | |
44 | Widget._ipython_display_ = raise_not_implemented |
|
46 | Widget._ipython_display_ = raise_not_implemented | |
45 |
|
47 | |||
46 | def teardown(): |
|
48 | def teardown(): | |
47 | for attr, value in _widget_attrs.items(): |
|
49 | for attr, value in _widget_attrs.items(): | |
48 | if value is undefined: |
|
50 | if value is undefined: | |
49 | delattr(Widget, attr) |
|
51 | delattr(Widget, attr) | |
50 | else: |
|
52 | else: | |
51 | setattr(Widget, attr, value) |
|
53 | setattr(Widget, attr, value) | |
52 |
|
54 | |||
53 | def f(**kwargs): |
|
55 | def f(**kwargs): | |
54 | pass |
|
56 | pass | |
55 |
|
57 | |||
56 | def clear_display(): |
|
58 | def clear_display(): | |
57 | global displayed |
|
59 | global displayed | |
58 | displayed = [] |
|
60 | displayed = [] | |
59 |
|
61 | |||
60 | def record_display(*args): |
|
62 | def record_display(*args): | |
61 | displayed.extend(args) |
|
63 | displayed.extend(args) | |
62 |
|
64 | |||
63 | #----------------------------------------------------------------------------- |
|
65 | #----------------------------------------------------------------------------- | |
64 | # Actual tests |
|
66 | # Actual tests | |
65 | #----------------------------------------------------------------------------- |
|
67 | #----------------------------------------------------------------------------- | |
66 |
|
68 | |||
67 | def check_widget(w, **d): |
|
69 | def check_widget(w, **d): | |
68 | """Check a single widget against a dict""" |
|
70 | """Check a single widget against a dict""" | |
69 | for attr, expected in d.items(): |
|
71 | for attr, expected in d.items(): | |
70 | if attr == 'cls': |
|
72 | if attr == 'cls': | |
71 | nt.assert_is(w.__class__, expected) |
|
73 | nt.assert_is(w.__class__, expected) | |
72 | else: |
|
74 | else: | |
73 | value = getattr(w, attr) |
|
75 | value = getattr(w, attr) | |
74 | nt.assert_equal(value, expected, |
|
76 | nt.assert_equal(value, expected, | |
75 | "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected) |
|
77 | "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected) | |
76 | ) |
|
78 | ) | |
77 |
|
79 | |||
78 | def check_widgets(container, **to_check): |
|
80 | def check_widgets(container, **to_check): | |
79 | """Check that widgets are created as expected""" |
|
81 | """Check that widgets are created as expected""" | |
80 | # build a widget dictionary, so it matches |
|
82 | # build a widget dictionary, so it matches | |
81 | widgets = {} |
|
83 | widgets = {} | |
82 | for w in container.children: |
|
84 | for w in container.children: | |
83 | widgets[w.description] = w |
|
85 | widgets[w.description] = w | |
84 |
|
86 | |||
85 | for key, d in to_check.items(): |
|
87 | for key, d in to_check.items(): | |
86 | nt.assert_in(key, widgets) |
|
88 | nt.assert_in(key, widgets) | |
87 | check_widget(widgets[key], **d) |
|
89 | check_widget(widgets[key], **d) | |
88 |
|
90 | |||
89 |
|
91 | |||
90 | def test_single_value_string(): |
|
92 | def test_single_value_string(): | |
91 | a = u'hello' |
|
93 | a = u'hello' | |
92 | c = interactive(f, a=a) |
|
94 | c = interactive(f, a=a) | |
93 | w = c.children[0] |
|
95 | w = c.children[0] | |
94 | check_widget(w, |
|
96 | check_widget(w, | |
95 | cls=widgets.Text, |
|
97 | cls=widgets.Text, | |
96 | description='a', |
|
98 | description='a', | |
97 | value=a, |
|
99 | value=a, | |
98 | ) |
|
100 | ) | |
99 |
|
101 | |||
100 | def test_single_value_bool(): |
|
102 | def test_single_value_bool(): | |
101 | for a in (True, False): |
|
103 | for a in (True, False): | |
102 | c = interactive(f, a=a) |
|
104 | c = interactive(f, a=a) | |
103 | w = c.children[0] |
|
105 | w = c.children[0] | |
104 | check_widget(w, |
|
106 | check_widget(w, | |
105 | cls=widgets.Checkbox, |
|
107 | cls=widgets.Checkbox, | |
106 | description='a', |
|
108 | description='a', | |
107 | value=a, |
|
109 | value=a, | |
108 | ) |
|
110 | ) | |
109 |
|
111 | |||
110 | def test_single_value_dict(): |
|
112 | def test_single_value_dict(): | |
111 | for d in [ |
|
113 | for d in [ | |
112 | dict(a=5), |
|
114 | dict(a=5), | |
113 | dict(a=5, b='b', c=dict), |
|
115 | dict(a=5, b='b', c=dict), | |
114 | ]: |
|
116 | ]: | |
115 | c = interactive(f, d=d) |
|
117 | c = interactive(f, d=d) | |
116 | w = c.children[0] |
|
118 | w = c.children[0] | |
117 | check_widget(w, |
|
119 | check_widget(w, | |
118 | cls=widgets.Dropdown, |
|
120 | cls=widgets.Dropdown, | |
119 | description='d', |
|
121 | description='d', | |
120 | options=d, |
|
122 | options=d, | |
121 | value=next(iter(d.values())), |
|
123 | value=next(iter(d.values())), | |
122 | ) |
|
124 | ) | |
123 |
|
125 | |||
124 | def test_single_value_float(): |
|
126 | def test_single_value_float(): | |
125 | for a in (2.25, 1.0, -3.5): |
|
127 | for a in (2.25, 1.0, -3.5): | |
126 | c = interactive(f, a=a) |
|
128 | c = interactive(f, a=a) | |
127 | w = c.children[0] |
|
129 | w = c.children[0] | |
128 | check_widget(w, |
|
130 | check_widget(w, | |
129 | cls=widgets.FloatSlider, |
|
131 | cls=widgets.FloatSlider, | |
130 | description='a', |
|
132 | description='a', | |
131 | value=a, |
|
133 | value=a, | |
132 | min= -a if a > 0 else 3*a, |
|
134 | min= -a if a > 0 else 3*a, | |
133 | max= 3*a if a > 0 else -a, |
|
135 | max= 3*a if a > 0 else -a, | |
134 | step=0.1, |
|
136 | step=0.1, | |
135 | readout=True, |
|
137 | readout=True, | |
136 | ) |
|
138 | ) | |
137 |
|
139 | |||
138 | def test_single_value_int(): |
|
140 | def test_single_value_int(): | |
139 | for a in (1, 5, -3): |
|
141 | for a in (1, 5, -3): | |
140 | c = interactive(f, a=a) |
|
142 | c = interactive(f, a=a) | |
141 | nt.assert_equal(len(c.children), 1) |
|
143 | nt.assert_equal(len(c.children), 1) | |
142 | w = c.children[0] |
|
144 | w = c.children[0] | |
143 | check_widget(w, |
|
145 | check_widget(w, | |
144 | cls=widgets.IntSlider, |
|
146 | cls=widgets.IntSlider, | |
145 | description='a', |
|
147 | description='a', | |
146 | value=a, |
|
148 | value=a, | |
147 | min= -a if a > 0 else 3*a, |
|
149 | min= -a if a > 0 else 3*a, | |
148 | max= 3*a if a > 0 else -a, |
|
150 | max= 3*a if a > 0 else -a, | |
149 | step=1, |
|
151 | step=1, | |
150 | readout=True, |
|
152 | readout=True, | |
151 | ) |
|
153 | ) | |
152 |
|
154 | |||
153 | def test_list_tuple_2_int(): |
|
155 | def test_list_tuple_2_int(): | |
154 | with nt.assert_raises(ValueError): |
|
156 | with nt.assert_raises(ValueError): | |
155 | c = interactive(f, tup=(1,1)) |
|
157 | c = interactive(f, tup=(1,1)) | |
156 | with nt.assert_raises(ValueError): |
|
158 | with nt.assert_raises(ValueError): | |
157 | c = interactive(f, tup=(1,-1)) |
|
159 | c = interactive(f, tup=(1,-1)) | |
158 | for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]: |
|
160 | for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]: | |
159 | c = interactive(f, tup=(min, max), lis=[min, max]) |
|
161 | c = interactive(f, tup=(min, max), lis=[min, max]) | |
160 | nt.assert_equal(len(c.children), 2) |
|
162 | nt.assert_equal(len(c.children), 2) | |
161 | d = dict( |
|
163 | d = dict( | |
162 | cls=widgets.IntSlider, |
|
164 | cls=widgets.IntSlider, | |
163 | min=min, |
|
165 | min=min, | |
164 | max=max, |
|
166 | max=max, | |
165 | step=1, |
|
167 | step=1, | |
166 | readout=True, |
|
168 | readout=True, | |
167 | ) |
|
169 | ) | |
168 | check_widgets(c, tup=d, lis=d) |
|
170 | check_widgets(c, tup=d, lis=d) | |
169 |
|
171 | |||
170 | def test_list_tuple_3_int(): |
|
172 | def test_list_tuple_3_int(): | |
171 | with nt.assert_raises(ValueError): |
|
173 | with nt.assert_raises(ValueError): | |
172 | c = interactive(f, tup=(1,2,0)) |
|
174 | c = interactive(f, tup=(1,2,0)) | |
173 | with nt.assert_raises(ValueError): |
|
175 | with nt.assert_raises(ValueError): | |
174 | c = interactive(f, tup=(1,2,-1)) |
|
176 | c = interactive(f, tup=(1,2,-1)) | |
175 | for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]: |
|
177 | for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]: | |
176 | c = interactive(f, tup=(min, max, step), lis=[min, max, step]) |
|
178 | c = interactive(f, tup=(min, max, step), lis=[min, max, step]) | |
177 | nt.assert_equal(len(c.children), 2) |
|
179 | nt.assert_equal(len(c.children), 2) | |
178 | d = dict( |
|
180 | d = dict( | |
179 | cls=widgets.IntSlider, |
|
181 | cls=widgets.IntSlider, | |
180 | min=min, |
|
182 | min=min, | |
181 | max=max, |
|
183 | max=max, | |
182 | step=step, |
|
184 | step=step, | |
183 | readout=True, |
|
185 | readout=True, | |
184 | ) |
|
186 | ) | |
185 | check_widgets(c, tup=d, lis=d) |
|
187 | check_widgets(c, tup=d, lis=d) | |
186 |
|
188 | |||
187 | def test_list_tuple_2_float(): |
|
189 | def test_list_tuple_2_float(): | |
188 | with nt.assert_raises(ValueError): |
|
190 | with nt.assert_raises(ValueError): | |
189 | c = interactive(f, tup=(1.0,1.0)) |
|
191 | c = interactive(f, tup=(1.0,1.0)) | |
190 | with nt.assert_raises(ValueError): |
|
192 | with nt.assert_raises(ValueError): | |
191 | c = interactive(f, tup=(0.5,-0.5)) |
|
193 | c = interactive(f, tup=(0.5,-0.5)) | |
192 | for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]: |
|
194 | for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]: | |
193 | c = interactive(f, tup=(min, max), lis=[min, max]) |
|
195 | c = interactive(f, tup=(min, max), lis=[min, max]) | |
194 | nt.assert_equal(len(c.children), 2) |
|
196 | nt.assert_equal(len(c.children), 2) | |
195 | d = dict( |
|
197 | d = dict( | |
196 | cls=widgets.FloatSlider, |
|
198 | cls=widgets.FloatSlider, | |
197 | min=min, |
|
199 | min=min, | |
198 | max=max, |
|
200 | max=max, | |
199 | step=.1, |
|
201 | step=.1, | |
200 | readout=True, |
|
202 | readout=True, | |
201 | ) |
|
203 | ) | |
202 | check_widgets(c, tup=d, lis=d) |
|
204 | check_widgets(c, tup=d, lis=d) | |
203 |
|
205 | |||
204 | def test_list_tuple_3_float(): |
|
206 | def test_list_tuple_3_float(): | |
205 | with nt.assert_raises(ValueError): |
|
207 | with nt.assert_raises(ValueError): | |
206 | c = interactive(f, tup=(1,2,0.0)) |
|
208 | c = interactive(f, tup=(1,2,0.0)) | |
207 | with nt.assert_raises(ValueError): |
|
209 | with nt.assert_raises(ValueError): | |
208 | c = interactive(f, tup=(-1,-2,1.)) |
|
210 | c = interactive(f, tup=(-1,-2,1.)) | |
209 | with nt.assert_raises(ValueError): |
|
211 | with nt.assert_raises(ValueError): | |
210 | c = interactive(f, tup=(1,2.,-1.)) |
|
212 | c = interactive(f, tup=(1,2.,-1.)) | |
211 | for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]: |
|
213 | for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]: | |
212 | c = interactive(f, tup=(min, max, step), lis=[min, max, step]) |
|
214 | c = interactive(f, tup=(min, max, step), lis=[min, max, step]) | |
213 | nt.assert_equal(len(c.children), 2) |
|
215 | nt.assert_equal(len(c.children), 2) | |
214 | d = dict( |
|
216 | d = dict( | |
215 | cls=widgets.FloatSlider, |
|
217 | cls=widgets.FloatSlider, | |
216 | min=min, |
|
218 | min=min, | |
217 | max=max, |
|
219 | max=max, | |
218 | step=step, |
|
220 | step=step, | |
219 | readout=True, |
|
221 | readout=True, | |
220 | ) |
|
222 | ) | |
221 | check_widgets(c, tup=d, lis=d) |
|
223 | check_widgets(c, tup=d, lis=d) | |
222 |
|
224 | |||
223 | def test_list_tuple_str(): |
|
225 | def test_list_tuple_str(): | |
224 | values = ['hello', 'there', 'guy'] |
|
226 | values = ['hello', 'there', 'guy'] | |
225 | first = values[0] |
|
227 | first = values[0] | |
226 | c = interactive(f, tup=tuple(values), lis=list(values)) |
|
228 | c = interactive(f, tup=tuple(values), lis=list(values)) | |
227 | nt.assert_equal(len(c.children), 2) |
|
229 | nt.assert_equal(len(c.children), 2) | |
228 | d = dict( |
|
230 | d = dict( | |
229 | cls=widgets.Dropdown, |
|
231 | cls=widgets.Dropdown, | |
230 | value=first, |
|
232 | value=first, | |
231 | options=values |
|
233 | options=values | |
232 | ) |
|
234 | ) | |
233 | check_widgets(c, tup=d, lis=d) |
|
235 | check_widgets(c, tup=d, lis=d) | |
234 |
|
236 | |||
235 | def test_list_tuple_invalid(): |
|
237 | def test_list_tuple_invalid(): | |
236 | for bad in [ |
|
238 | for bad in [ | |
237 | (), |
|
239 | (), | |
238 | (5, 'hi'), |
|
240 | (5, 'hi'), | |
239 | ('hi', 5), |
|
241 | ('hi', 5), | |
240 | ({},), |
|
242 | ({},), | |
241 | (None,), |
|
243 | (None,), | |
242 | ]: |
|
244 | ]: | |
243 | with nt.assert_raises(ValueError): |
|
245 | with nt.assert_raises(ValueError): | |
244 | print(bad) # because there is no custom message in assert_raises |
|
246 | print(bad) # because there is no custom message in assert_raises | |
245 | c = interactive(f, tup=bad) |
|
247 | c = interactive(f, tup=bad) | |
246 |
|
248 | |||
247 | def test_defaults(): |
|
249 | def test_defaults(): | |
248 | @annotate(n=10) |
|
250 | @annotate(n=10) | |
249 | def f(n, f=4.5, g=1): |
|
251 | def f(n, f=4.5, g=1): | |
250 | pass |
|
252 | pass | |
251 |
|
253 | |||
252 | c = interactive(f) |
|
254 | c = interactive(f) | |
253 | check_widgets(c, |
|
255 | check_widgets(c, | |
254 | n=dict( |
|
256 | n=dict( | |
255 | cls=widgets.IntSlider, |
|
257 | cls=widgets.IntSlider, | |
256 | value=10, |
|
258 | value=10, | |
257 | ), |
|
259 | ), | |
258 | f=dict( |
|
260 | f=dict( | |
259 | cls=widgets.FloatSlider, |
|
261 | cls=widgets.FloatSlider, | |
260 | value=4.5, |
|
262 | value=4.5, | |
261 | ), |
|
263 | ), | |
262 | g=dict( |
|
264 | g=dict( | |
263 | cls=widgets.IntSlider, |
|
265 | cls=widgets.IntSlider, | |
264 | value=1, |
|
266 | value=1, | |
265 | ), |
|
267 | ), | |
266 | ) |
|
268 | ) | |
267 |
|
269 | |||
268 | def test_default_values(): |
|
270 | def test_default_values(): | |
269 | @annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there']) |
|
271 | @annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there']) | |
270 | def f(n, f=4.5, g=1, h=2, j='there'): |
|
272 | def f(n, f=4.5, g=1, h=2, j='there'): | |
271 | pass |
|
273 | pass | |
272 |
|
274 | |||
273 | c = interactive(f) |
|
275 | c = interactive(f) | |
274 | check_widgets(c, |
|
276 | check_widgets(c, | |
275 | n=dict( |
|
277 | n=dict( | |
276 | cls=widgets.IntSlider, |
|
278 | cls=widgets.IntSlider, | |
277 | value=10, |
|
279 | value=10, | |
278 | ), |
|
280 | ), | |
279 | f=dict( |
|
281 | f=dict( | |
280 | cls=widgets.FloatSlider, |
|
282 | cls=widgets.FloatSlider, | |
281 | value=4.5, |
|
283 | value=4.5, | |
282 | ), |
|
284 | ), | |
283 | g=dict( |
|
285 | g=dict( | |
284 | cls=widgets.IntSlider, |
|
286 | cls=widgets.IntSlider, | |
285 | value=5, |
|
287 | value=5, | |
286 | ), |
|
288 | ), | |
287 | h=dict( |
|
289 | h=dict( | |
288 | cls=widgets.Dropdown, |
|
290 | cls=widgets.Dropdown, | |
289 | options={'a': 1, 'b': 2}, |
|
291 | options={'a': 1, 'b': 2}, | |
290 | value=2 |
|
292 | value=2 | |
291 | ), |
|
293 | ), | |
292 | j=dict( |
|
294 | j=dict( | |
293 | cls=widgets.Dropdown, |
|
295 | cls=widgets.Dropdown, | |
294 | options=['hi', 'there'], |
|
296 | options=['hi', 'there'], | |
295 | value='there' |
|
297 | value='there' | |
296 | ), |
|
298 | ), | |
297 | ) |
|
299 | ) | |
298 |
|
300 | |||
299 | def test_default_out_of_bounds(): |
|
301 | def test_default_out_of_bounds(): | |
300 | @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there']) |
|
302 | @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there']) | |
301 | def f(f='hi', h=5, j='other'): |
|
303 | def f(f='hi', h=5, j='other'): | |
302 | pass |
|
304 | pass | |
303 |
|
305 | |||
304 | c = interactive(f) |
|
306 | c = interactive(f) | |
305 | check_widgets(c, |
|
307 | check_widgets(c, | |
306 | f=dict( |
|
308 | f=dict( | |
307 | cls=widgets.FloatSlider, |
|
309 | cls=widgets.FloatSlider, | |
308 | value=5., |
|
310 | value=5., | |
309 | ), |
|
311 | ), | |
310 | h=dict( |
|
312 | h=dict( | |
311 | cls=widgets.Dropdown, |
|
313 | cls=widgets.Dropdown, | |
312 | options={'a': 1}, |
|
314 | options={'a': 1}, | |
313 | value=1, |
|
315 | value=1, | |
314 | ), |
|
316 | ), | |
315 | j=dict( |
|
317 | j=dict( | |
316 | cls=widgets.Dropdown, |
|
318 | cls=widgets.Dropdown, | |
317 | options=['hi', 'there'], |
|
319 | options=['hi', 'there'], | |
318 | value='hi', |
|
320 | value='hi', | |
319 | ), |
|
321 | ), | |
320 | ) |
|
322 | ) | |
321 |
|
323 | |||
322 | def test_annotations(): |
|
324 | def test_annotations(): | |
323 | @annotate(n=10, f=widgets.FloatText()) |
|
325 | @annotate(n=10, f=widgets.FloatText()) | |
324 | def f(n, f): |
|
326 | def f(n, f): | |
325 | pass |
|
327 | pass | |
326 |
|
328 | |||
327 | c = interactive(f) |
|
329 | c = interactive(f) | |
328 | check_widgets(c, |
|
330 | check_widgets(c, | |
329 | n=dict( |
|
331 | n=dict( | |
330 | cls=widgets.IntSlider, |
|
332 | cls=widgets.IntSlider, | |
331 | value=10, |
|
333 | value=10, | |
332 | ), |
|
334 | ), | |
333 | f=dict( |
|
335 | f=dict( | |
334 | cls=widgets.FloatText, |
|
336 | cls=widgets.FloatText, | |
335 | ), |
|
337 | ), | |
336 | ) |
|
338 | ) | |
337 |
|
339 | |||
338 | def test_priority(): |
|
340 | def test_priority(): | |
339 | @annotate(annotate='annotate', kwarg='annotate') |
|
341 | @annotate(annotate='annotate', kwarg='annotate') | |
340 | def f(kwarg='default', annotate='default', default='default'): |
|
342 | def f(kwarg='default', annotate='default', default='default'): | |
341 | pass |
|
343 | pass | |
342 |
|
344 | |||
343 | c = interactive(f, kwarg='kwarg') |
|
345 | c = interactive(f, kwarg='kwarg') | |
344 | check_widgets(c, |
|
346 | check_widgets(c, | |
345 | kwarg=dict( |
|
347 | kwarg=dict( | |
346 | cls=widgets.Text, |
|
348 | cls=widgets.Text, | |
347 | value='kwarg', |
|
349 | value='kwarg', | |
348 | ), |
|
350 | ), | |
349 | annotate=dict( |
|
351 | annotate=dict( | |
350 | cls=widgets.Text, |
|
352 | cls=widgets.Text, | |
351 | value='annotate', |
|
353 | value='annotate', | |
352 | ), |
|
354 | ), | |
353 | ) |
|
355 | ) | |
354 |
|
356 | |||
355 | @nt.with_setup(clear_display) |
|
357 | @nt.with_setup(clear_display) | |
356 | def test_decorator_kwarg(): |
|
358 | def test_decorator_kwarg(): | |
357 |
with |
|
359 | with patch.object(interaction, 'display', record_display): | |
358 | @interact(a=5) |
|
360 | @interact(a=5) | |
359 | def foo(a): |
|
361 | def foo(a): | |
360 | pass |
|
362 | pass | |
361 | nt.assert_equal(len(displayed), 1) |
|
363 | nt.assert_equal(len(displayed), 1) | |
362 | w = displayed[0].children[0] |
|
364 | w = displayed[0].children[0] | |
363 | check_widget(w, |
|
365 | check_widget(w, | |
364 | cls=widgets.IntSlider, |
|
366 | cls=widgets.IntSlider, | |
365 | value=5, |
|
367 | value=5, | |
366 | ) |
|
368 | ) | |
367 |
|
369 | |||
368 | @nt.with_setup(clear_display) |
|
370 | @nt.with_setup(clear_display) | |
369 | def test_interact_instancemethod(): |
|
371 | def test_interact_instancemethod(): | |
370 | class Foo(object): |
|
372 | class Foo(object): | |
371 | def show(self, x): |
|
373 | def show(self, x): | |
372 | print(x) |
|
374 | print(x) | |
373 |
|
375 | |||
374 | f = Foo() |
|
376 | f = Foo() | |
375 |
|
377 | |||
376 |
with |
|
378 | with patch.object(interaction, 'display', record_display): | |
377 | g = interact(f.show, x=(1,10)) |
|
379 | g = interact(f.show, x=(1,10)) | |
378 | nt.assert_equal(len(displayed), 1) |
|
380 | nt.assert_equal(len(displayed), 1) | |
379 | w = displayed[0].children[0] |
|
381 | w = displayed[0].children[0] | |
380 | check_widget(w, |
|
382 | check_widget(w, | |
381 | cls=widgets.IntSlider, |
|
383 | cls=widgets.IntSlider, | |
382 | value=5, |
|
384 | value=5, | |
383 | ) |
|
385 | ) | |
384 |
|
386 | |||
385 | @nt.with_setup(clear_display) |
|
387 | @nt.with_setup(clear_display) | |
386 | def test_decorator_no_call(): |
|
388 | def test_decorator_no_call(): | |
387 |
with |
|
389 | with patch.object(interaction, 'display', record_display): | |
388 | @interact |
|
390 | @interact | |
389 | def foo(a='default'): |
|
391 | def foo(a='default'): | |
390 | pass |
|
392 | pass | |
391 | nt.assert_equal(len(displayed), 1) |
|
393 | nt.assert_equal(len(displayed), 1) | |
392 | w = displayed[0].children[0] |
|
394 | w = displayed[0].children[0] | |
393 | check_widget(w, |
|
395 | check_widget(w, | |
394 | cls=widgets.Text, |
|
396 | cls=widgets.Text, | |
395 | value='default', |
|
397 | value='default', | |
396 | ) |
|
398 | ) | |
397 |
|
399 | |||
398 | @nt.with_setup(clear_display) |
|
400 | @nt.with_setup(clear_display) | |
399 | def test_call_interact(): |
|
401 | def test_call_interact(): | |
400 | def foo(a='default'): |
|
402 | def foo(a='default'): | |
401 | pass |
|
403 | pass | |
402 |
with |
|
404 | with patch.object(interaction, 'display', record_display): | |
403 | ifoo = interact(foo) |
|
405 | ifoo = interact(foo) | |
404 | nt.assert_equal(len(displayed), 1) |
|
406 | nt.assert_equal(len(displayed), 1) | |
405 | w = displayed[0].children[0] |
|
407 | w = displayed[0].children[0] | |
406 | check_widget(w, |
|
408 | check_widget(w, | |
407 | cls=widgets.Text, |
|
409 | cls=widgets.Text, | |
408 | value='default', |
|
410 | value='default', | |
409 | ) |
|
411 | ) | |
410 |
|
412 | |||
411 | @nt.with_setup(clear_display) |
|
413 | @nt.with_setup(clear_display) | |
412 | def test_call_interact_kwargs(): |
|
414 | def test_call_interact_kwargs(): | |
413 | def foo(a='default'): |
|
415 | def foo(a='default'): | |
414 | pass |
|
416 | pass | |
415 |
with |
|
417 | with patch.object(interaction, 'display', record_display): | |
416 | ifoo = interact(foo, a=10) |
|
418 | ifoo = interact(foo, a=10) | |
417 | nt.assert_equal(len(displayed), 1) |
|
419 | nt.assert_equal(len(displayed), 1) | |
418 | w = displayed[0].children[0] |
|
420 | w = displayed[0].children[0] | |
419 | check_widget(w, |
|
421 | check_widget(w, | |
420 | cls=widgets.IntSlider, |
|
422 | cls=widgets.IntSlider, | |
421 | value=10, |
|
423 | value=10, | |
422 | ) |
|
424 | ) | |
423 |
|
425 | |||
424 | @nt.with_setup(clear_display) |
|
426 | @nt.with_setup(clear_display) | |
425 | def test_call_decorated_on_trait_change(): |
|
427 | def test_call_decorated_on_trait_change(): | |
426 | """test calling @interact decorated functions""" |
|
428 | """test calling @interact decorated functions""" | |
427 | d = {} |
|
429 | d = {} | |
428 |
with |
|
430 | with patch.object(interaction, 'display', record_display): | |
429 | @interact |
|
431 | @interact | |
430 | def foo(a='default'): |
|
432 | def foo(a='default'): | |
431 | d['a'] = a |
|
433 | d['a'] = a | |
432 | return a |
|
434 | return a | |
433 | nt.assert_equal(len(displayed), 1) |
|
435 | nt.assert_equal(len(displayed), 1) | |
434 | w = displayed[0].children[0] |
|
436 | w = displayed[0].children[0] | |
435 | check_widget(w, |
|
437 | check_widget(w, | |
436 | cls=widgets.Text, |
|
438 | cls=widgets.Text, | |
437 | value='default', |
|
439 | value='default', | |
438 | ) |
|
440 | ) | |
439 | # test calling the function directly |
|
441 | # test calling the function directly | |
440 | a = foo('hello') |
|
442 | a = foo('hello') | |
441 | nt.assert_equal(a, 'hello') |
|
443 | nt.assert_equal(a, 'hello') | |
442 | nt.assert_equal(d['a'], 'hello') |
|
444 | nt.assert_equal(d['a'], 'hello') | |
443 |
|
445 | |||
444 | # test that setting trait values calls the function |
|
446 | # test that setting trait values calls the function | |
445 | w.value = 'called' |
|
447 | w.value = 'called' | |
446 | nt.assert_equal(d['a'], 'called') |
|
448 | nt.assert_equal(d['a'], 'called') | |
447 |
|
449 | |||
448 | @nt.with_setup(clear_display) |
|
450 | @nt.with_setup(clear_display) | |
449 | def test_call_decorated_kwargs_on_trait_change(): |
|
451 | def test_call_decorated_kwargs_on_trait_change(): | |
450 | """test calling @interact(foo=bar) decorated functions""" |
|
452 | """test calling @interact(foo=bar) decorated functions""" | |
451 | d = {} |
|
453 | d = {} | |
452 |
with |
|
454 | with patch.object(interaction, 'display', record_display): | |
453 | @interact(a='kwarg') |
|
455 | @interact(a='kwarg') | |
454 | def foo(a='default'): |
|
456 | def foo(a='default'): | |
455 | d['a'] = a |
|
457 | d['a'] = a | |
456 | return a |
|
458 | return a | |
457 | nt.assert_equal(len(displayed), 1) |
|
459 | nt.assert_equal(len(displayed), 1) | |
458 | w = displayed[0].children[0] |
|
460 | w = displayed[0].children[0] | |
459 | check_widget(w, |
|
461 | check_widget(w, | |
460 | cls=widgets.Text, |
|
462 | cls=widgets.Text, | |
461 | value='kwarg', |
|
463 | value='kwarg', | |
462 | ) |
|
464 | ) | |
463 | # test calling the function directly |
|
465 | # test calling the function directly | |
464 | a = foo('hello') |
|
466 | a = foo('hello') | |
465 | nt.assert_equal(a, 'hello') |
|
467 | nt.assert_equal(a, 'hello') | |
466 | nt.assert_equal(d['a'], 'hello') |
|
468 | nt.assert_equal(d['a'], 'hello') | |
467 |
|
469 | |||
468 | # test that setting trait values calls the function |
|
470 | # test that setting trait values calls the function | |
469 | w.value = 'called' |
|
471 | w.value = 'called' | |
470 | nt.assert_equal(d['a'], 'called') |
|
472 | nt.assert_equal(d['a'], 'called') | |
471 |
|
473 | |||
472 | def test_fixed(): |
|
474 | def test_fixed(): | |
473 | c = interactive(f, a=widgets.fixed(5), b='text') |
|
475 | c = interactive(f, a=widgets.fixed(5), b='text') | |
474 | nt.assert_equal(len(c.children), 1) |
|
476 | nt.assert_equal(len(c.children), 1) | |
475 | w = c.children[0] |
|
477 | w = c.children[0] | |
476 | check_widget(w, |
|
478 | check_widget(w, | |
477 | cls=widgets.Text, |
|
479 | cls=widgets.Text, | |
478 | value='text', |
|
480 | value='text', | |
479 | description='b', |
|
481 | description='b', | |
480 | ) |
|
482 | ) | |
481 |
|
483 | |||
482 | def test_default_description(): |
|
484 | def test_default_description(): | |
483 | c = interactive(f, b='text') |
|
485 | c = interactive(f, b='text') | |
484 | w = c.children[0] |
|
486 | w = c.children[0] | |
485 | check_widget(w, |
|
487 | check_widget(w, | |
486 | cls=widgets.Text, |
|
488 | cls=widgets.Text, | |
487 | value='text', |
|
489 | value='text', | |
488 | description='b', |
|
490 | description='b', | |
489 | ) |
|
491 | ) | |
490 |
|
492 | |||
491 | def test_custom_description(): |
|
493 | def test_custom_description(): | |
492 | d = {} |
|
494 | d = {} | |
493 | def record_kwargs(**kwargs): |
|
495 | def record_kwargs(**kwargs): | |
494 | d.clear() |
|
496 | d.clear() | |
495 | d.update(kwargs) |
|
497 | d.update(kwargs) | |
496 |
|
498 | |||
497 | c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo')) |
|
499 | c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo')) | |
498 | w = c.children[0] |
|
500 | w = c.children[0] | |
499 | check_widget(w, |
|
501 | check_widget(w, | |
500 | cls=widgets.Text, |
|
502 | cls=widgets.Text, | |
501 | value='text', |
|
503 | value='text', | |
502 | description='foo', |
|
504 | description='foo', | |
503 | ) |
|
505 | ) | |
504 | w.value = 'different text' |
|
506 | w.value = 'different text' | |
505 | nt.assert_equal(d, {'b': 'different text'}) |
|
507 | nt.assert_equal(d, {'b': 'different text'}) | |
506 |
|
508 | |||
507 | def test_interact_manual_button(): |
|
509 | def test_interact_manual_button(): | |
508 | c = interactive(f, __manual=True) |
|
510 | c = interactive(f, __manual=True) | |
509 | w = c.children[0] |
|
511 | w = c.children[0] | |
510 | check_widget(w, cls=widgets.Button) |
|
512 | check_widget(w, cls=widgets.Button) | |
511 |
|
513 | |||
512 | def test_interact_manual_nocall(): |
|
514 | def test_interact_manual_nocall(): | |
513 | callcount = 0 |
|
515 | callcount = 0 | |
514 | def calltest(testarg): |
|
516 | def calltest(testarg): | |
515 | callcount += 1 |
|
517 | callcount += 1 | |
516 | c = interactive(calltest, testarg=5, __manual=True) |
|
518 | c = interactive(calltest, testarg=5, __manual=True) | |
517 | c.children[0].value = 10 |
|
519 | c.children[0].value = 10 | |
518 | nt.assert_equal(callcount, 0) |
|
520 | nt.assert_equal(callcount, 0) | |
519 |
|
521 | |||
520 | def test_int_range_logic(): |
|
522 | def test_int_range_logic(): | |
521 | irsw = widgets.IntRangeSlider |
|
523 | irsw = widgets.IntRangeSlider | |
522 | w = irsw(value=(2, 4), min=0, max=6) |
|
524 | w = irsw(value=(2, 4), min=0, max=6) | |
523 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) |
|
525 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |
524 | w.value = (4, 2) |
|
526 | w.value = (4, 2) | |
525 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) |
|
527 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |
526 | w.value = (-1, 7) |
|
528 | w.value = (-1, 7) | |
527 | check_widget(w, cls=irsw, value=(0, 6), min=0, max=6) |
|
529 | check_widget(w, cls=irsw, value=(0, 6), min=0, max=6) | |
528 | w.min = 3 |
|
530 | w.min = 3 | |
529 | check_widget(w, cls=irsw, value=(3, 6), min=3, max=6) |
|
531 | check_widget(w, cls=irsw, value=(3, 6), min=3, max=6) | |
530 | w.max = 3 |
|
532 | w.max = 3 | |
531 | check_widget(w, cls=irsw, value=(3, 3), min=3, max=3) |
|
533 | check_widget(w, cls=irsw, value=(3, 3), min=3, max=3) | |
532 |
|
534 | |||
533 | w.min = 0 |
|
535 | w.min = 0 | |
534 | w.max = 6 |
|
536 | w.max = 6 | |
535 | w.lower = 2 |
|
537 | w.lower = 2 | |
536 | w.upper = 4 |
|
538 | w.upper = 4 | |
537 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) |
|
539 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |
538 | w.value = (0, 1) #lower non-overlapping range |
|
540 | w.value = (0, 1) #lower non-overlapping range | |
539 | check_widget(w, cls=irsw, value=(0, 1), min=0, max=6) |
|
541 | check_widget(w, cls=irsw, value=(0, 1), min=0, max=6) | |
540 | w.value = (5, 6) #upper non-overlapping range |
|
542 | w.value = (5, 6) #upper non-overlapping range | |
541 | check_widget(w, cls=irsw, value=(5, 6), min=0, max=6) |
|
543 | check_widget(w, cls=irsw, value=(5, 6), min=0, max=6) | |
542 | w.value = (-1, 4) #semi out-of-range |
|
544 | w.value = (-1, 4) #semi out-of-range | |
543 | check_widget(w, cls=irsw, value=(0, 4), min=0, max=6) |
|
545 | check_widget(w, cls=irsw, value=(0, 4), min=0, max=6) | |
544 | w.lower = 2 |
|
546 | w.lower = 2 | |
545 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) |
|
547 | check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |
546 | w.value = (-2, -1) #wholly out of range |
|
548 | w.value = (-2, -1) #wholly out of range | |
547 | check_widget(w, cls=irsw, value=(0, 0), min=0, max=6) |
|
549 | check_widget(w, cls=irsw, value=(0, 0), min=0, max=6) | |
548 | w.value = (7, 8) |
|
550 | w.value = (7, 8) | |
549 | check_widget(w, cls=irsw, value=(6, 6), min=0, max=6) |
|
551 | check_widget(w, cls=irsw, value=(6, 6), min=0, max=6) | |
550 |
|
552 | |||
551 | with nt.assert_raises(ValueError): |
|
553 | with nt.assert_raises(ValueError): | |
552 | w.min = 7 |
|
554 | w.min = 7 | |
553 | with nt.assert_raises(ValueError): |
|
555 | with nt.assert_raises(ValueError): | |
554 | w.max = -1 |
|
556 | w.max = -1 | |
555 | with nt.assert_raises(ValueError): |
|
557 | with nt.assert_raises(ValueError): | |
556 | w.lower = 5 |
|
558 | w.lower = 5 | |
557 | with nt.assert_raises(ValueError): |
|
559 | with nt.assert_raises(ValueError): | |
558 | w.upper = 1 |
|
560 | w.upper = 1 | |
559 |
|
561 | |||
560 | w = irsw(min=2, max=3) |
|
562 | w = irsw(min=2, max=3) | |
561 | check_widget(w, min=2, max=3) |
|
563 | check_widget(w, min=2, max=3) | |
562 | w = irsw(min=100, max=200) |
|
564 | w = irsw(min=100, max=200) | |
563 | check_widget(w, lower=125, upper=175, value=(125, 175)) |
|
565 | check_widget(w, lower=125, upper=175, value=(125, 175)) | |
564 |
|
566 | |||
565 | with nt.assert_raises(ValueError): |
|
567 | with nt.assert_raises(ValueError): | |
566 | irsw(value=(2, 4), lower=3) |
|
568 | irsw(value=(2, 4), lower=3) | |
567 | with nt.assert_raises(ValueError): |
|
569 | with nt.assert_raises(ValueError): | |
568 | irsw(value=(2, 4), upper=3) |
|
570 | irsw(value=(2, 4), upper=3) | |
569 | with nt.assert_raises(ValueError): |
|
571 | with nt.assert_raises(ValueError): | |
570 | irsw(value=(2, 4), lower=3, upper=3) |
|
572 | irsw(value=(2, 4), lower=3, upper=3) | |
571 | with nt.assert_raises(ValueError): |
|
573 | with nt.assert_raises(ValueError): | |
572 | irsw(min=2, max=1) |
|
574 | irsw(min=2, max=1) | |
573 | with nt.assert_raises(ValueError): |
|
575 | with nt.assert_raises(ValueError): | |
574 | irsw(lower=5) |
|
576 | irsw(lower=5) | |
575 | with nt.assert_raises(ValueError): |
|
577 | with nt.assert_raises(ValueError): | |
576 | irsw(upper=5) |
|
578 | irsw(upper=5) | |
577 |
|
579 | |||
578 |
|
580 | |||
579 | def test_float_range_logic(): |
|
581 | def test_float_range_logic(): | |
580 | frsw = widgets.FloatRangeSlider |
|
582 | frsw = widgets.FloatRangeSlider | |
581 | w = frsw(value=(.2, .4), min=0., max=.6) |
|
583 | w = frsw(value=(.2, .4), min=0., max=.6) | |
582 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) |
|
584 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |
583 | w.value = (.4, .2) |
|
585 | w.value = (.4, .2) | |
584 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) |
|
586 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |
585 | w.value = (-.1, .7) |
|
587 | w.value = (-.1, .7) | |
586 | check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6) |
|
588 | check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6) | |
587 | w.min = .3 |
|
589 | w.min = .3 | |
588 | check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6) |
|
590 | check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6) | |
589 | w.max = .3 |
|
591 | w.max = .3 | |
590 | check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3) |
|
592 | check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3) | |
591 |
|
593 | |||
592 | w.min = 0. |
|
594 | w.min = 0. | |
593 | w.max = .6 |
|
595 | w.max = .6 | |
594 | w.lower = .2 |
|
596 | w.lower = .2 | |
595 | w.upper = .4 |
|
597 | w.upper = .4 | |
596 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) |
|
598 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |
597 | w.value = (0., .1) #lower non-overlapping range |
|
599 | w.value = (0., .1) #lower non-overlapping range | |
598 | check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6) |
|
600 | check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6) | |
599 | w.value = (.5, .6) #upper non-overlapping range |
|
601 | w.value = (.5, .6) #upper non-overlapping range | |
600 | check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6) |
|
602 | check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6) | |
601 | w.value = (-.1, .4) #semi out-of-range |
|
603 | w.value = (-.1, .4) #semi out-of-range | |
602 | check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6) |
|
604 | check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6) | |
603 | w.lower = .2 |
|
605 | w.lower = .2 | |
604 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) |
|
606 | check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |
605 | w.value = (-.2, -.1) #wholly out of range |
|
607 | w.value = (-.2, -.1) #wholly out of range | |
606 | check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6) |
|
608 | check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6) | |
607 | w.value = (.7, .8) |
|
609 | w.value = (.7, .8) | |
608 | check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6) |
|
610 | check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6) | |
609 |
|
611 | |||
610 | with nt.assert_raises(ValueError): |
|
612 | with nt.assert_raises(ValueError): | |
611 | w.min = .7 |
|
613 | w.min = .7 | |
612 | with nt.assert_raises(ValueError): |
|
614 | with nt.assert_raises(ValueError): | |
613 | w.max = -.1 |
|
615 | w.max = -.1 | |
614 | with nt.assert_raises(ValueError): |
|
616 | with nt.assert_raises(ValueError): | |
615 | w.lower = .5 |
|
617 | w.lower = .5 | |
616 | with nt.assert_raises(ValueError): |
|
618 | with nt.assert_raises(ValueError): | |
617 | w.upper = .1 |
|
619 | w.upper = .1 | |
618 |
|
620 | |||
619 | w = frsw(min=2, max=3) |
|
621 | w = frsw(min=2, max=3) | |
620 | check_widget(w, min=2, max=3) |
|
622 | check_widget(w, min=2, max=3) | |
621 | w = frsw(min=1., max=2.) |
|
623 | w = frsw(min=1., max=2.) | |
622 | check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75)) |
|
624 | check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75)) | |
623 |
|
625 | |||
624 | with nt.assert_raises(ValueError): |
|
626 | with nt.assert_raises(ValueError): | |
625 | frsw(value=(2, 4), lower=3) |
|
627 | frsw(value=(2, 4), lower=3) | |
626 | with nt.assert_raises(ValueError): |
|
628 | with nt.assert_raises(ValueError): | |
627 | frsw(value=(2, 4), upper=3) |
|
629 | frsw(value=(2, 4), upper=3) | |
628 | with nt.assert_raises(ValueError): |
|
630 | with nt.assert_raises(ValueError): | |
629 | frsw(value=(2, 4), lower=3, upper=3) |
|
631 | frsw(value=(2, 4), lower=3, upper=3) | |
630 | with nt.assert_raises(ValueError): |
|
632 | with nt.assert_raises(ValueError): | |
631 | frsw(min=.2, max=.1) |
|
633 | frsw(min=.2, max=.1) | |
632 | with nt.assert_raises(ValueError): |
|
634 | with nt.assert_raises(ValueError): | |
633 | frsw(lower=5) |
|
635 | frsw(lower=5) | |
634 | with nt.assert_raises(ValueError): |
|
636 | with nt.assert_raises(ValueError): | |
635 | frsw(upper=5) |
|
637 | frsw(upper=5) | |
636 |
|
638 | |||
637 |
|
639 | |||
638 | def test_multiple_selection(): |
|
640 | def test_multiple_selection(): | |
639 | smw = widgets.SelectMultiple |
|
641 | smw = widgets.SelectMultiple | |
640 |
|
642 | |||
641 | # degenerate multiple select |
|
643 | # degenerate multiple select | |
642 | w = smw() |
|
644 | w = smw() | |
643 | check_widget(w, value=tuple(), options=None, selected_labels=tuple()) |
|
645 | check_widget(w, value=tuple(), options=None, selected_labels=tuple()) | |
644 |
|
646 | |||
645 | # don't accept random other value when no options |
|
647 | # don't accept random other value when no options | |
646 | with nt.assert_raises(KeyError): |
|
648 | with nt.assert_raises(KeyError): | |
647 | w.value = (2,) |
|
649 | w.value = (2,) | |
648 | check_widget(w, value=tuple(), selected_labels=tuple()) |
|
650 | check_widget(w, value=tuple(), selected_labels=tuple()) | |
649 |
|
651 | |||
650 | # basic multiple select |
|
652 | # basic multiple select | |
651 | w = smw(options=[(1, 1)], value=[1]) |
|
653 | w = smw(options=[(1, 1)], value=[1]) | |
652 | check_widget(w, cls=smw, value=(1,), options=[(1, 1)]) |
|
654 | check_widget(w, cls=smw, value=(1,), options=[(1, 1)]) | |
653 |
|
655 | |||
654 | # don't accept random other value |
|
656 | # don't accept random other value | |
655 | with nt.assert_raises(KeyError): |
|
657 | with nt.assert_raises(KeyError): | |
656 | w.value = w.value + (2,) |
|
658 | w.value = w.value + (2,) | |
657 | check_widget(w, value=(1,), selected_labels=(1,)) |
|
659 | check_widget(w, value=(1,), selected_labels=(1,)) | |
658 |
|
660 | |||
659 | # change options |
|
661 | # change options | |
660 | w.options = w.options + [(2, 2)] |
|
662 | w.options = w.options + [(2, 2)] | |
661 | check_widget(w, options=[(1, 1), (2,2)]) |
|
663 | check_widget(w, options=[(1, 1), (2,2)]) | |
662 |
|
664 | |||
663 | # change value |
|
665 | # change value | |
664 | w.value = w.value + (2,) |
|
666 | w.value = w.value + (2,) | |
665 | check_widget(w, value=(1, 2), selected_labels=(1, 2)) |
|
667 | check_widget(w, value=(1, 2), selected_labels=(1, 2)) | |
666 |
|
668 | |||
667 | # change value name |
|
669 | # change value name | |
668 | w.selected_labels = (1,) |
|
670 | w.selected_labels = (1,) | |
669 | check_widget(w, value=(1,)) |
|
671 | check_widget(w, value=(1,)) | |
670 |
|
672 | |||
671 | # don't accept random other names when no options |
|
673 | # don't accept random other names when no options | |
672 | with nt.assert_raises(KeyError): |
|
674 | with nt.assert_raises(KeyError): | |
673 | w.selected_labels = (3,) |
|
675 | w.selected_labels = (3,) | |
674 | check_widget(w, value=(1,)) |
|
676 | check_widget(w, value=(1,)) | |
675 |
|
677 | |||
676 | # don't accept selected_label (from superclass) |
|
678 | # don't accept selected_label (from superclass) | |
677 | with nt.assert_raises(AttributeError): |
|
679 | with nt.assert_raises(AttributeError): | |
678 | w.selected_label = 3 |
|
680 | w.selected_label = 3 | |
679 |
|
681 | |||
680 | # don't return selected_label (from superclass) |
|
682 | # don't return selected_label (from superclass) | |
681 | with nt.assert_raises(AttributeError): |
|
683 | with nt.assert_raises(AttributeError): | |
682 | print(w.selected_label) |
|
684 | print(w.selected_label) | |
683 |
|
685 | |||
684 | # dict style |
|
686 | # dict style | |
685 | w.options = {1: 1} |
|
687 | w.options = {1: 1} | |
686 | check_widget(w, options={1: 1}) |
|
688 | check_widget(w, options={1: 1}) | |
687 |
|
689 | |||
688 | # updating |
|
690 | # updating | |
689 | with nt.assert_raises(KeyError): |
|
691 | with nt.assert_raises(KeyError): | |
690 | w.value = (2,) |
|
692 | w.value = (2,) | |
691 | check_widget(w, options={1: 1}) |
|
693 | check_widget(w, options={1: 1}) |
@@ -1,139 +1,139 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """Tests for IPython.utils.path.py""" |
|
2 | """Tests for IPython.utils.path.py""" | |
3 |
|
3 | |||
4 | #----------------------------------------------------------------------------- |
|
4 | # Copyright (c) IPython Development Team. | |
5 | # Copyright (C) 2008-2011 The IPython Development Team |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 | # |
|
6 | ||
7 | # Distributed under the terms of the BSD License. The full license is in |
|
7 | try: | |
8 | # the file COPYING, distributed as part of this software. |
|
8 | from unittest.mock import patch | |
9 | #----------------------------------------------------------------------------- |
|
9 | except ImportError: | |
|
10 | from mock import patch | |||
10 |
|
11 | |||
11 | import nose.tools as nt |
|
12 | import nose.tools as nt | |
12 |
|
13 | |||
13 | from IPython.lib import latextools |
|
14 | from IPython.lib import latextools | |
14 | from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib |
|
15 | from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib | |
15 | from IPython.testing.tools import monkeypatch |
|
|||
16 | from IPython.utils.process import FindCmdError |
|
16 | from IPython.utils.process import FindCmdError | |
17 |
|
17 | |||
18 |
|
18 | |||
19 | def test_latex_to_png_dvipng_fails_when_no_cmd(): |
|
19 | def test_latex_to_png_dvipng_fails_when_no_cmd(): | |
20 | """ |
|
20 | """ | |
21 | `latex_to_png_dvipng` should return None when there is no required command |
|
21 | `latex_to_png_dvipng` should return None when there is no required command | |
22 | """ |
|
22 | """ | |
23 | for command in ['latex', 'dvipng']: |
|
23 | for command in ['latex', 'dvipng']: | |
24 | yield (check_latex_to_png_dvipng_fails_when_no_cmd, command) |
|
24 | yield (check_latex_to_png_dvipng_fails_when_no_cmd, command) | |
25 |
|
25 | |||
26 |
|
26 | |||
27 | def check_latex_to_png_dvipng_fails_when_no_cmd(command): |
|
27 | def check_latex_to_png_dvipng_fails_when_no_cmd(command): | |
28 | def mock_find_cmd(arg): |
|
28 | def mock_find_cmd(arg): | |
29 | if arg == command: |
|
29 | if arg == command: | |
30 | raise FindCmdError |
|
30 | raise FindCmdError | |
31 |
|
31 | |||
32 |
with |
|
32 | with patch.object(latextools, "find_cmd", mock_find_cmd): | |
33 | nt.assert_equals(latextools.latex_to_png_dvipng("whatever", True), |
|
33 | nt.assert_equals(latextools.latex_to_png_dvipng("whatever", True), | |
34 | None) |
|
34 | None) | |
35 |
|
35 | |||
36 |
|
36 | |||
37 | @onlyif_cmds_exist('latex', 'dvipng') |
|
37 | @onlyif_cmds_exist('latex', 'dvipng') | |
38 | def test_latex_to_png_dvipng_runs(): |
|
38 | def test_latex_to_png_dvipng_runs(): | |
39 | """ |
|
39 | """ | |
40 | Test that latex_to_png_dvipng just runs without error. |
|
40 | Test that latex_to_png_dvipng just runs without error. | |
41 | """ |
|
41 | """ | |
42 | def mock_kpsewhich(filename): |
|
42 | def mock_kpsewhich(filename): | |
43 | nt.assert_equals(filename, "breqn.sty") |
|
43 | nt.assert_equals(filename, "breqn.sty") | |
44 | return None |
|
44 | return None | |
45 |
|
45 | |||
46 | for (s, wrap) in [(u"$$x^2$$", False), (u"x^2", True)]: |
|
46 | for (s, wrap) in [(u"$$x^2$$", False), (u"x^2", True)]: | |
47 | yield (latextools.latex_to_png_dvipng, s, wrap) |
|
47 | yield (latextools.latex_to_png_dvipng, s, wrap) | |
48 |
|
48 | |||
49 |
with |
|
49 | with patch.object(latextools, "kpsewhich", mock_kpsewhich): | |
50 | yield (latextools.latex_to_png_dvipng, s, wrap) |
|
50 | yield (latextools.latex_to_png_dvipng, s, wrap) | |
51 |
|
51 | |||
52 | @skipif_not_matplotlib |
|
52 | @skipif_not_matplotlib | |
53 | def test_latex_to_png_mpl_runs(): |
|
53 | def test_latex_to_png_mpl_runs(): | |
54 | """ |
|
54 | """ | |
55 | Test that latex_to_png_mpl just runs without error. |
|
55 | Test that latex_to_png_mpl just runs without error. | |
56 | """ |
|
56 | """ | |
57 | def mock_kpsewhich(filename): |
|
57 | def mock_kpsewhich(filename): | |
58 | nt.assert_equals(filename, "breqn.sty") |
|
58 | nt.assert_equals(filename, "breqn.sty") | |
59 | return None |
|
59 | return None | |
60 |
|
60 | |||
61 | for (s, wrap) in [("$x^2$", False), ("x^2", True)]: |
|
61 | for (s, wrap) in [("$x^2$", False), ("x^2", True)]: | |
62 | yield (latextools.latex_to_png_mpl, s, wrap) |
|
62 | yield (latextools.latex_to_png_mpl, s, wrap) | |
63 |
|
63 | |||
64 |
with |
|
64 | with patch.object(latextools, "kpsewhich", mock_kpsewhich): | |
65 | yield (latextools.latex_to_png_mpl, s, wrap) |
|
65 | yield (latextools.latex_to_png_mpl, s, wrap) | |
66 |
|
66 | |||
67 | @skipif_not_matplotlib |
|
67 | @skipif_not_matplotlib | |
68 | def test_latex_to_html(): |
|
68 | def test_latex_to_html(): | |
69 | img = latextools.latex_to_html("$x^2$") |
|
69 | img = latextools.latex_to_html("$x^2$") | |
70 | nt.assert_in("data:image/png;base64,iVBOR", img) |
|
70 | nt.assert_in("data:image/png;base64,iVBOR", img) | |
71 |
|
71 | |||
72 |
|
72 | |||
73 | def test_genelatex_no_wrap(): |
|
73 | def test_genelatex_no_wrap(): | |
74 | """ |
|
74 | """ | |
75 | Test genelatex with wrap=False. |
|
75 | Test genelatex with wrap=False. | |
76 | """ |
|
76 | """ | |
77 | def mock_kpsewhich(filename): |
|
77 | def mock_kpsewhich(filename): | |
78 | assert False, ("kpsewhich should not be called " |
|
78 | assert False, ("kpsewhich should not be called " | |
79 | "(called with {0})".format(filename)) |
|
79 | "(called with {0})".format(filename)) | |
80 |
|
80 | |||
81 |
with |
|
81 | with patch.object(latextools, "kpsewhich", mock_kpsewhich): | |
82 | nt.assert_equals( |
|
82 | nt.assert_equals( | |
83 | '\n'.join(latextools.genelatex("body text", False)), |
|
83 | '\n'.join(latextools.genelatex("body text", False)), | |
84 | r'''\documentclass{article} |
|
84 | r'''\documentclass{article} | |
85 | \usepackage{amsmath} |
|
85 | \usepackage{amsmath} | |
86 | \usepackage{amsthm} |
|
86 | \usepackage{amsthm} | |
87 | \usepackage{amssymb} |
|
87 | \usepackage{amssymb} | |
88 | \usepackage{bm} |
|
88 | \usepackage{bm} | |
89 | \pagestyle{empty} |
|
89 | \pagestyle{empty} | |
90 | \begin{document} |
|
90 | \begin{document} | |
91 | body text |
|
91 | body text | |
92 | \end{document}''') |
|
92 | \end{document}''') | |
93 |
|
93 | |||
94 |
|
94 | |||
95 | def test_genelatex_wrap_with_breqn(): |
|
95 | def test_genelatex_wrap_with_breqn(): | |
96 | """ |
|
96 | """ | |
97 | Test genelatex with wrap=True for the case breqn.sty is installed. |
|
97 | Test genelatex with wrap=True for the case breqn.sty is installed. | |
98 | """ |
|
98 | """ | |
99 | def mock_kpsewhich(filename): |
|
99 | def mock_kpsewhich(filename): | |
100 | nt.assert_equals(filename, "breqn.sty") |
|
100 | nt.assert_equals(filename, "breqn.sty") | |
101 | return "path/to/breqn.sty" |
|
101 | return "path/to/breqn.sty" | |
102 |
|
102 | |||
103 |
with |
|
103 | with patch.object(latextools, "kpsewhich", mock_kpsewhich): | |
104 | nt.assert_equals( |
|
104 | nt.assert_equals( | |
105 | '\n'.join(latextools.genelatex("x^2", True)), |
|
105 | '\n'.join(latextools.genelatex("x^2", True)), | |
106 | r'''\documentclass{article} |
|
106 | r'''\documentclass{article} | |
107 | \usepackage{amsmath} |
|
107 | \usepackage{amsmath} | |
108 | \usepackage{amsthm} |
|
108 | \usepackage{amsthm} | |
109 | \usepackage{amssymb} |
|
109 | \usepackage{amssymb} | |
110 | \usepackage{bm} |
|
110 | \usepackage{bm} | |
111 | \usepackage{breqn} |
|
111 | \usepackage{breqn} | |
112 | \pagestyle{empty} |
|
112 | \pagestyle{empty} | |
113 | \begin{document} |
|
113 | \begin{document} | |
114 | \begin{dmath*} |
|
114 | \begin{dmath*} | |
115 | x^2 |
|
115 | x^2 | |
116 | \end{dmath*} |
|
116 | \end{dmath*} | |
117 | \end{document}''') |
|
117 | \end{document}''') | |
118 |
|
118 | |||
119 |
|
119 | |||
120 | def test_genelatex_wrap_without_breqn(): |
|
120 | def test_genelatex_wrap_without_breqn(): | |
121 | """ |
|
121 | """ | |
122 | Test genelatex with wrap=True for the case breqn.sty is not installed. |
|
122 | Test genelatex with wrap=True for the case breqn.sty is not installed. | |
123 | """ |
|
123 | """ | |
124 | def mock_kpsewhich(filename): |
|
124 | def mock_kpsewhich(filename): | |
125 | nt.assert_equals(filename, "breqn.sty") |
|
125 | nt.assert_equals(filename, "breqn.sty") | |
126 | return None |
|
126 | return None | |
127 |
|
127 | |||
128 |
with |
|
128 | with patch.object(latextools, "kpsewhich", mock_kpsewhich): | |
129 | nt.assert_equals( |
|
129 | nt.assert_equals( | |
130 | '\n'.join(latextools.genelatex("x^2", True)), |
|
130 | '\n'.join(latextools.genelatex("x^2", True)), | |
131 | r'''\documentclass{article} |
|
131 | r'''\documentclass{article} | |
132 | \usepackage{amsmath} |
|
132 | \usepackage{amsmath} | |
133 | \usepackage{amsthm} |
|
133 | \usepackage{amsthm} | |
134 | \usepackage{amssymb} |
|
134 | \usepackage{amssymb} | |
135 | \usepackage{bm} |
|
135 | \usepackage{bm} | |
136 | \pagestyle{empty} |
|
136 | \pagestyle{empty} | |
137 | \begin{document} |
|
137 | \begin{document} | |
138 | $$x^2$$ |
|
138 | $$x^2$$ | |
139 | \end{document}''') |
|
139 | \end{document}''') |
@@ -1,498 +1,487 | |||||
1 | """Generic testing tools. |
|
1 | """Generic testing tools. | |
2 |
|
2 | |||
3 | Authors |
|
3 | Authors | |
4 | ------- |
|
4 | ------- | |
5 | - Fernando Perez <Fernando.Perez@berkeley.edu> |
|
5 | - Fernando Perez <Fernando.Perez@berkeley.edu> | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | from __future__ import absolute_import |
|
8 | from __future__ import absolute_import | |
9 |
|
9 | |||
10 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
11 | # Copyright (C) 2009 The IPython Development Team |
|
11 | # Copyright (C) 2009 The IPython Development Team | |
12 | # |
|
12 | # | |
13 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | # Distributed under the terms of the BSD License. The full license is in | |
14 | # the file COPYING, distributed as part of this software. |
|
14 | # the file COPYING, distributed as part of this software. | |
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 |
|
16 | |||
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 | # Imports |
|
18 | # Imports | |
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | import os |
|
21 | import os | |
22 | import re |
|
22 | import re | |
23 | import sys |
|
23 | import sys | |
24 | import tempfile |
|
24 | import tempfile | |
25 |
|
25 | |||
26 | from contextlib import contextmanager |
|
26 | from contextlib import contextmanager | |
27 | from io import StringIO |
|
27 | from io import StringIO | |
28 | from subprocess import Popen, PIPE |
|
28 | from subprocess import Popen, PIPE | |
29 |
|
29 | |||
30 | try: |
|
30 | try: | |
31 | # These tools are used by parts of the runtime, so we make the nose |
|
31 | # These tools are used by parts of the runtime, so we make the nose | |
32 | # dependency optional at this point. Nose is a hard dependency to run the |
|
32 | # dependency optional at this point. Nose is a hard dependency to run the | |
33 | # test suite, but NOT to use ipython itself. |
|
33 | # test suite, but NOT to use ipython itself. | |
34 | import nose.tools as nt |
|
34 | import nose.tools as nt | |
35 | has_nose = True |
|
35 | has_nose = True | |
36 | except ImportError: |
|
36 | except ImportError: | |
37 | has_nose = False |
|
37 | has_nose = False | |
38 |
|
38 | |||
39 | from IPython.config.loader import Config |
|
39 | from IPython.config.loader import Config | |
40 | from IPython.utils.process import get_output_error_code |
|
40 | from IPython.utils.process import get_output_error_code | |
41 | from IPython.utils.text import list_strings |
|
41 | from IPython.utils.text import list_strings | |
42 | from IPython.utils.io import temp_pyfile, Tee |
|
42 | from IPython.utils.io import temp_pyfile, Tee | |
43 | from IPython.utils import py3compat |
|
43 | from IPython.utils import py3compat | |
44 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
44 | from IPython.utils.encoding import DEFAULT_ENCODING | |
45 |
|
45 | |||
46 | from . import decorators as dec |
|
46 | from . import decorators as dec | |
47 | from . import skipdoctest |
|
47 | from . import skipdoctest | |
48 |
|
48 | |||
49 | #----------------------------------------------------------------------------- |
|
49 | #----------------------------------------------------------------------------- | |
50 | # Functions and classes |
|
50 | # Functions and classes | |
51 | #----------------------------------------------------------------------------- |
|
51 | #----------------------------------------------------------------------------- | |
52 |
|
52 | |||
53 | # The docstring for full_path doctests differently on win32 (different path |
|
53 | # The docstring for full_path doctests differently on win32 (different path | |
54 | # separator) so just skip the doctest there. The example remains informative. |
|
54 | # separator) so just skip the doctest there. The example remains informative. | |
55 | doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco |
|
55 | doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco | |
56 |
|
56 | |||
57 | @doctest_deco |
|
57 | @doctest_deco | |
58 | def full_path(startPath,files): |
|
58 | def full_path(startPath,files): | |
59 | """Make full paths for all the listed files, based on startPath. |
|
59 | """Make full paths for all the listed files, based on startPath. | |
60 |
|
60 | |||
61 | Only the base part of startPath is kept, since this routine is typically |
|
61 | Only the base part of startPath is kept, since this routine is typically | |
62 | used with a script's ``__file__`` variable as startPath. The base of startPath |
|
62 | used with a script's ``__file__`` variable as startPath. The base of startPath | |
63 | is then prepended to all the listed files, forming the output list. |
|
63 | is then prepended to all the listed files, forming the output list. | |
64 |
|
64 | |||
65 | Parameters |
|
65 | Parameters | |
66 | ---------- |
|
66 | ---------- | |
67 | startPath : string |
|
67 | startPath : string | |
68 | Initial path to use as the base for the results. This path is split |
|
68 | Initial path to use as the base for the results. This path is split | |
69 | using os.path.split() and only its first component is kept. |
|
69 | using os.path.split() and only its first component is kept. | |
70 |
|
70 | |||
71 | files : string or list |
|
71 | files : string or list | |
72 | One or more files. |
|
72 | One or more files. | |
73 |
|
73 | |||
74 | Examples |
|
74 | Examples | |
75 | -------- |
|
75 | -------- | |
76 |
|
76 | |||
77 | >>> full_path('/foo/bar.py',['a.txt','b.txt']) |
|
77 | >>> full_path('/foo/bar.py',['a.txt','b.txt']) | |
78 | ['/foo/a.txt', '/foo/b.txt'] |
|
78 | ['/foo/a.txt', '/foo/b.txt'] | |
79 |
|
79 | |||
80 | >>> full_path('/foo',['a.txt','b.txt']) |
|
80 | >>> full_path('/foo',['a.txt','b.txt']) | |
81 | ['/a.txt', '/b.txt'] |
|
81 | ['/a.txt', '/b.txt'] | |
82 |
|
82 | |||
83 | If a single file is given, the output is still a list:: |
|
83 | If a single file is given, the output is still a list:: | |
84 |
|
84 | |||
85 | >>> full_path('/foo','a.txt') |
|
85 | >>> full_path('/foo','a.txt') | |
86 | ['/a.txt'] |
|
86 | ['/a.txt'] | |
87 | """ |
|
87 | """ | |
88 |
|
88 | |||
89 | files = list_strings(files) |
|
89 | files = list_strings(files) | |
90 | base = os.path.split(startPath)[0] |
|
90 | base = os.path.split(startPath)[0] | |
91 | return [ os.path.join(base,f) for f in files ] |
|
91 | return [ os.path.join(base,f) for f in files ] | |
92 |
|
92 | |||
93 |
|
93 | |||
94 | def parse_test_output(txt): |
|
94 | def parse_test_output(txt): | |
95 | """Parse the output of a test run and return errors, failures. |
|
95 | """Parse the output of a test run and return errors, failures. | |
96 |
|
96 | |||
97 | Parameters |
|
97 | Parameters | |
98 | ---------- |
|
98 | ---------- | |
99 | txt : str |
|
99 | txt : str | |
100 | Text output of a test run, assumed to contain a line of one of the |
|
100 | Text output of a test run, assumed to contain a line of one of the | |
101 | following forms:: |
|
101 | following forms:: | |
102 |
|
102 | |||
103 | 'FAILED (errors=1)' |
|
103 | 'FAILED (errors=1)' | |
104 | 'FAILED (failures=1)' |
|
104 | 'FAILED (failures=1)' | |
105 | 'FAILED (errors=1, failures=1)' |
|
105 | 'FAILED (errors=1, failures=1)' | |
106 |
|
106 | |||
107 | Returns |
|
107 | Returns | |
108 | ------- |
|
108 | ------- | |
109 | nerr, nfail |
|
109 | nerr, nfail | |
110 | number of errors and failures. |
|
110 | number of errors and failures. | |
111 | """ |
|
111 | """ | |
112 |
|
112 | |||
113 | err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE) |
|
113 | err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE) | |
114 | if err_m: |
|
114 | if err_m: | |
115 | nerr = int(err_m.group(1)) |
|
115 | nerr = int(err_m.group(1)) | |
116 | nfail = 0 |
|
116 | nfail = 0 | |
117 | return nerr, nfail |
|
117 | return nerr, nfail | |
118 |
|
118 | |||
119 | fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE) |
|
119 | fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE) | |
120 | if fail_m: |
|
120 | if fail_m: | |
121 | nerr = 0 |
|
121 | nerr = 0 | |
122 | nfail = int(fail_m.group(1)) |
|
122 | nfail = int(fail_m.group(1)) | |
123 | return nerr, nfail |
|
123 | return nerr, nfail | |
124 |
|
124 | |||
125 | both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt, |
|
125 | both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt, | |
126 | re.MULTILINE) |
|
126 | re.MULTILINE) | |
127 | if both_m: |
|
127 | if both_m: | |
128 | nerr = int(both_m.group(1)) |
|
128 | nerr = int(both_m.group(1)) | |
129 | nfail = int(both_m.group(2)) |
|
129 | nfail = int(both_m.group(2)) | |
130 | return nerr, nfail |
|
130 | return nerr, nfail | |
131 |
|
131 | |||
132 | # If the input didn't match any of these forms, assume no error/failures |
|
132 | # If the input didn't match any of these forms, assume no error/failures | |
133 | return 0, 0 |
|
133 | return 0, 0 | |
134 |
|
134 | |||
135 |
|
135 | |||
136 | # So nose doesn't think this is a test |
|
136 | # So nose doesn't think this is a test | |
137 | parse_test_output.__test__ = False |
|
137 | parse_test_output.__test__ = False | |
138 |
|
138 | |||
139 |
|
139 | |||
140 | def default_argv(): |
|
140 | def default_argv(): | |
141 | """Return a valid default argv for creating testing instances of ipython""" |
|
141 | """Return a valid default argv for creating testing instances of ipython""" | |
142 |
|
142 | |||
143 | return ['--quick', # so no config file is loaded |
|
143 | return ['--quick', # so no config file is loaded | |
144 | # Other defaults to minimize side effects on stdout |
|
144 | # Other defaults to minimize side effects on stdout | |
145 | '--colors=NoColor', '--no-term-title','--no-banner', |
|
145 | '--colors=NoColor', '--no-term-title','--no-banner', | |
146 | '--autocall=0'] |
|
146 | '--autocall=0'] | |
147 |
|
147 | |||
148 |
|
148 | |||
149 | def default_config(): |
|
149 | def default_config(): | |
150 | """Return a config object with good defaults for testing.""" |
|
150 | """Return a config object with good defaults for testing.""" | |
151 | config = Config() |
|
151 | config = Config() | |
152 | config.TerminalInteractiveShell.colors = 'NoColor' |
|
152 | config.TerminalInteractiveShell.colors = 'NoColor' | |
153 | config.TerminalTerminalInteractiveShell.term_title = False, |
|
153 | config.TerminalTerminalInteractiveShell.term_title = False, | |
154 | config.TerminalInteractiveShell.autocall = 0 |
|
154 | config.TerminalInteractiveShell.autocall = 0 | |
155 | f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False) |
|
155 | f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False) | |
156 | config.HistoryManager.hist_file = f.name |
|
156 | config.HistoryManager.hist_file = f.name | |
157 | f.close() |
|
157 | f.close() | |
158 | config.HistoryManager.db_cache_size = 10000 |
|
158 | config.HistoryManager.db_cache_size = 10000 | |
159 | return config |
|
159 | return config | |
160 |
|
160 | |||
161 |
|
161 | |||
162 | def get_ipython_cmd(as_string=False): |
|
162 | def get_ipython_cmd(as_string=False): | |
163 | """ |
|
163 | """ | |
164 | Return appropriate IPython command line name. By default, this will return |
|
164 | Return appropriate IPython command line name. By default, this will return | |
165 | a list that can be used with subprocess.Popen, for example, but passing |
|
165 | a list that can be used with subprocess.Popen, for example, but passing | |
166 | `as_string=True` allows for returning the IPython command as a string. |
|
166 | `as_string=True` allows for returning the IPython command as a string. | |
167 |
|
167 | |||
168 | Parameters |
|
168 | Parameters | |
169 | ---------- |
|
169 | ---------- | |
170 | as_string: bool |
|
170 | as_string: bool | |
171 | Flag to allow to return the command as a string. |
|
171 | Flag to allow to return the command as a string. | |
172 | """ |
|
172 | """ | |
173 | ipython_cmd = [sys.executable, "-m", "IPython"] |
|
173 | ipython_cmd = [sys.executable, "-m", "IPython"] | |
174 |
|
174 | |||
175 | if as_string: |
|
175 | if as_string: | |
176 | ipython_cmd = " ".join(ipython_cmd) |
|
176 | ipython_cmd = " ".join(ipython_cmd) | |
177 |
|
177 | |||
178 | return ipython_cmd |
|
178 | return ipython_cmd | |
179 |
|
179 | |||
180 | def ipexec(fname, options=None, commands=()): |
|
180 | def ipexec(fname, options=None, commands=()): | |
181 | """Utility to call 'ipython filename'. |
|
181 | """Utility to call 'ipython filename'. | |
182 |
|
182 | |||
183 | Starts IPython with a minimal and safe configuration to make startup as fast |
|
183 | Starts IPython with a minimal and safe configuration to make startup as fast | |
184 | as possible. |
|
184 | as possible. | |
185 |
|
185 | |||
186 | Note that this starts IPython in a subprocess! |
|
186 | Note that this starts IPython in a subprocess! | |
187 |
|
187 | |||
188 | Parameters |
|
188 | Parameters | |
189 | ---------- |
|
189 | ---------- | |
190 | fname : str |
|
190 | fname : str | |
191 | Name of file to be executed (should have .py or .ipy extension). |
|
191 | Name of file to be executed (should have .py or .ipy extension). | |
192 |
|
192 | |||
193 | options : optional, list |
|
193 | options : optional, list | |
194 | Extra command-line flags to be passed to IPython. |
|
194 | Extra command-line flags to be passed to IPython. | |
195 |
|
195 | |||
196 | commands : optional, list |
|
196 | commands : optional, list | |
197 | Commands to send in on stdin |
|
197 | Commands to send in on stdin | |
198 |
|
198 | |||
199 | Returns |
|
199 | Returns | |
200 | ------- |
|
200 | ------- | |
201 | (stdout, stderr) of ipython subprocess. |
|
201 | (stdout, stderr) of ipython subprocess. | |
202 | """ |
|
202 | """ | |
203 | if options is None: options = [] |
|
203 | if options is None: options = [] | |
204 |
|
204 | |||
205 | # For these subprocess calls, eliminate all prompt printing so we only see |
|
205 | # For these subprocess calls, eliminate all prompt printing so we only see | |
206 | # output from script execution |
|
206 | # output from script execution | |
207 | prompt_opts = [ '--PromptManager.in_template=""', |
|
207 | prompt_opts = [ '--PromptManager.in_template=""', | |
208 | '--PromptManager.in2_template=""', |
|
208 | '--PromptManager.in2_template=""', | |
209 | '--PromptManager.out_template=""' |
|
209 | '--PromptManager.out_template=""' | |
210 | ] |
|
210 | ] | |
211 | cmdargs = default_argv() + prompt_opts + options |
|
211 | cmdargs = default_argv() + prompt_opts + options | |
212 |
|
212 | |||
213 | test_dir = os.path.dirname(__file__) |
|
213 | test_dir = os.path.dirname(__file__) | |
214 |
|
214 | |||
215 | ipython_cmd = get_ipython_cmd() |
|
215 | ipython_cmd = get_ipython_cmd() | |
216 | # Absolute path for filename |
|
216 | # Absolute path for filename | |
217 | full_fname = os.path.join(test_dir, fname) |
|
217 | full_fname = os.path.join(test_dir, fname) | |
218 | full_cmd = ipython_cmd + cmdargs + [full_fname] |
|
218 | full_cmd = ipython_cmd + cmdargs + [full_fname] | |
219 | env = os.environ.copy() |
|
219 | env = os.environ.copy() | |
220 | # FIXME: ignore all warnings in ipexec while we have shims |
|
220 | # FIXME: ignore all warnings in ipexec while we have shims | |
221 | # should we keep suppressing warnings here, even after removing shims? |
|
221 | # should we keep suppressing warnings here, even after removing shims? | |
222 | env['PYTHONWARNINGS'] = 'ignore' |
|
222 | env['PYTHONWARNINGS'] = 'ignore' | |
223 | # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr |
|
223 | # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr | |
224 | for k, v in env.items(): |
|
224 | for k, v in env.items(): | |
225 | # Debug a bizarre failure we've seen on Windows: |
|
225 | # Debug a bizarre failure we've seen on Windows: | |
226 | # TypeError: environment can only contain strings |
|
226 | # TypeError: environment can only contain strings | |
227 | if not isinstance(v, str): |
|
227 | if not isinstance(v, str): | |
228 | print(k, v) |
|
228 | print(k, v) | |
229 | p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env) |
|
229 | p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env) | |
230 | out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None) |
|
230 | out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None) | |
231 | out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) |
|
231 | out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) | |
232 | # `import readline` causes 'ESC[?1034h' to be output sometimes, |
|
232 | # `import readline` causes 'ESC[?1034h' to be output sometimes, | |
233 | # so strip that out before doing comparisons |
|
233 | # so strip that out before doing comparisons | |
234 | if out: |
|
234 | if out: | |
235 | out = re.sub(r'\x1b\[[^h]+h', '', out) |
|
235 | out = re.sub(r'\x1b\[[^h]+h', '', out) | |
236 | return out, err |
|
236 | return out, err | |
237 |
|
237 | |||
238 |
|
238 | |||
239 | def ipexec_validate(fname, expected_out, expected_err='', |
|
239 | def ipexec_validate(fname, expected_out, expected_err='', | |
240 | options=None, commands=()): |
|
240 | options=None, commands=()): | |
241 | """Utility to call 'ipython filename' and validate output/error. |
|
241 | """Utility to call 'ipython filename' and validate output/error. | |
242 |
|
242 | |||
243 | This function raises an AssertionError if the validation fails. |
|
243 | This function raises an AssertionError if the validation fails. | |
244 |
|
244 | |||
245 | Note that this starts IPython in a subprocess! |
|
245 | Note that this starts IPython in a subprocess! | |
246 |
|
246 | |||
247 | Parameters |
|
247 | Parameters | |
248 | ---------- |
|
248 | ---------- | |
249 | fname : str |
|
249 | fname : str | |
250 | Name of the file to be executed (should have .py or .ipy extension). |
|
250 | Name of the file to be executed (should have .py or .ipy extension). | |
251 |
|
251 | |||
252 | expected_out : str |
|
252 | expected_out : str | |
253 | Expected stdout of the process. |
|
253 | Expected stdout of the process. | |
254 |
|
254 | |||
255 | expected_err : optional, str |
|
255 | expected_err : optional, str | |
256 | Expected stderr of the process. |
|
256 | Expected stderr of the process. | |
257 |
|
257 | |||
258 | options : optional, list |
|
258 | options : optional, list | |
259 | Extra command-line flags to be passed to IPython. |
|
259 | Extra command-line flags to be passed to IPython. | |
260 |
|
260 | |||
261 | Returns |
|
261 | Returns | |
262 | ------- |
|
262 | ------- | |
263 | None |
|
263 | None | |
264 | """ |
|
264 | """ | |
265 |
|
265 | |||
266 | import nose.tools as nt |
|
266 | import nose.tools as nt | |
267 |
|
267 | |||
268 | out, err = ipexec(fname, options, commands) |
|
268 | out, err = ipexec(fname, options, commands) | |
269 | #print 'OUT', out # dbg |
|
269 | #print 'OUT', out # dbg | |
270 | #print 'ERR', err # dbg |
|
270 | #print 'ERR', err # dbg | |
271 | # If there are any errors, we must check those befor stdout, as they may be |
|
271 | # If there are any errors, we must check those befor stdout, as they may be | |
272 | # more informative than simply having an empty stdout. |
|
272 | # more informative than simply having an empty stdout. | |
273 | if err: |
|
273 | if err: | |
274 | if expected_err: |
|
274 | if expected_err: | |
275 | nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines())) |
|
275 | nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines())) | |
276 | else: |
|
276 | else: | |
277 | raise ValueError('Running file %r produced error: %r' % |
|
277 | raise ValueError('Running file %r produced error: %r' % | |
278 | (fname, err)) |
|
278 | (fname, err)) | |
279 | # If no errors or output on stderr was expected, match stdout |
|
279 | # If no errors or output on stderr was expected, match stdout | |
280 | nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines())) |
|
280 | nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines())) | |
281 |
|
281 | |||
282 |
|
282 | |||
283 | class TempFileMixin(object): |
|
283 | class TempFileMixin(object): | |
284 | """Utility class to create temporary Python/IPython files. |
|
284 | """Utility class to create temporary Python/IPython files. | |
285 |
|
285 | |||
286 | Meant as a mixin class for test cases.""" |
|
286 | Meant as a mixin class for test cases.""" | |
287 |
|
287 | |||
288 | def mktmp(self, src, ext='.py'): |
|
288 | def mktmp(self, src, ext='.py'): | |
289 | """Make a valid python temp file.""" |
|
289 | """Make a valid python temp file.""" | |
290 | fname, f = temp_pyfile(src, ext) |
|
290 | fname, f = temp_pyfile(src, ext) | |
291 | self.tmpfile = f |
|
291 | self.tmpfile = f | |
292 | self.fname = fname |
|
292 | self.fname = fname | |
293 |
|
293 | |||
294 | def tearDown(self): |
|
294 | def tearDown(self): | |
295 | if hasattr(self, 'tmpfile'): |
|
295 | if hasattr(self, 'tmpfile'): | |
296 | # If the tmpfile wasn't made because of skipped tests, like in |
|
296 | # If the tmpfile wasn't made because of skipped tests, like in | |
297 | # win32, there's nothing to cleanup. |
|
297 | # win32, there's nothing to cleanup. | |
298 | self.tmpfile.close() |
|
298 | self.tmpfile.close() | |
299 | try: |
|
299 | try: | |
300 | os.unlink(self.fname) |
|
300 | os.unlink(self.fname) | |
301 | except: |
|
301 | except: | |
302 | # On Windows, even though we close the file, we still can't |
|
302 | # On Windows, even though we close the file, we still can't | |
303 | # delete it. I have no clue why |
|
303 | # delete it. I have no clue why | |
304 | pass |
|
304 | pass | |
305 |
|
305 | |||
306 | pair_fail_msg = ("Testing {0}\n\n" |
|
306 | pair_fail_msg = ("Testing {0}\n\n" | |
307 | "In:\n" |
|
307 | "In:\n" | |
308 | " {1!r}\n" |
|
308 | " {1!r}\n" | |
309 | "Expected:\n" |
|
309 | "Expected:\n" | |
310 | " {2!r}\n" |
|
310 | " {2!r}\n" | |
311 | "Got:\n" |
|
311 | "Got:\n" | |
312 | " {3!r}\n") |
|
312 | " {3!r}\n") | |
313 | def check_pairs(func, pairs): |
|
313 | def check_pairs(func, pairs): | |
314 | """Utility function for the common case of checking a function with a |
|
314 | """Utility function for the common case of checking a function with a | |
315 | sequence of input/output pairs. |
|
315 | sequence of input/output pairs. | |
316 |
|
316 | |||
317 | Parameters |
|
317 | Parameters | |
318 | ---------- |
|
318 | ---------- | |
319 | func : callable |
|
319 | func : callable | |
320 | The function to be tested. Should accept a single argument. |
|
320 | The function to be tested. Should accept a single argument. | |
321 | pairs : iterable |
|
321 | pairs : iterable | |
322 | A list of (input, expected_output) tuples. |
|
322 | A list of (input, expected_output) tuples. | |
323 |
|
323 | |||
324 | Returns |
|
324 | Returns | |
325 | ------- |
|
325 | ------- | |
326 | None. Raises an AssertionError if any output does not match the expected |
|
326 | None. Raises an AssertionError if any output does not match the expected | |
327 | value. |
|
327 | value. | |
328 | """ |
|
328 | """ | |
329 | name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>")) |
|
329 | name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>")) | |
330 | for inp, expected in pairs: |
|
330 | for inp, expected in pairs: | |
331 | out = func(inp) |
|
331 | out = func(inp) | |
332 | assert out == expected, pair_fail_msg.format(name, inp, expected, out) |
|
332 | assert out == expected, pair_fail_msg.format(name, inp, expected, out) | |
333 |
|
333 | |||
334 |
|
334 | |||
335 | if py3compat.PY3: |
|
335 | if py3compat.PY3: | |
336 | MyStringIO = StringIO |
|
336 | MyStringIO = StringIO | |
337 | else: |
|
337 | else: | |
338 | # In Python 2, stdout/stderr can have either bytes or unicode written to them, |
|
338 | # In Python 2, stdout/stderr can have either bytes or unicode written to them, | |
339 | # so we need a class that can handle both. |
|
339 | # so we need a class that can handle both. | |
340 | class MyStringIO(StringIO): |
|
340 | class MyStringIO(StringIO): | |
341 | def write(self, s): |
|
341 | def write(self, s): | |
342 | s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING) |
|
342 | s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING) | |
343 | super(MyStringIO, self).write(s) |
|
343 | super(MyStringIO, self).write(s) | |
344 |
|
344 | |||
345 | _re_type = type(re.compile(r'')) |
|
345 | _re_type = type(re.compile(r'')) | |
346 |
|
346 | |||
347 | notprinted_msg = """Did not find {0!r} in printed output (on {1}): |
|
347 | notprinted_msg = """Did not find {0!r} in printed output (on {1}): | |
348 | ------- |
|
348 | ------- | |
349 | {2!s} |
|
349 | {2!s} | |
350 | ------- |
|
350 | ------- | |
351 | """ |
|
351 | """ | |
352 |
|
352 | |||
353 | class AssertPrints(object): |
|
353 | class AssertPrints(object): | |
354 | """Context manager for testing that code prints certain text. |
|
354 | """Context manager for testing that code prints certain text. | |
355 |
|
355 | |||
356 | Examples |
|
356 | Examples | |
357 | -------- |
|
357 | -------- | |
358 | >>> with AssertPrints("abc", suppress=False): |
|
358 | >>> with AssertPrints("abc", suppress=False): | |
359 | ... print("abcd") |
|
359 | ... print("abcd") | |
360 | ... print("def") |
|
360 | ... print("def") | |
361 | ... |
|
361 | ... | |
362 | abcd |
|
362 | abcd | |
363 | def |
|
363 | def | |
364 | """ |
|
364 | """ | |
365 | def __init__(self, s, channel='stdout', suppress=True): |
|
365 | def __init__(self, s, channel='stdout', suppress=True): | |
366 | self.s = s |
|
366 | self.s = s | |
367 | if isinstance(self.s, (py3compat.string_types, _re_type)): |
|
367 | if isinstance(self.s, (py3compat.string_types, _re_type)): | |
368 | self.s = [self.s] |
|
368 | self.s = [self.s] | |
369 | self.channel = channel |
|
369 | self.channel = channel | |
370 | self.suppress = suppress |
|
370 | self.suppress = suppress | |
371 |
|
371 | |||
372 | def __enter__(self): |
|
372 | def __enter__(self): | |
373 | self.orig_stream = getattr(sys, self.channel) |
|
373 | self.orig_stream = getattr(sys, self.channel) | |
374 | self.buffer = MyStringIO() |
|
374 | self.buffer = MyStringIO() | |
375 | self.tee = Tee(self.buffer, channel=self.channel) |
|
375 | self.tee = Tee(self.buffer, channel=self.channel) | |
376 | setattr(sys, self.channel, self.buffer if self.suppress else self.tee) |
|
376 | setattr(sys, self.channel, self.buffer if self.suppress else self.tee) | |
377 |
|
377 | |||
378 | def __exit__(self, etype, value, traceback): |
|
378 | def __exit__(self, etype, value, traceback): | |
379 | try: |
|
379 | try: | |
380 | if value is not None: |
|
380 | if value is not None: | |
381 | # If an error was raised, don't check anything else |
|
381 | # If an error was raised, don't check anything else | |
382 | return False |
|
382 | return False | |
383 | self.tee.flush() |
|
383 | self.tee.flush() | |
384 | setattr(sys, self.channel, self.orig_stream) |
|
384 | setattr(sys, self.channel, self.orig_stream) | |
385 | printed = self.buffer.getvalue() |
|
385 | printed = self.buffer.getvalue() | |
386 | for s in self.s: |
|
386 | for s in self.s: | |
387 | if isinstance(s, _re_type): |
|
387 | if isinstance(s, _re_type): | |
388 | assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed) |
|
388 | assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed) | |
389 | else: |
|
389 | else: | |
390 | assert s in printed, notprinted_msg.format(s, self.channel, printed) |
|
390 | assert s in printed, notprinted_msg.format(s, self.channel, printed) | |
391 | return False |
|
391 | return False | |
392 | finally: |
|
392 | finally: | |
393 | self.tee.close() |
|
393 | self.tee.close() | |
394 |
|
394 | |||
395 | printed_msg = """Found {0!r} in printed output (on {1}): |
|
395 | printed_msg = """Found {0!r} in printed output (on {1}): | |
396 | ------- |
|
396 | ------- | |
397 | {2!s} |
|
397 | {2!s} | |
398 | ------- |
|
398 | ------- | |
399 | """ |
|
399 | """ | |
400 |
|
400 | |||
401 | class AssertNotPrints(AssertPrints): |
|
401 | class AssertNotPrints(AssertPrints): | |
402 | """Context manager for checking that certain output *isn't* produced. |
|
402 | """Context manager for checking that certain output *isn't* produced. | |
403 |
|
403 | |||
404 | Counterpart of AssertPrints""" |
|
404 | Counterpart of AssertPrints""" | |
405 | def __exit__(self, etype, value, traceback): |
|
405 | def __exit__(self, etype, value, traceback): | |
406 | try: |
|
406 | try: | |
407 | if value is not None: |
|
407 | if value is not None: | |
408 | # If an error was raised, don't check anything else |
|
408 | # If an error was raised, don't check anything else | |
409 | self.tee.close() |
|
409 | self.tee.close() | |
410 | return False |
|
410 | return False | |
411 | self.tee.flush() |
|
411 | self.tee.flush() | |
412 | setattr(sys, self.channel, self.orig_stream) |
|
412 | setattr(sys, self.channel, self.orig_stream) | |
413 | printed = self.buffer.getvalue() |
|
413 | printed = self.buffer.getvalue() | |
414 | for s in self.s: |
|
414 | for s in self.s: | |
415 | if isinstance(s, _re_type): |
|
415 | if isinstance(s, _re_type): | |
416 | assert not s.search(printed),printed_msg.format( |
|
416 | assert not s.search(printed),printed_msg.format( | |
417 | s.pattern, self.channel, printed) |
|
417 | s.pattern, self.channel, printed) | |
418 | else: |
|
418 | else: | |
419 | assert s not in printed, printed_msg.format( |
|
419 | assert s not in printed, printed_msg.format( | |
420 | s, self.channel, printed) |
|
420 | s, self.channel, printed) | |
421 | return False |
|
421 | return False | |
422 | finally: |
|
422 | finally: | |
423 | self.tee.close() |
|
423 | self.tee.close() | |
424 |
|
424 | |||
425 | @contextmanager |
|
425 | @contextmanager | |
426 | def mute_warn(): |
|
426 | def mute_warn(): | |
427 | from IPython.utils import warn |
|
427 | from IPython.utils import warn | |
428 | save_warn = warn.warn |
|
428 | save_warn = warn.warn | |
429 | warn.warn = lambda *a, **kw: None |
|
429 | warn.warn = lambda *a, **kw: None | |
430 | try: |
|
430 | try: | |
431 | yield |
|
431 | yield | |
432 | finally: |
|
432 | finally: | |
433 | warn.warn = save_warn |
|
433 | warn.warn = save_warn | |
434 |
|
434 | |||
435 | @contextmanager |
|
435 | @contextmanager | |
436 | def make_tempfile(name): |
|
436 | def make_tempfile(name): | |
437 | """ Create an empty, named, temporary file for the duration of the context. |
|
437 | """ Create an empty, named, temporary file for the duration of the context. | |
438 | """ |
|
438 | """ | |
439 | f = open(name, 'w') |
|
439 | f = open(name, 'w') | |
440 | f.close() |
|
440 | f.close() | |
441 | try: |
|
441 | try: | |
442 | yield |
|
442 | yield | |
443 | finally: |
|
443 | finally: | |
444 | os.unlink(name) |
|
444 | os.unlink(name) | |
445 |
|
445 | |||
446 |
|
446 | |||
447 | @contextmanager |
|
|||
448 | def monkeypatch(obj, name, attr): |
|
|||
449 | """ |
|
|||
450 | Context manager to replace attribute named `name` in `obj` with `attr`. |
|
|||
451 | """ |
|
|||
452 | orig = getattr(obj, name) |
|
|||
453 | setattr(obj, name, attr) |
|
|||
454 | yield |
|
|||
455 | setattr(obj, name, orig) |
|
|||
456 |
|
||||
457 |
|
||||
458 | def help_output_test(subcommand=''): |
|
447 | def help_output_test(subcommand=''): | |
459 | """test that `ipython [subcommand] -h` works""" |
|
448 | """test that `ipython [subcommand] -h` works""" | |
460 | cmd = get_ipython_cmd() + [subcommand, '-h'] |
|
449 | cmd = get_ipython_cmd() + [subcommand, '-h'] | |
461 | out, err, rc = get_output_error_code(cmd) |
|
450 | out, err, rc = get_output_error_code(cmd) | |
462 | nt.assert_equal(rc, 0, err) |
|
451 | nt.assert_equal(rc, 0, err) | |
463 | nt.assert_not_in("Traceback", err) |
|
452 | nt.assert_not_in("Traceback", err) | |
464 | nt.assert_in("Options", out) |
|
453 | nt.assert_in("Options", out) | |
465 | nt.assert_in("--help-all", out) |
|
454 | nt.assert_in("--help-all", out) | |
466 | return out, err |
|
455 | return out, err | |
467 |
|
456 | |||
468 |
|
457 | |||
469 | def help_all_output_test(subcommand=''): |
|
458 | def help_all_output_test(subcommand=''): | |
470 | """test that `ipython [subcommand] --help-all` works""" |
|
459 | """test that `ipython [subcommand] --help-all` works""" | |
471 | cmd = get_ipython_cmd() + [subcommand, '--help-all'] |
|
460 | cmd = get_ipython_cmd() + [subcommand, '--help-all'] | |
472 | out, err, rc = get_output_error_code(cmd) |
|
461 | out, err, rc = get_output_error_code(cmd) | |
473 | nt.assert_equal(rc, 0, err) |
|
462 | nt.assert_equal(rc, 0, err) | |
474 | nt.assert_not_in("Traceback", err) |
|
463 | nt.assert_not_in("Traceback", err) | |
475 | nt.assert_in("Options", out) |
|
464 | nt.assert_in("Options", out) | |
476 | nt.assert_in("Class parameters", out) |
|
465 | nt.assert_in("Class parameters", out) | |
477 | return out, err |
|
466 | return out, err | |
478 |
|
467 | |||
479 | def assert_big_text_equal(a, b, chunk_size=80): |
|
468 | def assert_big_text_equal(a, b, chunk_size=80): | |
480 | """assert that large strings are equal |
|
469 | """assert that large strings are equal | |
481 |
|
470 | |||
482 | Zooms in on first chunk that differs, |
|
471 | Zooms in on first chunk that differs, | |
483 | to give better info than vanilla assertEqual for large text blobs. |
|
472 | to give better info than vanilla assertEqual for large text blobs. | |
484 | """ |
|
473 | """ | |
485 | for i in range(0, len(a), chunk_size): |
|
474 | for i in range(0, len(a), chunk_size): | |
486 | chunk_a = a[i:i + chunk_size] |
|
475 | chunk_a = a[i:i + chunk_size] | |
487 | chunk_b = b[i:i + chunk_size] |
|
476 | chunk_b = b[i:i + chunk_size] | |
488 | nt.assert_equal(chunk_a, chunk_b, "[offset: %i]\n%r != \n%r" % ( |
|
477 | nt.assert_equal(chunk_a, chunk_b, "[offset: %i]\n%r != \n%r" % ( | |
489 | i, chunk_a, chunk_b)) |
|
478 | i, chunk_a, chunk_b)) | |
490 |
|
479 | |||
491 | if len(a) > len(b): |
|
480 | if len(a) > len(b): | |
492 | nt.fail("Length doesn't match (%i > %i). Extra text:\n%r" % ( |
|
481 | nt.fail("Length doesn't match (%i > %i). Extra text:\n%r" % ( | |
493 | len(a), len(b), a[len(b):] |
|
482 | len(a), len(b), a[len(b):] | |
494 | )) |
|
483 | )) | |
495 | elif len(a) < len(b): |
|
484 | elif len(a) < len(b): | |
496 | nt.fail("Length doesn't match (%i < %i). Extra text:\n%r" % ( |
|
485 | nt.fail("Length doesn't match (%i < %i). Extra text:\n%r" % ( | |
497 | len(a), len(b), b[len(a):] |
|
486 | len(a), len(b), b[len(a):] | |
498 | )) |
|
487 | )) |
@@ -1,94 +1,95 | |||||
1 | #----------------------------------------------------------------------------- |
|
1 | # Copyright (c) IPython Development Team. | |
2 | # Copyright (C) 2012 The IPython Development Team |
|
2 | # Distributed under the terms of the Modified BSD License. | |
3 | # |
|
|||
4 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | # the file COPYING, distributed as part of this software. |
|
|||
6 | #----------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | import os |
|
4 | import os | |
9 | import sys |
|
5 | import sys | |
10 | import unittest |
|
6 | import unittest | |
11 | import base64 |
|
7 | import base64 | |
12 |
|
8 | |||
|
9 | try: | |||
|
10 | from unittest.mock import patch | |||
|
11 | except ImportError: | |||
|
12 | from mock import patch | |||
|
13 | ||||
13 | from IPython.kernel import KernelClient |
|
14 | from IPython.kernel import KernelClient | |
14 | from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell |
|
15 | from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell | |
15 | from IPython.utils.tempdir import TemporaryDirectory |
|
16 | from IPython.utils.tempdir import TemporaryDirectory | |
16 | from IPython.testing.tools import monkeypatch |
|
17 | from IPython.testing.tools import monkeypatch | |
17 | from IPython.testing.decorators import skip_without |
|
18 | from IPython.testing.decorators import skip_without | |
18 | from IPython.utils.ipstruct import Struct |
|
19 | from IPython.utils.ipstruct import Struct | |
19 |
|
20 | |||
20 |
|
21 | |||
21 | SCRIPT_PATH = os.path.join( |
|
22 | SCRIPT_PATH = os.path.join( | |
22 | os.path.abspath(os.path.dirname(__file__)), 'writetofile.py') |
|
23 | os.path.abspath(os.path.dirname(__file__)), 'writetofile.py') | |
23 |
|
24 | |||
24 |
|
25 | |||
25 | class ZMQTerminalInteractiveShellTestCase(unittest.TestCase): |
|
26 | class ZMQTerminalInteractiveShellTestCase(unittest.TestCase): | |
26 |
|
27 | |||
27 | def setUp(self): |
|
28 | def setUp(self): | |
28 | client = KernelClient() |
|
29 | client = KernelClient() | |
29 | self.shell = ZMQTerminalInteractiveShell(kernel_client=client) |
|
30 | self.shell = ZMQTerminalInteractiveShell(kernel_client=client) | |
30 | self.raw = b'dummy data' |
|
31 | self.raw = b'dummy data' | |
31 | self.mime = 'image/png' |
|
32 | self.mime = 'image/png' | |
32 | self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')} |
|
33 | self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')} | |
33 |
|
34 | |||
34 | def test_no_call_by_default(self): |
|
35 | def test_no_call_by_default(self): | |
35 | def raise_if_called(*args, **kwds): |
|
36 | def raise_if_called(*args, **kwds): | |
36 | assert False |
|
37 | assert False | |
37 |
|
38 | |||
38 | shell = self.shell |
|
39 | shell = self.shell | |
39 | shell.handle_image_PIL = raise_if_called |
|
40 | shell.handle_image_PIL = raise_if_called | |
40 | shell.handle_image_stream = raise_if_called |
|
41 | shell.handle_image_stream = raise_if_called | |
41 | shell.handle_image_tempfile = raise_if_called |
|
42 | shell.handle_image_tempfile = raise_if_called | |
42 | shell.handle_image_callable = raise_if_called |
|
43 | shell.handle_image_callable = raise_if_called | |
43 |
|
44 | |||
44 | shell.handle_image(None, None) # arguments are dummy |
|
45 | shell.handle_image(None, None) # arguments are dummy | |
45 |
|
46 | |||
46 | @skip_without('PIL') |
|
47 | @skip_without('PIL') | |
47 | def test_handle_image_PIL(self): |
|
48 | def test_handle_image_PIL(self): | |
48 | import PIL.Image |
|
49 | import PIL.Image | |
49 |
|
50 | |||
50 | open_called_with = [] |
|
51 | open_called_with = [] | |
51 | show_called_with = [] |
|
52 | show_called_with = [] | |
52 |
|
53 | |||
53 | def fake_open(arg): |
|
54 | def fake_open(arg): | |
54 | open_called_with.append(arg) |
|
55 | open_called_with.append(arg) | |
55 | return Struct(show=lambda: show_called_with.append(None)) |
|
56 | return Struct(show=lambda: show_called_with.append(None)) | |
56 |
|
57 | |||
57 |
with |
|
58 | with patch.object(PIL.Image, 'open', fake_open): | |
58 | self.shell.handle_image_PIL(self.data, self.mime) |
|
59 | self.shell.handle_image_PIL(self.data, self.mime) | |
59 |
|
60 | |||
60 | self.assertEqual(len(open_called_with), 1) |
|
61 | self.assertEqual(len(open_called_with), 1) | |
61 | self.assertEqual(len(show_called_with), 1) |
|
62 | self.assertEqual(len(show_called_with), 1) | |
62 | self.assertEqual(open_called_with[0].getvalue(), self.raw) |
|
63 | self.assertEqual(open_called_with[0].getvalue(), self.raw) | |
63 |
|
64 | |||
64 | def check_handler_with_file(self, inpath, handler): |
|
65 | def check_handler_with_file(self, inpath, handler): | |
65 | shell = self.shell |
|
66 | shell = self.shell | |
66 | configname = '{0}_image_handler'.format(handler) |
|
67 | configname = '{0}_image_handler'.format(handler) | |
67 | funcname = 'handle_image_{0}'.format(handler) |
|
68 | funcname = 'handle_image_{0}'.format(handler) | |
68 |
|
69 | |||
69 | assert hasattr(shell, configname) |
|
70 | assert hasattr(shell, configname) | |
70 | assert hasattr(shell, funcname) |
|
71 | assert hasattr(shell, funcname) | |
71 |
|
72 | |||
72 | with TemporaryDirectory() as tmpdir: |
|
73 | with TemporaryDirectory() as tmpdir: | |
73 | outpath = os.path.join(tmpdir, 'data') |
|
74 | outpath = os.path.join(tmpdir, 'data') | |
74 | cmd = [sys.executable, SCRIPT_PATH, inpath, outpath] |
|
75 | cmd = [sys.executable, SCRIPT_PATH, inpath, outpath] | |
75 | setattr(shell, configname, cmd) |
|
76 | setattr(shell, configname, cmd) | |
76 | getattr(shell, funcname)(self.data, self.mime) |
|
77 | getattr(shell, funcname)(self.data, self.mime) | |
77 | # cmd is called and file is closed. So it's safe to open now. |
|
78 | # cmd is called and file is closed. So it's safe to open now. | |
78 | with open(outpath, 'rb') as file: |
|
79 | with open(outpath, 'rb') as file: | |
79 | transferred = file.read() |
|
80 | transferred = file.read() | |
80 |
|
81 | |||
81 | self.assertEqual(transferred, self.raw) |
|
82 | self.assertEqual(transferred, self.raw) | |
82 |
|
83 | |||
83 | def test_handle_image_stream(self): |
|
84 | def test_handle_image_stream(self): | |
84 | self.check_handler_with_file('-', 'stream') |
|
85 | self.check_handler_with_file('-', 'stream') | |
85 |
|
86 | |||
86 | def test_handle_image_tempfile(self): |
|
87 | def test_handle_image_tempfile(self): | |
87 | self.check_handler_with_file('{file}', 'tempfile') |
|
88 | self.check_handler_with_file('{file}', 'tempfile') | |
88 |
|
89 | |||
89 | def test_handle_image_callable(self): |
|
90 | def test_handle_image_callable(self): | |
90 | called_with = [] |
|
91 | called_with = [] | |
91 | self.shell.callable_image_handler = called_with.append |
|
92 | self.shell.callable_image_handler = called_with.append | |
92 | self.shell.handle_image_callable(self.data, self.mime) |
|
93 | self.shell.handle_image_callable(self.data, self.mime) | |
93 | self.assertEqual(len(called_with), 1) |
|
94 | self.assertEqual(len(called_with), 1) | |
94 | assert called_with[0] is self.data |
|
95 | assert called_with[0] is self.data |
General Comments 0
You need to be logged in to leave comments.
Login now