##// END OF EJS Templates
Add test that macros are affected by AST transformations
Thomas Kluyver -
Show More
@@ -1,532 +1,542 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7
8 8 Authors
9 9 -------
10 10 * Fernando Perez
11 11 """
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2011 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22 # stdlib
23 23 import ast
24 24 import os
25 25 import shutil
26 26 import sys
27 27 import tempfile
28 28 import unittest
29 29 from os.path import join
30 30 from StringIO import StringIO
31 31
32 32 # third-party
33 33 import nose.tools as nt
34 34
35 35 # Our own
36 36 from IPython.testing.decorators import skipif
37 37 from IPython.testing import tools as tt
38 38 from IPython.utils import io
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Globals
42 42 #-----------------------------------------------------------------------------
43 43 # This is used by every single test, no point repeating it ad nauseam
44 44 ip = get_ipython()
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Tests
48 48 #-----------------------------------------------------------------------------
49 49
50 50 class InteractiveShellTestCase(unittest.TestCase):
51 51 def test_naked_string_cells(self):
52 52 """Test that cells with only naked strings are fully executed"""
53 53 # First, single-line inputs
54 54 ip.run_cell('"a"\n')
55 55 self.assertEqual(ip.user_ns['_'], 'a')
56 56 # And also multi-line cells
57 57 ip.run_cell('"""a\nb"""\n')
58 58 self.assertEqual(ip.user_ns['_'], 'a\nb')
59 59
60 60 def test_run_empty_cell(self):
61 61 """Just make sure we don't get a horrible error with a blank
62 62 cell of input. Yes, I did overlook that."""
63 63 old_xc = ip.execution_count
64 64 ip.run_cell('')
65 65 self.assertEqual(ip.execution_count, old_xc)
66 66
67 67 def test_run_cell_multiline(self):
68 68 """Multi-block, multi-line cells must execute correctly.
69 69 """
70 70 src = '\n'.join(["x=1",
71 71 "y=2",
72 72 "if 1:",
73 73 " x += 1",
74 74 " y += 1",])
75 75 ip.run_cell(src)
76 76 self.assertEqual(ip.user_ns['x'], 2)
77 77 self.assertEqual(ip.user_ns['y'], 3)
78 78
79 79 def test_multiline_string_cells(self):
80 80 "Code sprinkled with multiline strings should execute (GH-306)"
81 81 ip.run_cell('tmp=0')
82 82 self.assertEqual(ip.user_ns['tmp'], 0)
83 83 ip.run_cell('tmp=1;"""a\nb"""\n')
84 84 self.assertEqual(ip.user_ns['tmp'], 1)
85 85
86 86 def test_dont_cache_with_semicolon(self):
87 87 "Ending a line with semicolon should not cache the returned object (GH-307)"
88 88 oldlen = len(ip.user_ns['Out'])
89 89 a = ip.run_cell('1;', store_history=True)
90 90 newlen = len(ip.user_ns['Out'])
91 91 self.assertEqual(oldlen, newlen)
92 92 #also test the default caching behavior
93 93 ip.run_cell('1', store_history=True)
94 94 newlen = len(ip.user_ns['Out'])
95 95 self.assertEqual(oldlen+1, newlen)
96 96
97 97 def test_In_variable(self):
98 98 "Verify that In variable grows with user input (GH-284)"
99 99 oldlen = len(ip.user_ns['In'])
100 100 ip.run_cell('1;', store_history=True)
101 101 newlen = len(ip.user_ns['In'])
102 102 self.assertEqual(oldlen+1, newlen)
103 103 self.assertEqual(ip.user_ns['In'][-1],'1;')
104 104
105 105 def test_magic_names_in_string(self):
106 106 ip.run_cell('a = """\n%exit\n"""')
107 107 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
108 108
109 109 def test_alias_crash(self):
110 110 """Errors in prefilter can't crash IPython"""
111 111 ip.run_cell('%alias parts echo first %s second %s')
112 112 # capture stderr:
113 113 save_err = io.stderr
114 114 io.stderr = StringIO()
115 115 ip.run_cell('parts 1')
116 116 err = io.stderr.getvalue()
117 117 io.stderr = save_err
118 118 self.assertEqual(err.split(':')[0], 'ERROR')
119 119
120 120 def test_trailing_newline(self):
121 121 """test that running !(command) does not raise a SyntaxError"""
122 122 ip.run_cell('!(true)\n', False)
123 123 ip.run_cell('!(true)\n\n\n', False)
124 124
125 125 def test_gh_597(self):
126 126 """Pretty-printing lists of objects with non-ascii reprs may cause
127 127 problems."""
128 128 class Spam(object):
129 129 def __repr__(self):
130 130 return "\xe9"*50
131 131 import IPython.core.formatters
132 132 f = IPython.core.formatters.PlainTextFormatter()
133 133 f([Spam(),Spam()])
134 134
135 135
136 136 def test_future_flags(self):
137 137 """Check that future flags are used for parsing code (gh-777)"""
138 138 ip.run_cell('from __future__ import print_function')
139 139 try:
140 140 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
141 141 assert 'prfunc_return_val' in ip.user_ns
142 142 finally:
143 143 # Reset compiler flags so we don't mess up other tests.
144 144 ip.compile.reset_compiler_flags()
145 145
146 146 def test_future_unicode(self):
147 147 """Check that unicode_literals is imported from __future__ (gh #786)"""
148 148 try:
149 149 ip.run_cell(u'byte_str = "a"')
150 150 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
151 151 ip.run_cell('from __future__ import unicode_literals')
152 152 ip.run_cell(u'unicode_str = "a"')
153 153 assert isinstance(ip.user_ns['unicode_str'], unicode) # strings literals are now unicode
154 154 finally:
155 155 # Reset compiler flags so we don't mess up other tests.
156 156 ip.compile.reset_compiler_flags()
157 157
158 158 def test_can_pickle(self):
159 159 "Can we pickle objects defined interactively (GH-29)"
160 160 ip = get_ipython()
161 161 ip.reset()
162 162 ip.run_cell(("class Mylist(list):\n"
163 163 " def __init__(self,x=[]):\n"
164 164 " list.__init__(self,x)"))
165 165 ip.run_cell("w=Mylist([1,2,3])")
166 166
167 167 from cPickle import dumps
168 168
169 169 # We need to swap in our main module - this is only necessary
170 170 # inside the test framework, because IPython puts the interactive module
171 171 # in place (but the test framework undoes this).
172 172 _main = sys.modules['__main__']
173 173 sys.modules['__main__'] = ip.user_module
174 174 try:
175 175 res = dumps(ip.user_ns["w"])
176 176 finally:
177 177 sys.modules['__main__'] = _main
178 178 self.assertTrue(isinstance(res, bytes))
179 179
180 180 def test_global_ns(self):
181 181 "Code in functions must be able to access variables outside them."
182 182 ip = get_ipython()
183 183 ip.run_cell("a = 10")
184 184 ip.run_cell(("def f(x):\n"
185 185 " return x + a"))
186 186 ip.run_cell("b = f(12)")
187 187 self.assertEqual(ip.user_ns["b"], 22)
188 188
189 189 def test_bad_custom_tb(self):
190 190 """Check that InteractiveShell is protected from bad custom exception handlers"""
191 191 from IPython.utils import io
192 192 save_stderr = io.stderr
193 193 try:
194 194 # capture stderr
195 195 io.stderr = StringIO()
196 196 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
197 197 self.assertEqual(ip.custom_exceptions, (IOError,))
198 198 ip.run_cell(u'raise IOError("foo")')
199 199 self.assertEqual(ip.custom_exceptions, ())
200 200 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
201 201 finally:
202 202 io.stderr = save_stderr
203 203
204 204 def test_bad_custom_tb_return(self):
205 205 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
206 206 from IPython.utils import io
207 207 save_stderr = io.stderr
208 208 try:
209 209 # capture stderr
210 210 io.stderr = StringIO()
211 211 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
212 212 self.assertEqual(ip.custom_exceptions, (NameError,))
213 213 ip.run_cell(u'a=abracadabra')
214 214 self.assertEqual(ip.custom_exceptions, ())
215 215 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
216 216 finally:
217 217 io.stderr = save_stderr
218 218
219 219 def test_drop_by_id(self):
220 220 myvars = {"a":object(), "b":object(), "c": object()}
221 221 ip.push(myvars, interactive=False)
222 222 for name in myvars:
223 223 assert name in ip.user_ns, name
224 224 assert name in ip.user_ns_hidden, name
225 225 ip.user_ns['b'] = 12
226 226 ip.drop_by_id(myvars)
227 227 for name in ["a", "c"]:
228 228 assert name not in ip.user_ns, name
229 229 assert name not in ip.user_ns_hidden, name
230 230 assert ip.user_ns['b'] == 12
231 231 ip.reset()
232 232
233 233 def test_var_expand(self):
234 234 ip.user_ns['f'] = u'Ca\xf1o'
235 235 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
236 236 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
237 237 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
238 238 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
239 239
240 240 ip.user_ns['f'] = b'Ca\xc3\xb1o'
241 241 # This should not raise any exception:
242 242 ip.var_expand(u'echo $f')
243 243
244 244 def test_var_expand_local(self):
245 245 """Test local variable expansion in !system and %magic calls"""
246 246 # !system
247 247 ip.run_cell('def test():\n'
248 248 ' lvar = "ttt"\n'
249 249 ' ret = !echo {lvar}\n'
250 250 ' return ret[0]\n')
251 251 res = ip.user_ns['test']()
252 252 nt.assert_in('ttt', res)
253 253
254 254 # %magic
255 255 ip.run_cell('def makemacro():\n'
256 256 ' macroname = "macro_var_expand_locals"\n'
257 257 ' %macro {macroname} codestr\n')
258 258 ip.user_ns['codestr'] = "str(12)"
259 259 ip.run_cell('makemacro()')
260 260 nt.assert_in('macro_var_expand_locals', ip.user_ns)
261 261
262 262 def test_bad_var_expand(self):
263 263 """var_expand on invalid formats shouldn't raise"""
264 264 # SyntaxError
265 265 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
266 266 # NameError
267 267 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
268 268 # ZeroDivisionError
269 269 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
270 270
271 271 def test_silent_nopostexec(self):
272 272 """run_cell(silent=True) doesn't invoke post-exec funcs"""
273 273 d = dict(called=False)
274 274 def set_called():
275 275 d['called'] = True
276 276
277 277 ip.register_post_execute(set_called)
278 278 ip.run_cell("1", silent=True)
279 279 self.assertFalse(d['called'])
280 280 # double-check that non-silent exec did what we expected
281 281 # silent to avoid
282 282 ip.run_cell("1")
283 283 self.assertTrue(d['called'])
284 284 # remove post-exec
285 285 ip._post_execute.pop(set_called)
286 286
287 287 def test_silent_noadvance(self):
288 288 """run_cell(silent=True) doesn't advance execution_count"""
289 289 ec = ip.execution_count
290 290 # silent should force store_history=False
291 291 ip.run_cell("1", store_history=True, silent=True)
292 292
293 293 self.assertEqual(ec, ip.execution_count)
294 294 # double-check that non-silent exec did what we expected
295 295 # silent to avoid
296 296 ip.run_cell("1", store_history=True)
297 297 self.assertEqual(ec+1, ip.execution_count)
298 298
299 299 def test_silent_nodisplayhook(self):
300 300 """run_cell(silent=True) doesn't trigger displayhook"""
301 301 d = dict(called=False)
302 302
303 303 trap = ip.display_trap
304 304 save_hook = trap.hook
305 305
306 306 def failing_hook(*args, **kwargs):
307 307 d['called'] = True
308 308
309 309 try:
310 310 trap.hook = failing_hook
311 311 ip.run_cell("1", silent=True)
312 312 self.assertFalse(d['called'])
313 313 # double-check that non-silent exec did what we expected
314 314 # silent to avoid
315 315 ip.run_cell("1")
316 316 self.assertTrue(d['called'])
317 317 finally:
318 318 trap.hook = save_hook
319 319
320 320 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
321 321 def test_print_softspace(self):
322 322 """Verify that softspace is handled correctly when executing multiple
323 323 statements.
324 324
325 325 In [1]: print 1; print 2
326 326 1
327 327 2
328 328
329 329 In [2]: print 1,; print 2
330 330 1 2
331 331 """
332 332
333 333 def test_ofind_line_magic(self):
334 334 from IPython.core.magic import register_line_magic
335 335
336 336 @register_line_magic
337 337 def lmagic(line):
338 338 "A line magic"
339 339
340 340 # Get info on line magic
341 341 lfind = ip._ofind('lmagic')
342 342 info = dict(found=True, isalias=False, ismagic=True,
343 343 namespace = 'IPython internal', obj= lmagic.__wrapped__,
344 344 parent = None)
345 345 nt.assert_equal(lfind, info)
346 346
347 347 def test_ofind_cell_magic(self):
348 348 from IPython.core.magic import register_cell_magic
349 349
350 350 @register_cell_magic
351 351 def cmagic(line, cell):
352 352 "A cell magic"
353 353
354 354 # Get info on cell magic
355 355 find = ip._ofind('cmagic')
356 356 info = dict(found=True, isalias=False, ismagic=True,
357 357 namespace = 'IPython internal', obj= cmagic.__wrapped__,
358 358 parent = None)
359 359 nt.assert_equal(find, info)
360 360
361 361 def test_custom_exception(self):
362 362 called = []
363 363 def my_handler(shell, etype, value, tb, tb_offset=None):
364 364 called.append(etype)
365 365 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
366 366
367 367 ip.set_custom_exc((ValueError,), my_handler)
368 368 try:
369 369 ip.run_cell("raise ValueError('test')")
370 370 # Check that this was called, and only once.
371 371 self.assertEqual(called, [ValueError])
372 372 finally:
373 373 # Reset the custom exception hook
374 374 ip.set_custom_exc((), None)
375 375
376 376
377 377 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
378 378
379 379 def setUp(self):
380 380 self.BASETESTDIR = tempfile.mkdtemp()
381 381 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
382 382 os.mkdir(self.TESTDIR)
383 383 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
384 384 sfile.write("pass\n")
385 385 self.oldpath = os.getcwdu()
386 386 os.chdir(self.TESTDIR)
387 387 self.fname = u"Γ₯Àâtestscript.py"
388 388
389 389 def tearDown(self):
390 390 os.chdir(self.oldpath)
391 391 shutil.rmtree(self.BASETESTDIR)
392 392
393 393 def test_1(self):
394 394 """Test safe_execfile with non-ascii path
395 395 """
396 396 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
397 397
398 398
399 399 class TestSystemRaw(unittest.TestCase):
400 400 def test_1(self):
401 401 """Test system_raw with non-ascii cmd
402 402 """
403 403 cmd = ur'''python -c "'Γ₯Àâ'" '''
404 404 ip.system_raw(cmd)
405 405
406 406 class TestModules(unittest.TestCase, tt.TempFileMixin):
407 407 def test_extraneous_loads(self):
408 408 """Test we're not loading modules on startup that we shouldn't.
409 409 """
410 410 self.mktmp("import sys\n"
411 411 "print('numpy' in sys.modules)\n"
412 412 "print('IPython.parallel' in sys.modules)\n"
413 413 "print('IPython.zmq' in sys.modules)\n"
414 414 )
415 415 out = "False\nFalse\nFalse\n"
416 416 tt.ipexec_validate(self.fname, out)
417 417
418 418 class Negator(ast.NodeTransformer):
419 419 """Negates all number literals in an AST."""
420 420 def visit_Num(self, node):
421 421 node.n = -node.n
422 422 return node
423 423
424 424 class TestAstTransform(unittest.TestCase):
425 425 def setUp(self):
426 426 self.negator = Negator()
427 427 ip.ast_transformers.append(self.negator)
428 428
429 429 def tearDown(self):
430 430 ip.ast_transformers.remove(self.negator)
431 431
432 432 def test_run_cell(self):
433 433 with tt.AssertPrints('-34'):
434 434 ip.run_cell('print (12 + 22)')
435 435
436 436 # A named reference to a number shouldn't be transformed.
437 437 ip.user_ns['n'] = 55
438 438 with tt.AssertNotPrints('-55'):
439 439 ip.run_cell('print (n)')
440 440
441 441 def test_timeit(self):
442 442 called = set()
443 443 def f(x):
444 444 called.add(x)
445 445 ip.push({'f':f})
446 446
447 447 with tt.AssertPrints("best of "):
448 448 ip.run_line_magic("timeit", "-n1 f(1)")
449 449 self.assertEqual(called, set([-1]))
450 450 called.clear()
451 451
452 452 with tt.AssertPrints("best of "):
453 453 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
454 454 self.assertEqual(called, set([-2, -3]))
455 455
456 456 def test_time(self):
457 457 called = []
458 458 def f(x):
459 459 called.append(x)
460 460 ip.push({'f':f})
461 461
462 462 # Test with an expression
463 463 with tt.AssertPrints("CPU times"):
464 464 ip.run_line_magic("time", "f(5+9)")
465 465 self.assertEqual(called, [-14])
466 466 called[:] = []
467 467
468 468 # Test with a statement (different code path)
469 469 with tt.AssertPrints("CPU times"):
470 470 ip.run_line_magic("time", "a = f(-3 + -2)")
471 471 self.assertEqual(called, [5])
472 472
473 def test_macro(self):
474 ip.push({'a':10})
475 # The AST transformation makes this do a+=-1
476 ip.define_macro("amacro", "a+=1\nprint(a)")
477
478 with tt.AssertPrints("9"):
479 ip.run_cell("amacro")
480 with tt.AssertPrints("8"):
481 ip.run_cell("amacro")
482
473 483 class IntegerWrapper(ast.NodeTransformer):
474 484 """Wraps all integers in a call to Integer()"""
475 485 def visit_Num(self, node):
476 486 if isinstance(node.n, int):
477 487 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
478 488 args=[node], keywords=[])
479 489
480 490 class TestAstTransform2(unittest.TestCase):
481 491 def setUp(self):
482 492 self.intwrapper = IntegerWrapper()
483 493 ip.ast_transformers.append(self.intwrapper)
484 494
485 495 self.calls = []
486 496 def Integer(*args):
487 497 self.calls.append(args)
488 498 return args
489 499 ip.push({"Integer": Integer})
490 500
491 501 def tearDown(self):
492 502 ip.ast_transformers.remove(self.intwrapper)
493 503 del ip.user_ns['Integer']
494 504
495 505 def test_run_cell(self):
496 506 ip.run_cell("n = 2")
497 507 self.assertEqual(self.calls, [(2,)])
498 508
499 509 def test_timeit(self):
500 510 called = set()
501 511 def f(x):
502 512 called.add(x)
503 513 ip.push({'f':f})
504 514
505 515 with tt.AssertPrints("best of "):
506 516 ip.run_line_magic("timeit", "-n1 f(1)")
507 517 self.assertEqual(called, set([(1,)]))
508 518 called.clear()
509 519
510 520 with tt.AssertPrints("best of "):
511 521 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
512 522 self.assertEqual(called, set([(2,), (3,)]))
513 523
514 524 class ErrorTransformer(ast.NodeTransformer):
515 525 """Throws an error when it sees a number."""
516 526 def visit_Num(self):
517 527 raise ValueError("test")
518 528
519 529 class TestAstTransformError(unittest.TestCase):
520 530 def test_unregistering(self):
521 531 err_transformer = ErrorTransformer()
522 532 ip.ast_transformers.append(err_transformer)
523 533
524 534 with tt.AssertPrints("unregister", channel='stderr'):
525 535 ip.run_cell("1 + 2")
526 536
527 537 # This should have been removed.
528 538 nt.assert_not_in(err_transformer, ip.ast_transformers)
529 539
530 540 def test__IPYTHON__():
531 541 # This shouldn't raise a NameError, that's all
532 542 __IPYTHON__
General Comments 0
You need to be logged in to leave comments. Login now