##// END OF EJS Templates
test that cell magic preempts other transformers
MinRK -
Show More
@@ -1,564 +1,580 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module.
3 3
4 4 Authors
5 5 -------
6 6 * Fernando Perez
7 7 * Robert Kern
8 8 """
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2010-2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19 # stdlib
20 20 import unittest
21 21 import sys
22 22
23 23 # Third party
24 24 import nose.tools as nt
25 25
26 26 # Our own
27 27 from IPython.core import inputsplitter as isp
28 28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 29 from IPython.testing import tools as tt
30 30 from IPython.utils import py3compat
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Semi-complete examples (also used as tests)
34 34 #-----------------------------------------------------------------------------
35 35
36 36 # Note: at the bottom, there's a slightly more complete version of this that
37 37 # can be useful during development of code here.
38 38
39 39 def mini_interactive_loop(input_func):
40 40 """Minimal example of the logic of an interactive interpreter loop.
41 41
42 42 This serves as an example, and it is used by the test system with a fake
43 43 raw_input that simulates interactive input."""
44 44
45 45 from IPython.core.inputsplitter import InputSplitter
46 46
47 47 isp = InputSplitter()
48 48 # In practice, this input loop would be wrapped in an outside loop to read
49 49 # input indefinitely, until some exit/quit command was issued. Here we
50 50 # only illustrate the basic inner loop.
51 51 while isp.push_accepts_more():
52 52 indent = ' '*isp.indent_spaces
53 53 prompt = '>>> ' + indent
54 54 line = indent + input_func(prompt)
55 55 isp.push(line)
56 56
57 57 # Here we just return input so we can use it in a test suite, but a real
58 58 # interpreter would instead send it for execution somewhere.
59 59 src = isp.source_reset()
60 60 #print 'Input source was:\n', src # dbg
61 61 return src
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Test utilities, just for local use
65 65 #-----------------------------------------------------------------------------
66 66
67 67 def assemble(block):
68 68 """Assemble a block into multi-line sub-blocks."""
69 69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
70 70
71 71
72 72 def pseudo_input(lines):
73 73 """Return a function that acts like raw_input but feeds the input list."""
74 74 ilines = iter(lines)
75 75 def raw_in(prompt):
76 76 try:
77 77 return next(ilines)
78 78 except StopIteration:
79 79 return ''
80 80 return raw_in
81 81
82 82 #-----------------------------------------------------------------------------
83 83 # Tests
84 84 #-----------------------------------------------------------------------------
85 85 def test_spaces():
86 86 tests = [('', 0),
87 87 (' ', 1),
88 88 ('\n', 0),
89 89 (' \n', 1),
90 90 ('x', 0),
91 91 (' x', 1),
92 92 (' x',2),
93 93 (' x',4),
94 94 # Note: tabs are counted as a single whitespace!
95 95 ('\tx', 1),
96 96 ('\t x', 2),
97 97 ]
98 98 tt.check_pairs(isp.num_ini_spaces, tests)
99 99
100 100
101 101 def test_remove_comments():
102 102 tests = [('text', 'text'),
103 103 ('text # comment', 'text '),
104 104 ('text # comment\n', 'text \n'),
105 105 ('text # comment \n', 'text \n'),
106 106 ('line # c \nline\n','line \nline\n'),
107 107 ('line # c \nline#c2 \nline\nline #c\n\n',
108 108 'line \nline\nline\nline \n\n'),
109 109 ]
110 110 tt.check_pairs(isp.remove_comments, tests)
111 111
112 112
113 113 def test_get_input_encoding():
114 114 encoding = isp.get_input_encoding()
115 115 nt.assert_true(isinstance(encoding, basestring))
116 116 # simple-minded check that at least encoding a simple string works with the
117 117 # encoding we got.
118 118 nt.assert_equal(u'test'.encode(encoding), b'test')
119 119
120 120
121 121 class NoInputEncodingTestCase(unittest.TestCase):
122 122 def setUp(self):
123 123 self.old_stdin = sys.stdin
124 124 class X: pass
125 125 fake_stdin = X()
126 126 sys.stdin = fake_stdin
127 127
128 128 def test(self):
129 129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
130 130 # thing
131 131 enc = isp.get_input_encoding()
132 132 self.assertEqual(enc, 'ascii')
133 133
134 134 def tearDown(self):
135 135 sys.stdin = self.old_stdin
136 136
137 137
138 138 class InputSplitterTestCase(unittest.TestCase):
139 139 def setUp(self):
140 140 self.isp = isp.InputSplitter()
141 141
142 142 def test_reset(self):
143 143 isp = self.isp
144 144 isp.push('x=1')
145 145 isp.reset()
146 146 self.assertEqual(isp._buffer, [])
147 147 self.assertEqual(isp.indent_spaces, 0)
148 148 self.assertEqual(isp.source, '')
149 149 self.assertEqual(isp.code, None)
150 150 self.assertEqual(isp._is_complete, False)
151 151
152 152 def test_source(self):
153 153 self.isp._store('1')
154 154 self.isp._store('2')
155 155 self.assertEqual(self.isp.source, '1\n2\n')
156 156 self.assertTrue(len(self.isp._buffer)>0)
157 157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
158 158 self.assertEqual(self.isp._buffer, [])
159 159 self.assertEqual(self.isp.source, '')
160 160
161 161 def test_indent(self):
162 162 isp = self.isp # shorthand
163 163 isp.push('x=1')
164 164 self.assertEqual(isp.indent_spaces, 0)
165 165 isp.push('if 1:\n x=1')
166 166 self.assertEqual(isp.indent_spaces, 4)
167 167 isp.push('y=2\n')
168 168 self.assertEqual(isp.indent_spaces, 0)
169 169
170 170 def test_indent2(self):
171 171 isp = self.isp
172 172 isp.push('if 1:')
173 173 self.assertEqual(isp.indent_spaces, 4)
174 174 isp.push(' x=1')
175 175 self.assertEqual(isp.indent_spaces, 4)
176 176 # Blank lines shouldn't change the indent level
177 177 isp.push(' '*2)
178 178 self.assertEqual(isp.indent_spaces, 4)
179 179
180 180 def test_indent3(self):
181 181 isp = self.isp
182 182 # When a multiline statement contains parens or multiline strings, we
183 183 # shouldn't get confused.
184 184 isp.push("if 1:")
185 185 isp.push(" x = (1+\n 2)")
186 186 self.assertEqual(isp.indent_spaces, 4)
187 187
188 188 def test_indent4(self):
189 189 isp = self.isp
190 190 # whitespace after ':' should not screw up indent level
191 191 isp.push('if 1: \n x=1')
192 192 self.assertEqual(isp.indent_spaces, 4)
193 193 isp.push('y=2\n')
194 194 self.assertEqual(isp.indent_spaces, 0)
195 195 isp.push('if 1:\t\n x=1')
196 196 self.assertEqual(isp.indent_spaces, 4)
197 197 isp.push('y=2\n')
198 198 self.assertEqual(isp.indent_spaces, 0)
199 199
200 200 def test_dedent_pass(self):
201 201 isp = self.isp # shorthand
202 202 # should NOT cause dedent
203 203 isp.push('if 1:\n passes = 5')
204 204 self.assertEqual(isp.indent_spaces, 4)
205 205 isp.push('if 1:\n pass')
206 206 self.assertEqual(isp.indent_spaces, 0)
207 207 isp.push('if 1:\n pass ')
208 208 self.assertEqual(isp.indent_spaces, 0)
209 209
210 210 def test_dedent_break(self):
211 211 isp = self.isp # shorthand
212 212 # should NOT cause dedent
213 213 isp.push('while 1:\n breaks = 5')
214 214 self.assertEqual(isp.indent_spaces, 4)
215 215 isp.push('while 1:\n break')
216 216 self.assertEqual(isp.indent_spaces, 0)
217 217 isp.push('while 1:\n break ')
218 218 self.assertEqual(isp.indent_spaces, 0)
219 219
220 220 def test_dedent_continue(self):
221 221 isp = self.isp # shorthand
222 222 # should NOT cause dedent
223 223 isp.push('while 1:\n continues = 5')
224 224 self.assertEqual(isp.indent_spaces, 4)
225 225 isp.push('while 1:\n continue')
226 226 self.assertEqual(isp.indent_spaces, 0)
227 227 isp.push('while 1:\n continue ')
228 228 self.assertEqual(isp.indent_spaces, 0)
229 229
230 230 def test_dedent_raise(self):
231 231 isp = self.isp # shorthand
232 232 # should NOT cause dedent
233 233 isp.push('if 1:\n raised = 4')
234 234 self.assertEqual(isp.indent_spaces, 4)
235 235 isp.push('if 1:\n raise TypeError()')
236 236 self.assertEqual(isp.indent_spaces, 0)
237 237 isp.push('if 1:\n raise')
238 238 self.assertEqual(isp.indent_spaces, 0)
239 239 isp.push('if 1:\n raise ')
240 240 self.assertEqual(isp.indent_spaces, 0)
241 241
242 242 def test_dedent_return(self):
243 243 isp = self.isp # shorthand
244 244 # should NOT cause dedent
245 245 isp.push('if 1:\n returning = 4')
246 246 self.assertEqual(isp.indent_spaces, 4)
247 247 isp.push('if 1:\n return 5 + 493')
248 248 self.assertEqual(isp.indent_spaces, 0)
249 249 isp.push('if 1:\n return')
250 250 self.assertEqual(isp.indent_spaces, 0)
251 251 isp.push('if 1:\n return ')
252 252 self.assertEqual(isp.indent_spaces, 0)
253 253 isp.push('if 1:\n return(0)')
254 254 self.assertEqual(isp.indent_spaces, 0)
255 255
256 256 def test_push(self):
257 257 isp = self.isp
258 258 self.assertTrue(isp.push('x=1'))
259 259
260 260 def test_push2(self):
261 261 isp = self.isp
262 262 self.assertFalse(isp.push('if 1:'))
263 263 for line in [' x=1', '# a comment', ' y=2']:
264 264 print(line)
265 265 self.assertTrue(isp.push(line))
266 266
267 267 def test_push3(self):
268 268 isp = self.isp
269 269 isp.push('if True:')
270 270 isp.push(' a = 1')
271 271 self.assertFalse(isp.push('b = [1,'))
272 272
273 273 def test_push_accepts_more(self):
274 274 isp = self.isp
275 275 isp.push('x=1')
276 276 self.assertFalse(isp.push_accepts_more())
277 277
278 278 def test_push_accepts_more2(self):
279 279 isp = self.isp
280 280 isp.push('if 1:')
281 281 self.assertTrue(isp.push_accepts_more())
282 282 isp.push(' x=1')
283 283 self.assertTrue(isp.push_accepts_more())
284 284 isp.push('')
285 285 self.assertFalse(isp.push_accepts_more())
286 286
287 287 def test_push_accepts_more3(self):
288 288 isp = self.isp
289 289 isp.push("x = (2+\n3)")
290 290 self.assertFalse(isp.push_accepts_more())
291 291
292 292 def test_push_accepts_more4(self):
293 293 isp = self.isp
294 294 # When a multiline statement contains parens or multiline strings, we
295 295 # shouldn't get confused.
296 296 # FIXME: we should be able to better handle de-dents in statements like
297 297 # multiline strings and multiline expressions (continued with \ or
298 298 # parens). Right now we aren't handling the indentation tracking quite
299 299 # correctly with this, though in practice it may not be too much of a
300 300 # problem. We'll need to see.
301 301 isp.push("if 1:")
302 302 isp.push(" x = (2+")
303 303 isp.push(" 3)")
304 304 self.assertTrue(isp.push_accepts_more())
305 305 isp.push(" y = 3")
306 306 self.assertTrue(isp.push_accepts_more())
307 307 isp.push('')
308 308 self.assertFalse(isp.push_accepts_more())
309 309
310 310 def test_push_accepts_more5(self):
311 311 isp = self.isp
312 312 isp.push('try:')
313 313 isp.push(' a = 5')
314 314 isp.push('except:')
315 315 isp.push(' raise')
316 316 # We want to be able to add an else: block at this point, so it should
317 317 # wait for a blank line.
318 318 self.assertTrue(isp.push_accepts_more())
319 319
320 320 def test_continuation(self):
321 321 isp = self.isp
322 322 isp.push("import os, \\")
323 323 self.assertTrue(isp.push_accepts_more())
324 324 isp.push("sys")
325 325 self.assertFalse(isp.push_accepts_more())
326 326
327 327 def test_syntax_error(self):
328 328 isp = self.isp
329 329 # Syntax errors immediately produce a 'ready' block, so the invalid
330 330 # Python can be sent to the kernel for evaluation with possible ipython
331 331 # special-syntax conversion.
332 332 isp.push('run foo')
333 333 self.assertFalse(isp.push_accepts_more())
334 334
335 335 def test_unicode(self):
336 336 self.isp.push(u"PΓ©rez")
337 337 self.isp.push(u'\xc3\xa9')
338 338 self.isp.push(u"u'\xc3\xa9'")
339 339
340 340 def test_line_continuation(self):
341 341 """ Test issue #2108."""
342 342 isp = self.isp
343 343 # A blank line after a line continuation should not accept more
344 344 isp.push("1 \\\n\n")
345 345 self.assertFalse(isp.push_accepts_more())
346 346 # Whitespace after a \ is a SyntaxError. The only way to test that
347 347 # here is to test that push doesn't accept more (as with
348 348 # test_syntax_error() above).
349 349 isp.push(r"1 \ ")
350 350 self.assertFalse(isp.push_accepts_more())
351 351 # Even if the line is continuable (c.f. the regular Python
352 352 # interpreter)
353 353 isp.push(r"(1 \ ")
354 354 self.assertFalse(isp.push_accepts_more())
355 355
356 356 class InteractiveLoopTestCase(unittest.TestCase):
357 357 """Tests for an interactive loop like a python shell.
358 358 """
359 359 def check_ns(self, lines, ns):
360 360 """Validate that the given input lines produce the resulting namespace.
361 361
362 362 Note: the input lines are given exactly as they would be typed in an
363 363 auto-indenting environment, as mini_interactive_loop above already does
364 364 auto-indenting and prepends spaces to the input.
365 365 """
366 366 src = mini_interactive_loop(pseudo_input(lines))
367 367 test_ns = {}
368 368 exec src in test_ns
369 369 # We can't check that the provided ns is identical to the test_ns,
370 370 # because Python fills test_ns with extra keys (copyright, etc). But
371 371 # we can check that the given dict is *contained* in test_ns
372 372 for k,v in ns.iteritems():
373 373 self.assertEqual(test_ns[k], v)
374 374
375 375 def test_simple(self):
376 376 self.check_ns(['x=1'], dict(x=1))
377 377
378 378 def test_simple2(self):
379 379 self.check_ns(['if 1:', 'x=2'], dict(x=2))
380 380
381 381 def test_xy(self):
382 382 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
383 383
384 384 def test_abc(self):
385 385 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
386 386
387 387 def test_multi(self):
388 388 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
389 389
390 390
391 391 class IPythonInputTestCase(InputSplitterTestCase):
392 392 """By just creating a new class whose .isp is a different instance, we
393 393 re-run the same test battery on the new input splitter.
394 394
395 395 In addition, this runs the tests over the syntax and syntax_ml dicts that
396 396 were tested by individual functions, as part of the OO interface.
397 397
398 398 It also makes some checks on the raw buffer storage.
399 399 """
400 400
401 401 def setUp(self):
402 402 self.isp = isp.IPythonInputSplitter()
403 403
404 404 def test_syntax(self):
405 405 """Call all single-line syntax tests from the main object"""
406 406 isp = self.isp
407 407 for example in syntax.itervalues():
408 408 for raw, out_t in example:
409 409 if raw.startswith(' '):
410 410 continue
411 411
412 412 isp.push(raw+'\n')
413 413 out, out_raw = isp.source_raw_reset()
414 414 self.assertEqual(out.rstrip(), out_t,
415 415 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
416 416 self.assertEqual(out_raw.rstrip(), raw.rstrip())
417 417
418 418 def test_syntax_multiline(self):
419 419 isp = self.isp
420 420 for example in syntax_ml.itervalues():
421 421 for line_pairs in example:
422 422 out_t_parts = []
423 423 raw_parts = []
424 424 for lraw, out_t_part in line_pairs:
425 425 if out_t_part is not None:
426 426 out_t_parts.append(out_t_part)
427 427
428 428 if lraw is not None:
429 429 isp.push(lraw)
430 430 raw_parts.append(lraw)
431 431
432 432 out, out_raw = isp.source_raw_reset()
433 433 out_t = '\n'.join(out_t_parts).rstrip()
434 434 raw = '\n'.join(raw_parts).rstrip()
435 435 self.assertEqual(out.rstrip(), out_t)
436 436 self.assertEqual(out_raw.rstrip(), raw)
437 437
438 438 def test_syntax_multiline_cell(self):
439 439 isp = self.isp
440 440 for example in syntax_ml.itervalues():
441 441
442 442 out_t_parts = []
443 443 for line_pairs in example:
444 444 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
445 445 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
446 446 out = isp.transform_cell(raw)
447 447 # Match ignoring trailing whitespace
448 448 self.assertEqual(out.rstrip(), out_t.rstrip())
449
450 def test_cellmagic_preempt(self):
451 isp = self.isp
452 for raw, name, line, cell in [
453 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
454 ("%%cellm \n...:", u'cellm', u'', u'...:'),
455 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
456 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
457 ]:
458 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
459 name, line, cell
460 )
461 out = isp.transform_cell(raw)
462 self.assertEqual(out.rstrip(), expected.rstrip())
463
464
449 465
450 466 #-----------------------------------------------------------------------------
451 467 # Main - use as a script, mostly for developer experiments
452 468 #-----------------------------------------------------------------------------
453 469
454 470 if __name__ == '__main__':
455 471 # A simple demo for interactive experimentation. This code will not get
456 472 # picked up by any test suite.
457 473 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
458 474
459 475 # configure here the syntax to use, prompt and whether to autoindent
460 476 #isp, start_prompt = InputSplitter(), '>>> '
461 477 isp, start_prompt = IPythonInputSplitter(), 'In> '
462 478
463 479 autoindent = True
464 480 #autoindent = False
465 481
466 482 try:
467 483 while True:
468 484 prompt = start_prompt
469 485 while isp.push_accepts_more():
470 486 indent = ' '*isp.indent_spaces
471 487 if autoindent:
472 488 line = indent + raw_input(prompt+indent)
473 489 else:
474 490 line = raw_input(prompt)
475 491 isp.push(line)
476 492 prompt = '... '
477 493
478 494 # Here we just return input so we can use it in a test suite, but a
479 495 # real interpreter would instead send it for execution somewhere.
480 496 #src = isp.source; raise EOFError # dbg
481 497 src, raw = isp.source_raw_reset()
482 498 print 'Input source was:\n', src
483 499 print 'Raw source was:\n', raw
484 500 except EOFError:
485 501 print 'Bye'
486 502
487 503 # Tests for cell magics support
488 504
489 505 def test_last_blank():
490 506 nt.assert_false(isp.last_blank(''))
491 507 nt.assert_false(isp.last_blank('abc'))
492 508 nt.assert_false(isp.last_blank('abc\n'))
493 509 nt.assert_false(isp.last_blank('abc\na'))
494 510
495 511 nt.assert_true(isp.last_blank('\n'))
496 512 nt.assert_true(isp.last_blank('\n '))
497 513 nt.assert_true(isp.last_blank('abc\n '))
498 514 nt.assert_true(isp.last_blank('abc\n\n'))
499 515 nt.assert_true(isp.last_blank('abc\nd\n\n'))
500 516 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
501 517 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
502 518
503 519
504 520 def test_last_two_blanks():
505 521 nt.assert_false(isp.last_two_blanks(''))
506 522 nt.assert_false(isp.last_two_blanks('abc'))
507 523 nt.assert_false(isp.last_two_blanks('abc\n'))
508 524 nt.assert_false(isp.last_two_blanks('abc\n\na'))
509 525 nt.assert_false(isp.last_two_blanks('abc\n \n'))
510 526 nt.assert_false(isp.last_two_blanks('abc\n\n'))
511 527
512 528 nt.assert_true(isp.last_two_blanks('\n\n'))
513 529 nt.assert_true(isp.last_two_blanks('\n\n '))
514 530 nt.assert_true(isp.last_two_blanks('\n \n'))
515 531 nt.assert_true(isp.last_two_blanks('abc\n\n '))
516 532 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
517 533 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
518 534 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
519 535 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
520 536 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
521 537 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
522 538
523 539
524 540 class CellMagicsCommon(object):
525 541
526 542 def test_whole_cell(self):
527 543 src = "%%cellm line\nbody\n"
528 544 sp = self.sp
529 545 sp.push(src)
530 546 out = sp.source_reset()
531 547 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
532 548 nt.assert_equal(out, py3compat.u_format(ref))
533 549
534 550 def test_cellmagic_help(self):
535 551 self.sp.push('%%cellm?')
536 552 nt.assert_false(self.sp.push_accepts_more())
537 553
538 554 def tearDown(self):
539 555 self.sp.reset()
540 556
541 557
542 558 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
543 559 sp = isp.IPythonInputSplitter(line_input_checker=False)
544 560
545 561 def test_incremental(self):
546 562 sp = self.sp
547 563 sp.push('%%cellm firstline\n')
548 564 nt.assert_true(sp.push_accepts_more()) #1
549 565 sp.push('line2\n')
550 566 nt.assert_true(sp.push_accepts_more()) #2
551 567 sp.push('\n')
552 568 # This should accept a blank line and carry on until the cell is reset
553 569 nt.assert_true(sp.push_accepts_more()) #3
554 570
555 571 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
556 572 sp = isp.IPythonInputSplitter(line_input_checker=True)
557 573
558 574 def test_incremental(self):
559 575 sp = self.sp
560 576 sp.push('%%cellm line2\n')
561 577 nt.assert_true(sp.push_accepts_more()) #1
562 578 sp.push('\n')
563 579 # In this case, a blank line should end the cell magic
564 580 nt.assert_false(sp.push_accepts_more()) #2
General Comments 0
You need to be logged in to leave comments. Login now