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