##// END OF EJS Templates
Calculate indentation based on tokens, not regexes...
Thomas Kluyver -
Show More
@@ -1,679 +1,787 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 import io
21 22 import re
22 23 import sys
24 import tokenize
23 25 import warnings
24 26
25 27 from IPython.utils.py3compat import cast_unicode
26 28 from IPython.core.inputtransformer import (leading_indent,
27 29 classic_prompt,
28 30 ipy_prompt,
29 31 cellmagic,
30 32 assemble_logical_lines,
31 33 help_end,
32 34 escaped_commands,
33 35 assign_from_magic,
34 36 assign_from_system,
35 37 assemble_python_lines,
36 38 )
37 39
38 40 # These are available in this module for backwards compatibility.
39 41 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
40 42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
41 43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
42 44
43 45 #-----------------------------------------------------------------------------
44 46 # Utilities
45 47 #-----------------------------------------------------------------------------
46 48
47 49 # FIXME: These are general-purpose utilities that later can be moved to the
48 50 # general ward. Kept here for now because we're being very strict about test
49 51 # coverage with this code, and this lets us ensure that we keep 100% coverage
50 52 # while developing.
51 53
52 54 # compiled regexps for autoindent management
53 55 dedent_re = re.compile('|'.join([
54 56 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
55 57 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
56 58 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
57 59 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
58 60 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
59 61 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
60 62 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
61 63 ]))
62 64 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
63 65
64 66 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
65 67 # before pure comments
66 68 comment_line_re = re.compile('^\s*\#')
67 69
68 70
69 71 def num_ini_spaces(s):
70 72 """Return the number of initial spaces in a string.
71 73
72 74 Note that tabs are counted as a single space. For now, we do *not* support
73 75 mixing of tabs and spaces in the user's input.
74 76
75 77 Parameters
76 78 ----------
77 79 s : string
78 80
79 81 Returns
80 82 -------
81 83 n : int
82 84 """
83 85
84 86 ini_spaces = ini_spaces_re.match(s)
85 87 if ini_spaces:
86 88 return ini_spaces.end()
87 89 else:
88 90 return 0
89 91
92 # Fake token types for partial_tokenize:
93 INCOMPLETE_STRING = tokenize.N_TOKENS
94 IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1
95
96 # The 2 classes below have the same API as TokenInfo, but don't try to look up
97 # a token type name that they won't find.
98 class IncompleteString:
99 type = exact_type = INCOMPLETE_STRING
100 def __init__(self, s, start, end, line):
101 self.s = s
102 self.start = start
103 self.end = end
104 self.line = line
105
106 class InMultilineStatement:
107 type = exact_type = IN_MULTILINE_STATEMENT
108 def __init__(self, pos, line):
109 self.s = ''
110 self.start = self.end = pos
111 self.line = line
112
113 def partial_tokens(s):
114 """Iterate over tokens from a possibly-incomplete string of code.
115
116 This adds two special token types: INCOMPLETE_STRING and
117 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and
118 represent the two main ways for code to be incomplete.
119 """
120 readline = io.StringIO(s).readline
121 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
122 try:
123 for token in tokenize.generate_tokens(readline):
124 yield token
125 except tokenize.TokenError as e:
126 # catch EOF error
127 lines = s.splitlines(keepends=True)
128 end = len(lines), len(lines[-1])
129 if 'multi-line string' in e.args[0]:
130 l, c = start = token.end
131 s = lines[l-1][c:] + ''.join(lines[l:])
132 yield IncompleteString(s, start, end, lines[-1])
133 elif 'multi-line statement' in e.args[0]:
134 yield InMultilineStatement(end, lines[-1])
135 else:
136 raise
137
138 def find_next_indent(code):
139 """Find the number of spaces for the next line of indentation"""
140 tokens = list(partial_tokens(code))
141 if tokens[-1].type == tokenize.ENDMARKER:
142 tokens.pop()
143 if not tokens:
144 return 0
145 if tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}:
146 tokens.pop()
147
148 if tokens[-1].type == INCOMPLETE_STRING:
149 # Inside a multiline string
150 return 0
151
152 # Find the indents used before
153 prev_indents = [0]
154 def _add_indent(n):
155 if n != prev_indents[-1]:
156 prev_indents.append(n)
157
158 tokiter = iter(tokens)
159 for tok in tokiter:
160 if tok.type in {tokenize.INDENT, tokenize.DEDENT}:
161 _add_indent(tok.end[1])
162 elif (tok.type == tokenize.NL):
163 try:
164 _add_indent(next(tokiter).start[1])
165 except StopIteration:
166 break
167
168 last_indent = prev_indents.pop()
169
170 if tokens[-1].type == IN_MULTILINE_STATEMENT:
171 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}:
172 return last_indent + 4
173 return last_indent
174
175 if tokens[-1].exact_type == tokenize.COLON:
176 # Line ends with colon - indent
177 return last_indent + 4
178
179 if last_indent:
180 # Examine the last line for dedent cues - statements like return or
181 # raise which normally end a block of code.
182 last_line_starts = 0
183 for i, tok in enumerate(tokens):
184 if tok.type == tokenize.NEWLINE:
185 last_line_starts = i + 1
186
187 last_line_tokens = tokens[last_line_starts:]
188 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME]
189 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}:
190 # Find the most recent indentation less than the current level
191 for indent in reversed(prev_indents):
192 if indent < last_indent:
193 return indent
194
195 return last_indent
196
197
90 198 def last_blank(src):
91 199 """Determine if the input source ends in a blank.
92 200
93 201 A blank is either a newline or a line consisting of whitespace.
94 202
95 203 Parameters
96 204 ----------
97 205 src : string
98 206 A single or multiline string.
99 207 """
100 208 if not src: return False
101 209 ll = src.splitlines()[-1]
102 210 return (ll == '') or ll.isspace()
103 211
104 212
105 213 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
106 214 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
107 215
108 216 def last_two_blanks(src):
109 217 """Determine if the input source ends in two blanks.
110 218
111 219 A blank is either a newline or a line consisting of whitespace.
112 220
113 221 Parameters
114 222 ----------
115 223 src : string
116 224 A single or multiline string.
117 225 """
118 226 if not src: return False
119 227 # The logic here is tricky: I couldn't get a regexp to work and pass all
120 228 # the tests, so I took a different approach: split the source by lines,
121 229 # grab the last two and prepend '###\n' as a stand-in for whatever was in
122 230 # the body before the last two lines. Then, with that structure, it's
123 231 # possible to analyze with two regexps. Not the most elegant solution, but
124 232 # it works. If anyone tries to change this logic, make sure to validate
125 233 # the whole test suite first!
126 234 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
127 235 return (bool(last_two_blanks_re.match(new_src)) or
128 236 bool(last_two_blanks_re2.match(new_src)) )
129 237
130 238
131 239 def remove_comments(src):
132 240 """Remove all comments from input source.
133 241
134 242 Note: comments are NOT recognized inside of strings!
135 243
136 244 Parameters
137 245 ----------
138 246 src : string
139 247 A single or multiline input string.
140 248
141 249 Returns
142 250 -------
143 251 String with all Python comments removed.
144 252 """
145 253
146 254 return re.sub('#.*', '', src)
147 255
148 256
149 257 def get_input_encoding():
150 258 """Return the default standard input encoding.
151 259
152 260 If sys.stdin has no encoding, 'ascii' is returned."""
153 261 # There are strange environments for which sys.stdin.encoding is None. We
154 262 # ensure that a valid encoding is returned.
155 263 encoding = getattr(sys.stdin, 'encoding', None)
156 264 if encoding is None:
157 265 encoding = 'ascii'
158 266 return encoding
159 267
160 268 #-----------------------------------------------------------------------------
161 269 # Classes and functions for normal Python syntax handling
162 270 #-----------------------------------------------------------------------------
163 271
164 272 class InputSplitter(object):
165 273 r"""An object that can accumulate lines of Python source before execution.
166 274
167 275 This object is designed to be fed python source line-by-line, using
168 276 :meth:`push`. It will return on each push whether the currently pushed
169 277 code could be executed already. In addition, it provides a method called
170 278 :meth:`push_accepts_more` that can be used to query whether more input
171 279 can be pushed into a single interactive block.
172 280
173 281 This is a simple example of how an interactive terminal-based client can use
174 282 this tool::
175 283
176 284 isp = InputSplitter()
177 285 while isp.push_accepts_more():
178 286 indent = ' '*isp.indent_spaces
179 287 prompt = '>>> ' + indent
180 288 line = indent + raw_input(prompt)
181 289 isp.push(line)
182 290 print 'Input source was:\n', isp.source_reset(),
183 291 """
184 292 # Number of spaces of indentation computed from input that has been pushed
185 293 # so far. This is the attributes callers should query to get the current
186 294 # indentation level, in order to provide auto-indent facilities.
187 295 indent_spaces = 0
188 296 # String, indicating the default input encoding. It is computed by default
189 297 # at initialization time via get_input_encoding(), but it can be reset by a
190 298 # client with specific knowledge of the encoding.
191 299 encoding = ''
192 300 # String where the current full source input is stored, properly encoded.
193 301 # Reading this attribute is the normal way of querying the currently pushed
194 302 # source code, that has been properly encoded.
195 303 source = ''
196 304 # Code object corresponding to the current source. It is automatically
197 305 # synced to the source, so it can be queried at any time to obtain the code
198 306 # object; it will be None if the source doesn't compile to valid Python.
199 307 code = None
200 308
201 309 # Private attributes
202 310
203 311 # List with lines of input accumulated so far
204 312 _buffer = None
205 313 # Command compiler
206 314 _compile = None
207 315 # Mark when input has changed indentation all the way back to flush-left
208 316 _full_dedent = False
209 317 # Boolean indicating whether the current block is complete
210 318 _is_complete = None
211 319 # Boolean indicating whether the current block has an unrecoverable syntax error
212 320 _is_invalid = False
213 321
214 322 def __init__(self):
215 323 """Create a new InputSplitter instance.
216 324 """
217 325 self._buffer = []
218 326 self._compile = codeop.CommandCompiler()
219 327 self.encoding = get_input_encoding()
220 328
221 329 def reset(self):
222 330 """Reset the input buffer and associated state."""
223 331 self.indent_spaces = 0
224 332 self._buffer[:] = []
225 333 self.source = ''
226 334 self.code = None
227 335 self._is_complete = False
228 336 self._is_invalid = False
229 337 self._full_dedent = False
230 338
231 339 def source_reset(self):
232 340 """Return the input source and perform a full reset.
233 341 """
234 342 out = self.source
235 343 self.reset()
236 344 return out
237 345
238 346 def check_complete(self, source):
239 347 """Return whether a block of code is ready to execute, or should be continued
240 348
241 349 This is a non-stateful API, and will reset the state of this InputSplitter.
242 350
243 351 Parameters
244 352 ----------
245 353 source : string
246 354 Python input code, which can be multiline.
247 355
248 356 Returns
249 357 -------
250 358 status : str
251 359 One of 'complete', 'incomplete', or 'invalid' if source is not a
252 360 prefix of valid code.
253 361 indent_spaces : int or None
254 362 The number of spaces by which to indent the next line of code. If
255 363 status is not 'incomplete', this is None.
256 364 """
257 365 self.reset()
258 366 try:
259 367 self.push(source)
260 368 except SyntaxError:
261 369 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 370 # which push() will not catch.
263 371 return 'invalid', None
264 372 else:
265 373 if self._is_invalid:
266 374 return 'invalid', None
267 375 elif self.push_accepts_more():
268 376 return 'incomplete', self.indent_spaces
269 377 else:
270 378 return 'complete', None
271 379 finally:
272 380 self.reset()
273 381
274 382 def push(self, lines):
275 383 """Push one or more lines of input.
276 384
277 385 This stores the given lines and returns a status code indicating
278 386 whether the code forms a complete Python block or not.
279 387
280 388 Any exceptions generated in compilation are swallowed, but if an
281 389 exception was produced, the method returns True.
282 390
283 391 Parameters
284 392 ----------
285 393 lines : string
286 394 One or more lines of Python input.
287 395
288 396 Returns
289 397 -------
290 398 is_complete : boolean
291 399 True if the current input source (the result of the current input
292 400 plus prior inputs) forms a complete Python execution block. Note that
293 401 this value is also stored as a private attribute (``_is_complete``), so it
294 402 can be queried at any time.
295 403 """
296 404 self._store(lines)
297 405 source = self.source
298 406
299 407 # Before calling _compile(), reset the code object to None so that if an
300 408 # exception is raised in compilation, we don't mislead by having
301 409 # inconsistent code/source attributes.
302 410 self.code, self._is_complete = None, None
303 411 self._is_invalid = False
304 412
305 413 # Honor termination lines properly
306 414 if source.endswith('\\\n'):
307 415 return False
308 416
309 self._update_indent(lines)
417 self._update_indent()
310 418 try:
311 419 with warnings.catch_warnings():
312 420 warnings.simplefilter('error', SyntaxWarning)
313 421 self.code = self._compile(source, symbol="exec")
314 422 # Invalid syntax can produce any of a number of different errors from
315 423 # inside the compiler, so we have to catch them all. Syntax errors
316 424 # immediately produce a 'ready' block, so the invalid Python can be
317 425 # sent to the kernel for evaluation with possible ipython
318 426 # special-syntax conversion.
319 427 except (SyntaxError, OverflowError, ValueError, TypeError,
320 428 MemoryError, SyntaxWarning):
321 429 self._is_complete = True
322 430 self._is_invalid = True
323 431 else:
324 432 # Compilation didn't produce any exceptions (though it may not have
325 433 # given a complete code object)
326 434 self._is_complete = self.code is not None
327 435
328 436 return self._is_complete
329 437
330 438 def push_accepts_more(self):
331 439 """Return whether a block of interactive input can accept more input.
332 440
333 441 This method is meant to be used by line-oriented frontends, who need to
334 442 guess whether a block is complete or not based solely on prior and
335 443 current input lines. The InputSplitter considers it has a complete
336 444 interactive block and will not accept more input when either:
337 445
338 446 * A SyntaxError is raised
339 447
340 448 * The code is complete and consists of a single line or a single
341 449 non-compound statement
342 450
343 451 * The code is complete and has a blank line at the end
344 452
345 453 If the current input produces a syntax error, this method immediately
346 454 returns False but does *not* raise the syntax error exception, as
347 455 typically clients will want to send invalid syntax to an execution
348 456 backend which might convert the invalid syntax into valid Python via
349 457 one of the dynamic IPython mechanisms.
350 458 """
351 459
352 460 # With incomplete input, unconditionally accept more
353 461 # A syntax error also sets _is_complete to True - see push()
354 462 if not self._is_complete:
355 463 #print("Not complete") # debug
356 464 return True
357 465
358 466 # The user can make any (complete) input execute by leaving a blank line
359 467 last_line = self.source.splitlines()[-1]
360 468 if (not last_line) or last_line.isspace():
361 469 #print("Blank line") # debug
362 470 return False
363 471
364 472 # If there's just a single line or AST node, and we're flush left, as is
365 473 # the case after a simple statement such as 'a=1', we want to execute it
366 474 # straight away.
367 475 if self.indent_spaces==0:
368 476 if len(self.source.splitlines()) <= 1:
369 477 return False
370 478
371 479 try:
372 480 code_ast = ast.parse(u''.join(self._buffer))
373 481 except Exception:
374 482 #print("Can't parse AST") # debug
375 483 return False
376 484 else:
377 485 if len(code_ast.body) == 1 and \
378 486 not hasattr(code_ast.body[0], 'body'):
379 487 #print("Simple statement") # debug
380 488 return False
381 489
382 490 # General fallback - accept more code
383 491 return True
384 492
385 493 #------------------------------------------------------------------------
386 494 # Private interface
387 495 #------------------------------------------------------------------------
388 496
389 497 def _find_indent(self, line):
390 498 """Compute the new indentation level for a single line.
391 499
392 500 Parameters
393 501 ----------
394 502 line : str
395 503 A single new line of non-whitespace, non-comment Python input.
396 504
397 505 Returns
398 506 -------
399 507 indent_spaces : int
400 508 New value for the indent level (it may be equal to self.indent_spaces
401 509 if indentation doesn't change.
402 510
403 511 full_dedent : boolean
404 512 Whether the new line causes a full flush-left dedent.
405 513 """
406 514 indent_spaces = self.indent_spaces
407 515 full_dedent = self._full_dedent
408 516
409 517 inisp = num_ini_spaces(line)
410 518 if inisp < indent_spaces:
411 519 indent_spaces = inisp
412 520 if indent_spaces <= 0:
413 521 #print 'Full dedent in text',self.source # dbg
414 522 full_dedent = True
415 523
416 524 if line.rstrip()[-1] == ':':
417 525 indent_spaces += 4
418 526 elif dedent_re.match(line):
419 527 indent_spaces -= 4
420 528 if indent_spaces <= 0:
421 529 full_dedent = True
422 530
423 531 # Safety
424 532 if indent_spaces < 0:
425 533 indent_spaces = 0
426 534 #print 'safety' # dbg
427 535
428 536 return indent_spaces, full_dedent
429 537
430 def _update_indent(self, lines):
431 for line in remove_comments(lines).splitlines():
432 if line and not line.isspace():
433 self.indent_spaces, self._full_dedent = self._find_indent(line)
538 def _update_indent(self):
539 # self.source always has a trailing newline
540 self.indent_spaces = find_next_indent(self.source[:-1])
541 self._full_dedent = (self.indent_spaces == 0)
434 542
435 543 def _store(self, lines, buffer=None, store='source'):
436 544 """Store one or more lines of input.
437 545
438 546 If input lines are not newline-terminated, a newline is automatically
439 547 appended."""
440 548
441 549 if buffer is None:
442 550 buffer = self._buffer
443 551
444 552 if lines.endswith('\n'):
445 553 buffer.append(lines)
446 554 else:
447 555 buffer.append(lines+'\n')
448 556 setattr(self, store, self._set_source(buffer))
449 557
450 558 def _set_source(self, buffer):
451 559 return u''.join(buffer)
452 560
453 561
454 562 class IPythonInputSplitter(InputSplitter):
455 563 """An input splitter that recognizes all of IPython's special syntax."""
456 564
457 565 # String with raw, untransformed input.
458 566 source_raw = ''
459 567
460 568 # Flag to track when a transformer has stored input that it hasn't given
461 569 # back yet.
462 570 transformer_accumulating = False
463 571
464 572 # Flag to track when assemble_python_lines has stored input that it hasn't
465 573 # given back yet.
466 574 within_python_line = False
467 575
468 576 # Private attributes
469 577
470 578 # List with lines of raw input accumulated so far.
471 579 _buffer_raw = None
472 580
473 581 def __init__(self, line_input_checker=True, physical_line_transforms=None,
474 582 logical_line_transforms=None, python_line_transforms=None):
475 583 super(IPythonInputSplitter, self).__init__()
476 584 self._buffer_raw = []
477 585 self._validate = True
478 586
479 587 if physical_line_transforms is not None:
480 588 self.physical_line_transforms = physical_line_transforms
481 589 else:
482 590 self.physical_line_transforms = [
483 591 leading_indent(),
484 592 classic_prompt(),
485 593 ipy_prompt(),
486 594 cellmagic(end_on_blank_line=line_input_checker),
487 595 ]
488 596
489 597 self.assemble_logical_lines = assemble_logical_lines()
490 598 if logical_line_transforms is not None:
491 599 self.logical_line_transforms = logical_line_transforms
492 600 else:
493 601 self.logical_line_transforms = [
494 602 help_end(),
495 603 escaped_commands(),
496 604 assign_from_magic(),
497 605 assign_from_system(),
498 606 ]
499 607
500 608 self.assemble_python_lines = assemble_python_lines()
501 609 if python_line_transforms is not None:
502 610 self.python_line_transforms = python_line_transforms
503 611 else:
504 612 # We don't use any of these at present
505 613 self.python_line_transforms = []
506 614
507 615 @property
508 616 def transforms(self):
509 617 "Quick access to all transformers."
510 618 return self.physical_line_transforms + \
511 619 [self.assemble_logical_lines] + self.logical_line_transforms + \
512 620 [self.assemble_python_lines] + self.python_line_transforms
513 621
514 622 @property
515 623 def transforms_in_use(self):
516 624 """Transformers, excluding logical line transformers if we're in a
517 625 Python line."""
518 626 t = self.physical_line_transforms[:]
519 627 if not self.within_python_line:
520 628 t += [self.assemble_logical_lines] + self.logical_line_transforms
521 629 return t + [self.assemble_python_lines] + self.python_line_transforms
522 630
523 631 def reset(self):
524 632 """Reset the input buffer and associated state."""
525 633 super(IPythonInputSplitter, self).reset()
526 634 self._buffer_raw[:] = []
527 635 self.source_raw = ''
528 636 self.transformer_accumulating = False
529 637 self.within_python_line = False
530 638
531 639 for t in self.transforms:
532 640 try:
533 641 t.reset()
534 642 except SyntaxError:
535 643 # Nothing that calls reset() expects to handle transformer
536 644 # errors
537 645 pass
538 646
539 647 def flush_transformers(self):
540 648 def _flush(transform, outs):
541 649 """yield transformed lines
542 650
543 651 always strings, never None
544 652
545 653 transform: the current transform
546 654 outs: an iterable of previously transformed inputs.
547 655 Each may be multiline, which will be passed
548 656 one line at a time to transform.
549 657 """
550 658 for out in outs:
551 659 for line in out.splitlines():
552 660 # push one line at a time
553 661 tmp = transform.push(line)
554 662 if tmp is not None:
555 663 yield tmp
556 664
557 665 # reset the transform
558 666 tmp = transform.reset()
559 667 if tmp is not None:
560 668 yield tmp
561 669
562 670 out = []
563 671 for t in self.transforms_in_use:
564 672 out = _flush(t, out)
565 673
566 674 out = list(out)
567 675 if out:
568 676 self._store('\n'.join(out))
569 677
570 678 def raw_reset(self):
571 679 """Return raw input only and perform a full reset.
572 680 """
573 681 out = self.source_raw
574 682 self.reset()
575 683 return out
576 684
577 685 def source_reset(self):
578 686 try:
579 687 self.flush_transformers()
580 688 return self.source
581 689 finally:
582 690 self.reset()
583 691
584 692 def push_accepts_more(self):
585 693 if self.transformer_accumulating:
586 694 return True
587 695 else:
588 696 return super(IPythonInputSplitter, self).push_accepts_more()
589 697
590 698 def transform_cell(self, cell):
591 699 """Process and translate a cell of input.
592 700 """
593 701 self.reset()
594 702 try:
595 703 self.push(cell)
596 704 self.flush_transformers()
597 705 return self.source
598 706 finally:
599 707 self.reset()
600 708
601 709 def push(self, lines):
602 710 """Push one or more lines of IPython input.
603 711
604 712 This stores the given lines and returns a status code indicating
605 713 whether the code forms a complete Python block or not, after processing
606 714 all input lines for special IPython syntax.
607 715
608 716 Any exceptions generated in compilation are swallowed, but if an
609 717 exception was produced, the method returns True.
610 718
611 719 Parameters
612 720 ----------
613 721 lines : string
614 722 One or more lines of Python input.
615 723
616 724 Returns
617 725 -------
618 726 is_complete : boolean
619 727 True if the current input source (the result of the current input
620 728 plus prior inputs) forms a complete Python execution block. Note that
621 729 this value is also stored as a private attribute (_is_complete), so it
622 730 can be queried at any time.
623 731 """
624 732
625 733 # We must ensure all input is pure unicode
626 734 lines = cast_unicode(lines, self.encoding)
627 735 # ''.splitlines() --> [], but we need to push the empty line to transformers
628 736 lines_list = lines.splitlines()
629 737 if not lines_list:
630 738 lines_list = ['']
631 739
632 740 # Store raw source before applying any transformations to it. Note
633 741 # that this must be done *after* the reset() call that would otherwise
634 742 # flush the buffer.
635 743 self._store(lines, self._buffer_raw, 'source_raw')
636 744
637 745 for line in lines_list:
638 746 out = self.push_line(line)
639 747
640 748 return out
641 749
642 750 def push_line(self, line):
643 751 buf = self._buffer
644 752
645 753 def _accumulating(dbg):
646 754 #print(dbg)
647 755 self.transformer_accumulating = True
648 756 return False
649 757
650 758 for transformer in self.physical_line_transforms:
651 759 line = transformer.push(line)
652 760 if line is None:
653 761 return _accumulating(transformer)
654 762
655 763 if not self.within_python_line:
656 764 line = self.assemble_logical_lines.push(line)
657 765 if line is None:
658 766 return _accumulating('acc logical line')
659 767
660 768 for transformer in self.logical_line_transforms:
661 769 line = transformer.push(line)
662 770 if line is None:
663 771 return _accumulating(transformer)
664 772
665 773 line = self.assemble_python_lines.push(line)
666 774 if line is None:
667 775 self.within_python_line = True
668 776 return _accumulating('acc python line')
669 777 else:
670 778 self.within_python_line = False
671 779
672 780 for transformer in self.python_line_transforms:
673 781 line = transformer.push(line)
674 782 if line is None:
675 783 return _accumulating(transformer)
676 784
677 785 #print("transformers clear") #debug
678 786 self.transformer_accumulating = False
679 787 return super(IPythonInputSplitter, self).push(line)
@@ -1,614 +1,638 b''
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
616 indentation_samples = [
617 ('a = 1', 0),
618 ('for a in b:', 4),
619 ('def f():', 4),
620 ('def f(): #comment', 4),
621 ('a = ":#not a comment"', 0),
622 ('def f():\n a = 1', 4),
623 ('def f():\n return 1', 0),
624 ('for a in b:\n'
625 ' if a < 0:'
626 ' continue', 3),
627 ('a = {', 4),
628 ('a = {\n'
629 ' 1,', 5),
630 ('b = """123', 0),
631 ('', 0),
632 ]
633
634 def test_find_next_indent():
635 for code, exp in indentation_samples:
636 res = isp.find_next_indent(code)
637 msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code)
638 assert res == exp, msg
General Comments 0
You need to be logged in to leave comments. Login now