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