##// END OF EJS Templates
Backport PR #8843: don't strip encoding cookie from cell magics...
Min RK -
Show More
@@ -1,682 +1,682 b''
1 1 """Input handling and transformation machinery.
2 2
3 3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 4 input from a line-oriented frontend is complete and should be executed, and when
5 5 the user should be prompted for another line of code instead. The name 'input
6 6 splitter' is largely for historical reasons.
7 7
8 8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 9 with full support for the extended IPython syntax (magics, system calls, etc).
10 10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 12 and stores the results.
13 13
14 14 For more details, see the class docstrings below.
15 15 """
16 16
17 17 # Copyright (c) IPython Development Team.
18 18 # Distributed under the terms of the Modified BSD License.
19 19 import ast
20 20 import codeop
21 21 import re
22 22 import sys
23 23 import warnings
24 24
25 25 from IPython.utils.py3compat import cast_unicode
26 26 from IPython.core.inputtransformer import (leading_indent,
27 27 classic_prompt,
28 28 ipy_prompt,
29 29 strip_encoding_cookie,
30 30 cellmagic,
31 31 assemble_logical_lines,
32 32 help_end,
33 33 escaped_commands,
34 34 assign_from_magic,
35 35 assign_from_system,
36 36 assemble_python_lines,
37 37 )
38 38
39 39 # These are available in this module for backwards compatibility.
40 40 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
41 41 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
42 42 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Utilities
46 46 #-----------------------------------------------------------------------------
47 47
48 48 # FIXME: These are general-purpose utilities that later can be moved to the
49 49 # general ward. Kept here for now because we're being very strict about test
50 50 # coverage with this code, and this lets us ensure that we keep 100% coverage
51 51 # while developing.
52 52
53 53 # compiled regexps for autoindent management
54 54 dedent_re = re.compile('|'.join([
55 55 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
56 56 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
57 57 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
58 58 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
59 59 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
60 60 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
61 61 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
62 62 ]))
63 63 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
64 64
65 65 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
66 66 # before pure comments
67 67 comment_line_re = re.compile('^\s*\#')
68 68
69 69
70 70 def num_ini_spaces(s):
71 71 """Return the number of initial spaces in a string.
72 72
73 73 Note that tabs are counted as a single space. For now, we do *not* support
74 74 mixing of tabs and spaces in the user's input.
75 75
76 76 Parameters
77 77 ----------
78 78 s : string
79 79
80 80 Returns
81 81 -------
82 82 n : int
83 83 """
84 84
85 85 ini_spaces = ini_spaces_re.match(s)
86 86 if ini_spaces:
87 87 return ini_spaces.end()
88 88 else:
89 89 return 0
90 90
91 91 def last_blank(src):
92 92 """Determine if the input source ends in a blank.
93 93
94 94 A blank is either a newline or a line consisting of whitespace.
95 95
96 96 Parameters
97 97 ----------
98 98 src : string
99 99 A single or multiline string.
100 100 """
101 101 if not src: return False
102 102 ll = src.splitlines()[-1]
103 103 return (ll == '') or ll.isspace()
104 104
105 105
106 106 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
107 107 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
108 108
109 109 def last_two_blanks(src):
110 110 """Determine if the input source ends in two blanks.
111 111
112 112 A blank is either a newline or a line consisting of whitespace.
113 113
114 114 Parameters
115 115 ----------
116 116 src : string
117 117 A single or multiline string.
118 118 """
119 119 if not src: return False
120 120 # The logic here is tricky: I couldn't get a regexp to work and pass all
121 121 # the tests, so I took a different approach: split the source by lines,
122 122 # grab the last two and prepend '###\n' as a stand-in for whatever was in
123 123 # the body before the last two lines. Then, with that structure, it's
124 124 # possible to analyze with two regexps. Not the most elegant solution, but
125 125 # it works. If anyone tries to change this logic, make sure to validate
126 126 # the whole test suite first!
127 127 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
128 128 return (bool(last_two_blanks_re.match(new_src)) or
129 129 bool(last_two_blanks_re2.match(new_src)) )
130 130
131 131
132 132 def remove_comments(src):
133 133 """Remove all comments from input source.
134 134
135 135 Note: comments are NOT recognized inside of strings!
136 136
137 137 Parameters
138 138 ----------
139 139 src : string
140 140 A single or multiline input string.
141 141
142 142 Returns
143 143 -------
144 144 String with all Python comments removed.
145 145 """
146 146
147 147 return re.sub('#.*', '', src)
148 148
149 149
150 150 def get_input_encoding():
151 151 """Return the default standard input encoding.
152 152
153 153 If sys.stdin has no encoding, 'ascii' is returned."""
154 154 # There are strange environments for which sys.stdin.encoding is None. We
155 155 # ensure that a valid encoding is returned.
156 156 encoding = getattr(sys.stdin, 'encoding', None)
157 157 if encoding is None:
158 158 encoding = 'ascii'
159 159 return encoding
160 160
161 161 #-----------------------------------------------------------------------------
162 162 # Classes and functions for normal Python syntax handling
163 163 #-----------------------------------------------------------------------------
164 164
165 165 class InputSplitter(object):
166 166 r"""An object that can accumulate lines of Python source before execution.
167 167
168 168 This object is designed to be fed python source line-by-line, using
169 169 :meth:`push`. It will return on each push whether the currently pushed
170 170 code could be executed already. In addition, it provides a method called
171 171 :meth:`push_accepts_more` that can be used to query whether more input
172 172 can be pushed into a single interactive block.
173 173
174 174 This is a simple example of how an interactive terminal-based client can use
175 175 this tool::
176 176
177 177 isp = InputSplitter()
178 178 while isp.push_accepts_more():
179 179 indent = ' '*isp.indent_spaces
180 180 prompt = '>>> ' + indent
181 181 line = indent + raw_input(prompt)
182 182 isp.push(line)
183 183 print 'Input source was:\n', isp.source_reset(),
184 184 """
185 185 # Number of spaces of indentation computed from input that has been pushed
186 186 # so far. This is the attributes callers should query to get the current
187 187 # indentation level, in order to provide auto-indent facilities.
188 188 indent_spaces = 0
189 189 # String, indicating the default input encoding. It is computed by default
190 190 # at initialization time via get_input_encoding(), but it can be reset by a
191 191 # client with specific knowledge of the encoding.
192 192 encoding = ''
193 193 # String where the current full source input is stored, properly encoded.
194 194 # Reading this attribute is the normal way of querying the currently pushed
195 195 # source code, that has been properly encoded.
196 196 source = ''
197 197 # Code object corresponding to the current source. It is automatically
198 198 # synced to the source, so it can be queried at any time to obtain the code
199 199 # object; it will be None if the source doesn't compile to valid Python.
200 200 code = None
201 201
202 202 # Private attributes
203 203
204 204 # List with lines of input accumulated so far
205 205 _buffer = None
206 206 # Command compiler
207 207 _compile = None
208 208 # Mark when input has changed indentation all the way back to flush-left
209 209 _full_dedent = False
210 210 # Boolean indicating whether the current block is complete
211 211 _is_complete = None
212 212 # Boolean indicating whether the current block has an unrecoverable syntax error
213 213 _is_invalid = False
214 214
215 215 def __init__(self):
216 216 """Create a new InputSplitter instance.
217 217 """
218 218 self._buffer = []
219 219 self._compile = codeop.CommandCompiler()
220 220 self.encoding = get_input_encoding()
221 221
222 222 def reset(self):
223 223 """Reset the input buffer and associated state."""
224 224 self.indent_spaces = 0
225 225 self._buffer[:] = []
226 226 self.source = ''
227 227 self.code = None
228 228 self._is_complete = False
229 229 self._is_invalid = False
230 230 self._full_dedent = False
231 231
232 232 def source_reset(self):
233 233 """Return the input source and perform a full reset.
234 234 """
235 235 out = self.source
236 236 self.reset()
237 237 return out
238 238
239 239 def check_complete(self, source):
240 240 """Return whether a block of code is ready to execute, or should be continued
241 241
242 242 This is a non-stateful API, and will reset the state of this InputSplitter.
243 243
244 244 Parameters
245 245 ----------
246 246 source : string
247 247 Python input code, which can be multiline.
248 248
249 249 Returns
250 250 -------
251 251 status : str
252 252 One of 'complete', 'incomplete', or 'invalid' if source is not a
253 253 prefix of valid code.
254 254 indent_spaces : int or None
255 255 The number of spaces by which to indent the next line of code. If
256 256 status is not 'incomplete', this is None.
257 257 """
258 258 self.reset()
259 259 try:
260 260 self.push(source)
261 261 except SyntaxError:
262 262 # Transformers in IPythonInputSplitter can raise SyntaxError,
263 263 # which push() will not catch.
264 264 return 'invalid', None
265 265 else:
266 266 if self._is_invalid:
267 267 return 'invalid', None
268 268 elif self.push_accepts_more():
269 269 return 'incomplete', self.indent_spaces
270 270 else:
271 271 return 'complete', None
272 272 finally:
273 273 self.reset()
274 274
275 275 def push(self, lines):
276 276 """Push one or more lines of input.
277 277
278 278 This stores the given lines and returns a status code indicating
279 279 whether the code forms a complete Python block or not.
280 280
281 281 Any exceptions generated in compilation are swallowed, but if an
282 282 exception was produced, the method returns True.
283 283
284 284 Parameters
285 285 ----------
286 286 lines : string
287 287 One or more lines of Python input.
288 288
289 289 Returns
290 290 -------
291 291 is_complete : boolean
292 292 True if the current input source (the result of the current input
293 293 plus prior inputs) forms a complete Python execution block. Note that
294 294 this value is also stored as a private attribute (``_is_complete``), so it
295 295 can be queried at any time.
296 296 """
297 297 self._store(lines)
298 298 source = self.source
299 299
300 300 # Before calling _compile(), reset the code object to None so that if an
301 301 # exception is raised in compilation, we don't mislead by having
302 302 # inconsistent code/source attributes.
303 303 self.code, self._is_complete = None, None
304 304 self._is_invalid = False
305 305
306 306 # Honor termination lines properly
307 307 if source.endswith('\\\n'):
308 308 return False
309 309
310 310 self._update_indent(lines)
311 311 try:
312 312 with warnings.catch_warnings():
313 313 warnings.simplefilter('error', SyntaxWarning)
314 314 self.code = self._compile(source, symbol="exec")
315 315 # Invalid syntax can produce any of a number of different errors from
316 316 # inside the compiler, so we have to catch them all. Syntax errors
317 317 # immediately produce a 'ready' block, so the invalid Python can be
318 318 # sent to the kernel for evaluation with possible ipython
319 319 # special-syntax conversion.
320 320 except (SyntaxError, OverflowError, ValueError, TypeError,
321 321 MemoryError, SyntaxWarning):
322 322 self._is_complete = True
323 323 self._is_invalid = True
324 324 else:
325 325 # Compilation didn't produce any exceptions (though it may not have
326 326 # given a complete code object)
327 327 self._is_complete = self.code is not None
328 328
329 329 return self._is_complete
330 330
331 331 def push_accepts_more(self):
332 332 """Return whether a block of interactive input can accept more input.
333 333
334 334 This method is meant to be used by line-oriented frontends, who need to
335 335 guess whether a block is complete or not based solely on prior and
336 336 current input lines. The InputSplitter considers it has a complete
337 337 interactive block and will not accept more input when either:
338 338
339 339 * A SyntaxError is raised
340 340
341 341 * The code is complete and consists of a single line or a single
342 342 non-compound statement
343 343
344 344 * The code is complete and has a blank line at the end
345 345
346 346 If the current input produces a syntax error, this method immediately
347 347 returns False but does *not* raise the syntax error exception, as
348 348 typically clients will want to send invalid syntax to an execution
349 349 backend which might convert the invalid syntax into valid Python via
350 350 one of the dynamic IPython mechanisms.
351 351 """
352 352
353 353 # With incomplete input, unconditionally accept more
354 354 # A syntax error also sets _is_complete to True - see push()
355 355 if not self._is_complete:
356 356 #print("Not complete") # debug
357 357 return True
358 358
359 359 # The user can make any (complete) input execute by leaving a blank line
360 360 last_line = self.source.splitlines()[-1]
361 361 if (not last_line) or last_line.isspace():
362 362 #print("Blank line") # debug
363 363 return False
364 364
365 365 # If there's just a single line or AST node, and we're flush left, as is
366 366 # the case after a simple statement such as 'a=1', we want to execute it
367 367 # straight away.
368 368 if self.indent_spaces==0:
369 369 if len(self.source.splitlines()) <= 1:
370 370 return False
371 371
372 372 try:
373 373 code_ast = ast.parse(u''.join(self._buffer))
374 374 except Exception:
375 375 #print("Can't parse AST") # debug
376 376 return False
377 377 else:
378 378 if len(code_ast.body) == 1 and \
379 379 not hasattr(code_ast.body[0], 'body'):
380 380 #print("Simple statement") # debug
381 381 return False
382 382
383 383 # General fallback - accept more code
384 384 return True
385 385
386 386 #------------------------------------------------------------------------
387 387 # Private interface
388 388 #------------------------------------------------------------------------
389 389
390 390 def _find_indent(self, line):
391 391 """Compute the new indentation level for a single line.
392 392
393 393 Parameters
394 394 ----------
395 395 line : str
396 396 A single new line of non-whitespace, non-comment Python input.
397 397
398 398 Returns
399 399 -------
400 400 indent_spaces : int
401 401 New value for the indent level (it may be equal to self.indent_spaces
402 402 if indentation doesn't change.
403 403
404 404 full_dedent : boolean
405 405 Whether the new line causes a full flush-left dedent.
406 406 """
407 407 indent_spaces = self.indent_spaces
408 408 full_dedent = self._full_dedent
409 409
410 410 inisp = num_ini_spaces(line)
411 411 if inisp < indent_spaces:
412 412 indent_spaces = inisp
413 413 if indent_spaces <= 0:
414 414 #print 'Full dedent in text',self.source # dbg
415 415 full_dedent = True
416 416
417 417 if line.rstrip()[-1] == ':':
418 418 indent_spaces += 4
419 419 elif dedent_re.match(line):
420 420 indent_spaces -= 4
421 421 if indent_spaces <= 0:
422 422 full_dedent = True
423 423
424 424 # Safety
425 425 if indent_spaces < 0:
426 426 indent_spaces = 0
427 427 #print 'safety' # dbg
428 428
429 429 return indent_spaces, full_dedent
430 430
431 431 def _update_indent(self, lines):
432 432 for line in remove_comments(lines).splitlines():
433 433 if line and not line.isspace():
434 434 self.indent_spaces, self._full_dedent = self._find_indent(line)
435 435
436 436 def _store(self, lines, buffer=None, store='source'):
437 437 """Store one or more lines of input.
438 438
439 439 If input lines are not newline-terminated, a newline is automatically
440 440 appended."""
441 441
442 442 if buffer is None:
443 443 buffer = self._buffer
444 444
445 445 if lines.endswith('\n'):
446 446 buffer.append(lines)
447 447 else:
448 448 buffer.append(lines+'\n')
449 449 setattr(self, store, self._set_source(buffer))
450 450
451 451 def _set_source(self, buffer):
452 452 return u''.join(buffer)
453 453
454 454
455 455 class IPythonInputSplitter(InputSplitter):
456 456 """An input splitter that recognizes all of IPython's special syntax."""
457 457
458 458 # String with raw, untransformed input.
459 459 source_raw = ''
460 460
461 461 # Flag to track when a transformer has stored input that it hasn't given
462 462 # back yet.
463 463 transformer_accumulating = False
464 464
465 465 # Flag to track when assemble_python_lines has stored input that it hasn't
466 466 # given back yet.
467 467 within_python_line = False
468 468
469 469 # Private attributes
470 470
471 471 # List with lines of raw input accumulated so far.
472 472 _buffer_raw = None
473 473
474 474 def __init__(self, line_input_checker=True, physical_line_transforms=None,
475 475 logical_line_transforms=None, python_line_transforms=None):
476 476 super(IPythonInputSplitter, self).__init__()
477 477 self._buffer_raw = []
478 478 self._validate = True
479 479
480 480 if physical_line_transforms is not None:
481 481 self.physical_line_transforms = physical_line_transforms
482 482 else:
483 483 self.physical_line_transforms = [
484 484 leading_indent(),
485 485 classic_prompt(),
486 486 ipy_prompt(),
487 strip_encoding_cookie(),
488 487 cellmagic(end_on_blank_line=line_input_checker),
488 strip_encoding_cookie(),
489 489 ]
490 490
491 491 self.assemble_logical_lines = assemble_logical_lines()
492 492 if logical_line_transforms is not None:
493 493 self.logical_line_transforms = logical_line_transforms
494 494 else:
495 495 self.logical_line_transforms = [
496 496 help_end(),
497 497 escaped_commands(),
498 498 assign_from_magic(),
499 499 assign_from_system(),
500 500 ]
501 501
502 502 self.assemble_python_lines = assemble_python_lines()
503 503 if python_line_transforms is not None:
504 504 self.python_line_transforms = python_line_transforms
505 505 else:
506 506 # We don't use any of these at present
507 507 self.python_line_transforms = []
508 508
509 509 @property
510 510 def transforms(self):
511 511 "Quick access to all transformers."
512 512 return self.physical_line_transforms + \
513 513 [self.assemble_logical_lines] + self.logical_line_transforms + \
514 514 [self.assemble_python_lines] + self.python_line_transforms
515 515
516 516 @property
517 517 def transforms_in_use(self):
518 518 """Transformers, excluding logical line transformers if we're in a
519 519 Python line."""
520 520 t = self.physical_line_transforms[:]
521 521 if not self.within_python_line:
522 522 t += [self.assemble_logical_lines] + self.logical_line_transforms
523 523 return t + [self.assemble_python_lines] + self.python_line_transforms
524 524
525 525 def reset(self):
526 526 """Reset the input buffer and associated state."""
527 527 super(IPythonInputSplitter, self).reset()
528 528 self._buffer_raw[:] = []
529 529 self.source_raw = ''
530 530 self.transformer_accumulating = False
531 531 self.within_python_line = False
532 532
533 533 for t in self.transforms:
534 534 try:
535 535 t.reset()
536 536 except SyntaxError:
537 537 # Nothing that calls reset() expects to handle transformer
538 538 # errors
539 539 pass
540 540
541 541 def flush_transformers(self):
542 542 def _flush(transform, outs):
543 543 """yield transformed lines
544 544
545 545 always strings, never None
546 546
547 547 transform: the current transform
548 548 outs: an iterable of previously transformed inputs.
549 549 Each may be multiline, which will be passed
550 550 one line at a time to transform.
551 551 """
552 552 for out in outs:
553 553 for line in out.splitlines():
554 554 # push one line at a time
555 555 tmp = transform.push(line)
556 556 if tmp is not None:
557 557 yield tmp
558 558
559 559 # reset the transform
560 560 tmp = transform.reset()
561 561 if tmp is not None:
562 562 yield tmp
563 563
564 564 out = []
565 565 for t in self.transforms_in_use:
566 566 out = _flush(t, out)
567 567
568 568 out = list(out)
569 569 if out:
570 570 self._store('\n'.join(out))
571 571
572 572 def raw_reset(self):
573 573 """Return raw input only and perform a full reset.
574 574 """
575 575 out = self.source_raw
576 576 self.reset()
577 577 return out
578 578
579 579 def source_reset(self):
580 580 try:
581 581 self.flush_transformers()
582 582 return self.source
583 583 finally:
584 584 self.reset()
585 585
586 586 def push_accepts_more(self):
587 587 if self.transformer_accumulating:
588 588 return True
589 589 else:
590 590 return super(IPythonInputSplitter, self).push_accepts_more()
591 591
592 592 def transform_cell(self, cell):
593 593 """Process and translate a cell of input.
594 594 """
595 595 self.reset()
596 596 try:
597 597 self.push(cell)
598 598 self.flush_transformers()
599 599 return self.source
600 600 finally:
601 601 self.reset()
602 602
603 603 def push(self, lines):
604 604 """Push one or more lines of IPython input.
605 605
606 606 This stores the given lines and returns a status code indicating
607 607 whether the code forms a complete Python block or not, after processing
608 608 all input lines for special IPython syntax.
609 609
610 610 Any exceptions generated in compilation are swallowed, but if an
611 611 exception was produced, the method returns True.
612 612
613 613 Parameters
614 614 ----------
615 615 lines : string
616 616 One or more lines of Python input.
617 617
618 618 Returns
619 619 -------
620 620 is_complete : boolean
621 621 True if the current input source (the result of the current input
622 622 plus prior inputs) forms a complete Python execution block. Note that
623 623 this value is also stored as a private attribute (_is_complete), so it
624 624 can be queried at any time.
625 625 """
626 626
627 627 # We must ensure all input is pure unicode
628 628 lines = cast_unicode(lines, self.encoding)
629 629
630 630 # ''.splitlines() --> [], but we need to push the empty line to transformers
631 631 lines_list = lines.splitlines()
632 632 if not lines_list:
633 633 lines_list = ['']
634 634
635 635 # Store raw source before applying any transformations to it. Note
636 636 # that this must be done *after* the reset() call that would otherwise
637 637 # flush the buffer.
638 638 self._store(lines, self._buffer_raw, 'source_raw')
639 639
640 640 for line in lines_list:
641 641 out = self.push_line(line)
642 642
643 643 return out
644 644
645 645 def push_line(self, line):
646 646 buf = self._buffer
647 647
648 648 def _accumulating(dbg):
649 649 #print(dbg)
650 650 self.transformer_accumulating = True
651 651 return False
652 652
653 653 for transformer in self.physical_line_transforms:
654 654 line = transformer.push(line)
655 655 if line is None:
656 656 return _accumulating(transformer)
657 657
658 658 if not self.within_python_line:
659 659 line = self.assemble_logical_lines.push(line)
660 660 if line is None:
661 661 return _accumulating('acc logical line')
662 662
663 663 for transformer in self.logical_line_transforms:
664 664 line = transformer.push(line)
665 665 if line is None:
666 666 return _accumulating(transformer)
667 667
668 668 line = self.assemble_python_lines.push(line)
669 669 if line is None:
670 670 self.within_python_line = True
671 671 return _accumulating('acc python line')
672 672 else:
673 673 self.within_python_line = False
674 674
675 675 for transformer in self.python_line_transforms:
676 676 line = transformer.push(line)
677 677 if line is None:
678 678 return _accumulating(transformer)
679 679
680 680 #print("transformers clear") #debug
681 681 self.transformer_accumulating = False
682 682 return super(IPythonInputSplitter, self).push(line)
@@ -1,605 +1,615 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the inputsplitter module."""
3 3
4 4 from __future__ import print_function
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import unittest
10 10 import sys
11 11
12 12 import nose.tools as nt
13 13
14 14 from IPython.core import inputsplitter as isp
15 15 from IPython.core.inputtransformer import InputTransformer
16 16 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
17 17 from IPython.testing import tools as tt
18 18 from IPython.utils import py3compat
19 19 from IPython.utils.py3compat import string_types, input
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Semi-complete examples (also used as tests)
23 23 #-----------------------------------------------------------------------------
24 24
25 25 # Note: at the bottom, there's a slightly more complete version of this that
26 26 # can be useful during development of code here.
27 27
28 28 def mini_interactive_loop(input_func):
29 29 """Minimal example of the logic of an interactive interpreter loop.
30 30
31 31 This serves as an example, and it is used by the test system with a fake
32 32 raw_input that simulates interactive input."""
33 33
34 34 from IPython.core.inputsplitter import InputSplitter
35 35
36 36 isp = InputSplitter()
37 37 # In practice, this input loop would be wrapped in an outside loop to read
38 38 # input indefinitely, until some exit/quit command was issued. Here we
39 39 # only illustrate the basic inner loop.
40 40 while isp.push_accepts_more():
41 41 indent = ' '*isp.indent_spaces
42 42 prompt = '>>> ' + indent
43 43 line = indent + input_func(prompt)
44 44 isp.push(line)
45 45
46 46 # Here we just return input so we can use it in a test suite, but a real
47 47 # interpreter would instead send it for execution somewhere.
48 48 src = isp.source_reset()
49 49 #print 'Input source was:\n', src # dbg
50 50 return src
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # Test utilities, just for local use
54 54 #-----------------------------------------------------------------------------
55 55
56 56 def assemble(block):
57 57 """Assemble a block into multi-line sub-blocks."""
58 58 return ['\n'.join(sub_block)+'\n' for sub_block in block]
59 59
60 60
61 61 def pseudo_input(lines):
62 62 """Return a function that acts like raw_input but feeds the input list."""
63 63 ilines = iter(lines)
64 64 def raw_in(prompt):
65 65 try:
66 66 return next(ilines)
67 67 except StopIteration:
68 68 return ''
69 69 return raw_in
70 70
71 71 #-----------------------------------------------------------------------------
72 72 # Tests
73 73 #-----------------------------------------------------------------------------
74 74 def test_spaces():
75 75 tests = [('', 0),
76 76 (' ', 1),
77 77 ('\n', 0),
78 78 (' \n', 1),
79 79 ('x', 0),
80 80 (' x', 1),
81 81 (' x',2),
82 82 (' x',4),
83 83 # Note: tabs are counted as a single whitespace!
84 84 ('\tx', 1),
85 85 ('\t x', 2),
86 86 ]
87 87 tt.check_pairs(isp.num_ini_spaces, tests)
88 88
89 89
90 90 def test_remove_comments():
91 91 tests = [('text', 'text'),
92 92 ('text # comment', 'text '),
93 93 ('text # comment\n', 'text \n'),
94 94 ('text # comment \n', 'text \n'),
95 95 ('line # c \nline\n','line \nline\n'),
96 96 ('line # c \nline#c2 \nline\nline #c\n\n',
97 97 'line \nline\nline\nline \n\n'),
98 98 ]
99 99 tt.check_pairs(isp.remove_comments, tests)
100 100
101 101
102 102 def test_get_input_encoding():
103 103 encoding = isp.get_input_encoding()
104 104 nt.assert_true(isinstance(encoding, string_types))
105 105 # simple-minded check that at least encoding a simple string works with the
106 106 # encoding we got.
107 107 nt.assert_equal(u'test'.encode(encoding), b'test')
108 108
109 109
110 110 class NoInputEncodingTestCase(unittest.TestCase):
111 111 def setUp(self):
112 112 self.old_stdin = sys.stdin
113 113 class X: pass
114 114 fake_stdin = X()
115 115 sys.stdin = fake_stdin
116 116
117 117 def test(self):
118 118 # Verify that if sys.stdin has no 'encoding' attribute we do the right
119 119 # thing
120 120 enc = isp.get_input_encoding()
121 121 self.assertEqual(enc, 'ascii')
122 122
123 123 def tearDown(self):
124 124 sys.stdin = self.old_stdin
125 125
126 126
127 127 class InputSplitterTestCase(unittest.TestCase):
128 128 def setUp(self):
129 129 self.isp = isp.InputSplitter()
130 130
131 131 def test_reset(self):
132 132 isp = self.isp
133 133 isp.push('x=1')
134 134 isp.reset()
135 135 self.assertEqual(isp._buffer, [])
136 136 self.assertEqual(isp.indent_spaces, 0)
137 137 self.assertEqual(isp.source, '')
138 138 self.assertEqual(isp.code, None)
139 139 self.assertEqual(isp._is_complete, False)
140 140
141 141 def test_source(self):
142 142 self.isp._store('1')
143 143 self.isp._store('2')
144 144 self.assertEqual(self.isp.source, '1\n2\n')
145 145 self.assertTrue(len(self.isp._buffer)>0)
146 146 self.assertEqual(self.isp.source_reset(), '1\n2\n')
147 147 self.assertEqual(self.isp._buffer, [])
148 148 self.assertEqual(self.isp.source, '')
149 149
150 150 def test_indent(self):
151 151 isp = self.isp # shorthand
152 152 isp.push('x=1')
153 153 self.assertEqual(isp.indent_spaces, 0)
154 154 isp.push('if 1:\n x=1')
155 155 self.assertEqual(isp.indent_spaces, 4)
156 156 isp.push('y=2\n')
157 157 self.assertEqual(isp.indent_spaces, 0)
158 158
159 159 def test_indent2(self):
160 160 isp = self.isp
161 161 isp.push('if 1:')
162 162 self.assertEqual(isp.indent_spaces, 4)
163 163 isp.push(' x=1')
164 164 self.assertEqual(isp.indent_spaces, 4)
165 165 # Blank lines shouldn't change the indent level
166 166 isp.push(' '*2)
167 167 self.assertEqual(isp.indent_spaces, 4)
168 168
169 169 def test_indent3(self):
170 170 isp = self.isp
171 171 # When a multiline statement contains parens or multiline strings, we
172 172 # shouldn't get confused.
173 173 isp.push("if 1:")
174 174 isp.push(" x = (1+\n 2)")
175 175 self.assertEqual(isp.indent_spaces, 4)
176 176
177 177 def test_indent4(self):
178 178 isp = self.isp
179 179 # whitespace after ':' should not screw up indent level
180 180 isp.push('if 1: \n x=1')
181 181 self.assertEqual(isp.indent_spaces, 4)
182 182 isp.push('y=2\n')
183 183 self.assertEqual(isp.indent_spaces, 0)
184 184 isp.push('if 1:\t\n x=1')
185 185 self.assertEqual(isp.indent_spaces, 4)
186 186 isp.push('y=2\n')
187 187 self.assertEqual(isp.indent_spaces, 0)
188 188
189 189 def test_dedent_pass(self):
190 190 isp = self.isp # shorthand
191 191 # should NOT cause dedent
192 192 isp.push('if 1:\n passes = 5')
193 193 self.assertEqual(isp.indent_spaces, 4)
194 194 isp.push('if 1:\n pass')
195 195 self.assertEqual(isp.indent_spaces, 0)
196 196 isp.push('if 1:\n pass ')
197 197 self.assertEqual(isp.indent_spaces, 0)
198 198
199 199 def test_dedent_break(self):
200 200 isp = self.isp # shorthand
201 201 # should NOT cause dedent
202 202 isp.push('while 1:\n breaks = 5')
203 203 self.assertEqual(isp.indent_spaces, 4)
204 204 isp.push('while 1:\n break')
205 205 self.assertEqual(isp.indent_spaces, 0)
206 206 isp.push('while 1:\n break ')
207 207 self.assertEqual(isp.indent_spaces, 0)
208 208
209 209 def test_dedent_continue(self):
210 210 isp = self.isp # shorthand
211 211 # should NOT cause dedent
212 212 isp.push('while 1:\n continues = 5')
213 213 self.assertEqual(isp.indent_spaces, 4)
214 214 isp.push('while 1:\n continue')
215 215 self.assertEqual(isp.indent_spaces, 0)
216 216 isp.push('while 1:\n continue ')
217 217 self.assertEqual(isp.indent_spaces, 0)
218 218
219 219 def test_dedent_raise(self):
220 220 isp = self.isp # shorthand
221 221 # should NOT cause dedent
222 222 isp.push('if 1:\n raised = 4')
223 223 self.assertEqual(isp.indent_spaces, 4)
224 224 isp.push('if 1:\n raise TypeError()')
225 225 self.assertEqual(isp.indent_spaces, 0)
226 226 isp.push('if 1:\n raise')
227 227 self.assertEqual(isp.indent_spaces, 0)
228 228 isp.push('if 1:\n raise ')
229 229 self.assertEqual(isp.indent_spaces, 0)
230 230
231 231 def test_dedent_return(self):
232 232 isp = self.isp # shorthand
233 233 # should NOT cause dedent
234 234 isp.push('if 1:\n returning = 4')
235 235 self.assertEqual(isp.indent_spaces, 4)
236 236 isp.push('if 1:\n return 5 + 493')
237 237 self.assertEqual(isp.indent_spaces, 0)
238 238 isp.push('if 1:\n return')
239 239 self.assertEqual(isp.indent_spaces, 0)
240 240 isp.push('if 1:\n return ')
241 241 self.assertEqual(isp.indent_spaces, 0)
242 242 isp.push('if 1:\n return(0)')
243 243 self.assertEqual(isp.indent_spaces, 0)
244 244
245 245 def test_push(self):
246 246 isp = self.isp
247 247 self.assertTrue(isp.push('x=1'))
248 248
249 249 def test_push2(self):
250 250 isp = self.isp
251 251 self.assertFalse(isp.push('if 1:'))
252 252 for line in [' x=1', '# a comment', ' y=2']:
253 253 print(line)
254 254 self.assertTrue(isp.push(line))
255 255
256 256 def test_push3(self):
257 257 isp = self.isp
258 258 isp.push('if True:')
259 259 isp.push(' a = 1')
260 260 self.assertFalse(isp.push('b = [1,'))
261 261
262 262 def test_push_accepts_more(self):
263 263 isp = self.isp
264 264 isp.push('x=1')
265 265 self.assertFalse(isp.push_accepts_more())
266 266
267 267 def test_push_accepts_more2(self):
268 268 isp = self.isp
269 269 isp.push('if 1:')
270 270 self.assertTrue(isp.push_accepts_more())
271 271 isp.push(' x=1')
272 272 self.assertTrue(isp.push_accepts_more())
273 273 isp.push('')
274 274 self.assertFalse(isp.push_accepts_more())
275 275
276 276 def test_push_accepts_more3(self):
277 277 isp = self.isp
278 278 isp.push("x = (2+\n3)")
279 279 self.assertFalse(isp.push_accepts_more())
280 280
281 281 def test_push_accepts_more4(self):
282 282 isp = self.isp
283 283 # When a multiline statement contains parens or multiline strings, we
284 284 # shouldn't get confused.
285 285 # FIXME: we should be able to better handle de-dents in statements like
286 286 # multiline strings and multiline expressions (continued with \ or
287 287 # parens). Right now we aren't handling the indentation tracking quite
288 288 # correctly with this, though in practice it may not be too much of a
289 289 # problem. We'll need to see.
290 290 isp.push("if 1:")
291 291 isp.push(" x = (2+")
292 292 isp.push(" 3)")
293 293 self.assertTrue(isp.push_accepts_more())
294 294 isp.push(" y = 3")
295 295 self.assertTrue(isp.push_accepts_more())
296 296 isp.push('')
297 297 self.assertFalse(isp.push_accepts_more())
298 298
299 299 def test_push_accepts_more5(self):
300 300 isp = self.isp
301 301 isp.push('try:')
302 302 isp.push(' a = 5')
303 303 isp.push('except:')
304 304 isp.push(' raise')
305 305 # We want to be able to add an else: block at this point, so it should
306 306 # wait for a blank line.
307 307 self.assertTrue(isp.push_accepts_more())
308 308
309 309 def test_continuation(self):
310 310 isp = self.isp
311 311 isp.push("import os, \\")
312 312 self.assertTrue(isp.push_accepts_more())
313 313 isp.push("sys")
314 314 self.assertFalse(isp.push_accepts_more())
315 315
316 316 def test_syntax_error(self):
317 317 isp = self.isp
318 318 # Syntax errors immediately produce a 'ready' block, so the invalid
319 319 # Python can be sent to the kernel for evaluation with possible ipython
320 320 # special-syntax conversion.
321 321 isp.push('run foo')
322 322 self.assertFalse(isp.push_accepts_more())
323 323
324 324 def test_unicode(self):
325 325 self.isp.push(u"Pérez")
326 326 self.isp.push(u'\xc3\xa9')
327 327 self.isp.push(u"u'\xc3\xa9'")
328 328
329 329 def test_line_continuation(self):
330 330 """ Test issue #2108."""
331 331 isp = self.isp
332 332 # A blank line after a line continuation should not accept more
333 333 isp.push("1 \\\n\n")
334 334 self.assertFalse(isp.push_accepts_more())
335 335 # Whitespace after a \ is a SyntaxError. The only way to test that
336 336 # here is to test that push doesn't accept more (as with
337 337 # test_syntax_error() above).
338 338 isp.push(r"1 \ ")
339 339 self.assertFalse(isp.push_accepts_more())
340 340 # Even if the line is continuable (c.f. the regular Python
341 341 # interpreter)
342 342 isp.push(r"(1 \ ")
343 343 self.assertFalse(isp.push_accepts_more())
344 344
345 345 def test_check_complete(self):
346 346 isp = self.isp
347 347 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
348 348 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
349 349 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
350 350 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
351 351 self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None))
352 352
353 353 class InteractiveLoopTestCase(unittest.TestCase):
354 354 """Tests for an interactive loop like a python shell.
355 355 """
356 356 def check_ns(self, lines, ns):
357 357 """Validate that the given input lines produce the resulting namespace.
358 358
359 359 Note: the input lines are given exactly as they would be typed in an
360 360 auto-indenting environment, as mini_interactive_loop above already does
361 361 auto-indenting and prepends spaces to the input.
362 362 """
363 363 src = mini_interactive_loop(pseudo_input(lines))
364 364 test_ns = {}
365 365 exec(src, test_ns)
366 366 # We can't check that the provided ns is identical to the test_ns,
367 367 # because Python fills test_ns with extra keys (copyright, etc). But
368 368 # we can check that the given dict is *contained* in test_ns
369 369 for k,v in ns.items():
370 370 self.assertEqual(test_ns[k], v)
371 371
372 372 def test_simple(self):
373 373 self.check_ns(['x=1'], dict(x=1))
374 374
375 375 def test_simple2(self):
376 376 self.check_ns(['if 1:', 'x=2'], dict(x=2))
377 377
378 378 def test_xy(self):
379 379 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
380 380
381 381 def test_abc(self):
382 382 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
383 383
384 384 def test_multi(self):
385 385 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
386 386
387 387
388 388 class IPythonInputTestCase(InputSplitterTestCase):
389 389 """By just creating a new class whose .isp is a different instance, we
390 390 re-run the same test battery on the new input splitter.
391 391
392 392 In addition, this runs the tests over the syntax and syntax_ml dicts that
393 393 were tested by individual functions, as part of the OO interface.
394 394
395 395 It also makes some checks on the raw buffer storage.
396 396 """
397 397
398 398 def setUp(self):
399 399 self.isp = isp.IPythonInputSplitter()
400 400
401 401 def test_syntax(self):
402 402 """Call all single-line syntax tests from the main object"""
403 403 isp = self.isp
404 404 for example in syntax.values():
405 405 for raw, out_t in example:
406 406 if raw.startswith(' '):
407 407 continue
408 408
409 409 isp.push(raw+'\n')
410 410 out_raw = isp.source_raw
411 411 out = isp.source_reset()
412 412 self.assertEqual(out.rstrip(), out_t,
413 413 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
414 414 self.assertEqual(out_raw.rstrip(), raw.rstrip())
415 415
416 416 def test_syntax_multiline(self):
417 417 isp = self.isp
418 418 for example in syntax_ml.values():
419 419 for line_pairs in example:
420 420 out_t_parts = []
421 421 raw_parts = []
422 422 for lraw, out_t_part in line_pairs:
423 423 if out_t_part is not None:
424 424 out_t_parts.append(out_t_part)
425 425
426 426 if lraw is not None:
427 427 isp.push(lraw)
428 428 raw_parts.append(lraw)
429 429
430 430 out_raw = isp.source_raw
431 431 out = isp.source_reset()
432 432 out_t = '\n'.join(out_t_parts).rstrip()
433 433 raw = '\n'.join(raw_parts).rstrip()
434 434 self.assertEqual(out.rstrip(), out_t)
435 435 self.assertEqual(out_raw.rstrip(), raw)
436 436
437 437 def test_syntax_multiline_cell(self):
438 438 isp = self.isp
439 439 for example in syntax_ml.values():
440 440
441 441 out_t_parts = []
442 442 for line_pairs in example:
443 443 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
444 444 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
445 445 out = isp.transform_cell(raw)
446 446 # Match ignoring trailing whitespace
447 447 self.assertEqual(out.rstrip(), out_t.rstrip())
448 448
449 449 def test_cellmagic_preempt(self):
450 450 isp = self.isp
451 451 for raw, name, line, cell in [
452 452 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
453 453 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
454 454 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
455 455 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
456 456 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
457 457 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
458 458 ]:
459 459 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
460 460 name, line, cell
461 461 )
462 462 out = isp.transform_cell(raw)
463 463 self.assertEqual(out.rstrip(), expected.rstrip())
464 464
465 465 def test_multiline_passthrough(self):
466 466 isp = self.isp
467 467 class CommentTransformer(InputTransformer):
468 468 def __init__(self):
469 469 self._lines = []
470 470
471 471 def push(self, line):
472 472 self._lines.append(line + '#')
473 473
474 474 def reset(self):
475 475 text = '\n'.join(self._lines)
476 476 self._lines = []
477 477 return text
478 478
479 479 isp.physical_line_transforms.insert(0, CommentTransformer())
480 480
481 481 for raw, expected in [
482 482 ("a=5", "a=5#"),
483 483 ("%ls foo", "get_ipython().magic(%r)" % u'ls foo#'),
484 484 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().magic(%r)" % (
485 485 u'ls foo#', u'ls bar#'
486 486 )),
487 487 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().magic(%r)\n4#\n5#" % u'ls foo#'),
488 488 ]:
489 489 out = isp.transform_cell(raw)
490 490 self.assertEqual(out.rstrip(), expected.rstrip())
491 491
492 492 #-----------------------------------------------------------------------------
493 493 # Main - use as a script, mostly for developer experiments
494 494 #-----------------------------------------------------------------------------
495 495
496 496 if __name__ == '__main__':
497 497 # A simple demo for interactive experimentation. This code will not get
498 498 # picked up by any test suite.
499 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
499 from IPython.core.inputsplitter import IPythonInputSplitter
500 500
501 501 # configure here the syntax to use, prompt and whether to autoindent
502 502 #isp, start_prompt = InputSplitter(), '>>> '
503 503 isp, start_prompt = IPythonInputSplitter(), 'In> '
504 504
505 505 autoindent = True
506 506 #autoindent = False
507 507
508 508 try:
509 509 while True:
510 510 prompt = start_prompt
511 511 while isp.push_accepts_more():
512 512 indent = ' '*isp.indent_spaces
513 513 if autoindent:
514 514 line = indent + input(prompt+indent)
515 515 else:
516 516 line = input(prompt)
517 517 isp.push(line)
518 518 prompt = '... '
519 519
520 520 # Here we just return input so we can use it in a test suite, but a
521 521 # real interpreter would instead send it for execution somewhere.
522 522 #src = isp.source; raise EOFError # dbg
523 523 raw = isp.source_raw
524 524 src = isp.source_reset()
525 525 print('Input source was:\n', src)
526 526 print('Raw source was:\n', raw)
527 527 except EOFError:
528 528 print('Bye')
529 529
530 530 # Tests for cell magics support
531 531
532 532 def test_last_blank():
533 533 nt.assert_false(isp.last_blank(''))
534 534 nt.assert_false(isp.last_blank('abc'))
535 535 nt.assert_false(isp.last_blank('abc\n'))
536 536 nt.assert_false(isp.last_blank('abc\na'))
537 537
538 538 nt.assert_true(isp.last_blank('\n'))
539 539 nt.assert_true(isp.last_blank('\n '))
540 540 nt.assert_true(isp.last_blank('abc\n '))
541 541 nt.assert_true(isp.last_blank('abc\n\n'))
542 542 nt.assert_true(isp.last_blank('abc\nd\n\n'))
543 543 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
544 544 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
545 545
546 546
547 547 def test_last_two_blanks():
548 548 nt.assert_false(isp.last_two_blanks(''))
549 549 nt.assert_false(isp.last_two_blanks('abc'))
550 550 nt.assert_false(isp.last_two_blanks('abc\n'))
551 551 nt.assert_false(isp.last_two_blanks('abc\n\na'))
552 552 nt.assert_false(isp.last_two_blanks('abc\n \n'))
553 553 nt.assert_false(isp.last_two_blanks('abc\n\n'))
554 554
555 555 nt.assert_true(isp.last_two_blanks('\n\n'))
556 556 nt.assert_true(isp.last_two_blanks('\n\n '))
557 557 nt.assert_true(isp.last_two_blanks('\n \n'))
558 558 nt.assert_true(isp.last_two_blanks('abc\n\n '))
559 559 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
560 560 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
561 561 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
562 562 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
563 563 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
564 564 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
565 565
566 566
567 567 class CellMagicsCommon(object):
568 568
569 569 def test_whole_cell(self):
570 570 src = "%%cellm line\nbody\n"
571 571 out = self.sp.transform_cell(src)
572 572 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
573 573 nt.assert_equal(out, py3compat.u_format(ref))
574 574
575 575 def test_cellmagic_help(self):
576 576 self.sp.push('%%cellm?')
577 577 nt.assert_false(self.sp.push_accepts_more())
578 578
579 579 def tearDown(self):
580 580 self.sp.reset()
581 581
582 582
583 583 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
584 584 sp = isp.IPythonInputSplitter(line_input_checker=False)
585 585
586 586 def test_incremental(self):
587 587 sp = self.sp
588 588 sp.push('%%cellm firstline\n')
589 589 nt.assert_true(sp.push_accepts_more()) #1
590 590 sp.push('line2\n')
591 591 nt.assert_true(sp.push_accepts_more()) #2
592 592 sp.push('\n')
593 593 # This should accept a blank line and carry on until the cell is reset
594 594 nt.assert_true(sp.push_accepts_more()) #3
595
596 def test_no_strip_coding(self):
597 src = '\n'.join([
598 '%%writefile foo.py',
599 '# coding: utf-8',
600 'print(u"üñîçø∂é")',
601 ])
602 out = self.sp.transform_cell(src)
603 nt.assert_in('# coding: utf-8', out)
604
595 605
596 606 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
597 607 sp = isp.IPythonInputSplitter(line_input_checker=True)
598 608
599 609 def test_incremental(self):
600 610 sp = self.sp
601 611 sp.push('%%cellm line2\n')
602 612 nt.assert_true(sp.push_accepts_more()) #1
603 613 sp.push('\n')
604 614 # In this case, a blank line should end the cell magic
605 615 nt.assert_false(sp.push_accepts_more()) #2
General Comments 0
You need to be logged in to leave comments. Login now