##// END OF EJS Templates
Clarify comment per @willingc's suggestion
Thomas Kluyver -
Show More
@@ -1,759 +1,761 b''
1 1 """Input handling and transformation machinery.
2 2
3 3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 4 input from a line-oriented frontend is complete and should be executed, and when
5 5 the user should be prompted for another line of code instead. The name 'input
6 6 splitter' is largely for historical reasons.
7 7
8 8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 9 with full support for the extended IPython syntax (magics, system calls, etc).
10 10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 12 and stores the results.
13 13
14 14 For more details, see the class docstrings below.
15 15 """
16 16
17 17 # Copyright (c) IPython Development Team.
18 18 # Distributed under the terms of the Modified BSD License.
19 19 import ast
20 20 import codeop
21 21 import 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 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 # A cache for calculating the current indentation.
294 # If the first value matches self.source, the second value is an integer
295 # number of spaces for the current indentation. If the first value does not
296 # match, self.source has changed, and the indentation must be recalculated.
293 # A cache for storing the current indentation
294 # The first value stores the most recently processed source input
295 # The second value is the number of spaces for the current indentation
296 # If self.source matches the first value, the second value is a valid
297 # current indentation. Otherwise, the cache is invalid and the indentation
298 # must be recalculated.
297 299 _indent_spaces_cache = None, None
298 300 # String, indicating the default input encoding. It is computed by default
299 301 # at initialization time via get_input_encoding(), but it can be reset by a
300 302 # client with specific knowledge of the encoding.
301 303 encoding = ''
302 304 # String where the current full source input is stored, properly encoded.
303 305 # Reading this attribute is the normal way of querying the currently pushed
304 306 # source code, that has been properly encoded.
305 307 source = ''
306 308 # Code object corresponding to the current source. It is automatically
307 309 # synced to the source, so it can be queried at any time to obtain the code
308 310 # object; it will be None if the source doesn't compile to valid Python.
309 311 code = None
310 312
311 313 # Private attributes
312 314
313 315 # List with lines of input accumulated so far
314 316 _buffer = None
315 317 # Command compiler
316 318 _compile = None
317 319 # Boolean indicating whether the current block is complete
318 320 _is_complete = None
319 321 # Boolean indicating whether the current block has an unrecoverable syntax error
320 322 _is_invalid = False
321 323
322 324 def __init__(self):
323 325 """Create a new InputSplitter instance.
324 326 """
325 327 self._buffer = []
326 328 self._compile = codeop.CommandCompiler()
327 329 self.encoding = get_input_encoding()
328 330
329 331 def reset(self):
330 332 """Reset the input buffer and associated state."""
331 333 self._buffer[:] = []
332 334 self.source = ''
333 335 self.code = None
334 336 self._is_complete = False
335 337 self._is_invalid = False
336 338
337 339 def source_reset(self):
338 340 """Return the input source and perform a full reset.
339 341 """
340 342 out = self.source
341 343 self.reset()
342 344 return out
343 345
344 346 def check_complete(self, source):
345 347 """Return whether a block of code is ready to execute, or should be continued
346 348
347 349 This is a non-stateful API, and will reset the state of this InputSplitter.
348 350
349 351 Parameters
350 352 ----------
351 353 source : string
352 354 Python input code, which can be multiline.
353 355
354 356 Returns
355 357 -------
356 358 status : str
357 359 One of 'complete', 'incomplete', or 'invalid' if source is not a
358 360 prefix of valid code.
359 361 indent_spaces : int or None
360 362 The number of spaces by which to indent the next line of code. If
361 363 status is not 'incomplete', this is None.
362 364 """
363 365 self.reset()
364 366 try:
365 367 self.push(source)
366 368 except SyntaxError:
367 369 # Transformers in IPythonInputSplitter can raise SyntaxError,
368 370 # which push() will not catch.
369 371 return 'invalid', None
370 372 else:
371 373 if self._is_invalid:
372 374 return 'invalid', None
373 375 elif self.push_accepts_more():
374 376 return 'incomplete', self.get_indent_spaces()
375 377 else:
376 378 return 'complete', None
377 379 finally:
378 380 self.reset()
379 381
380 382 def push(self, lines):
381 383 """Push one or more lines of input.
382 384
383 385 This stores the given lines and returns a status code indicating
384 386 whether the code forms a complete Python block or not.
385 387
386 388 Any exceptions generated in compilation are swallowed, but if an
387 389 exception was produced, the method returns True.
388 390
389 391 Parameters
390 392 ----------
391 393 lines : string
392 394 One or more lines of Python input.
393 395
394 396 Returns
395 397 -------
396 398 is_complete : boolean
397 399 True if the current input source (the result of the current input
398 400 plus prior inputs) forms a complete Python execution block. Note that
399 401 this value is also stored as a private attribute (``_is_complete``), so it
400 402 can be queried at any time.
401 403 """
402 404 self._store(lines)
403 405 source = self.source
404 406
405 407 # Before calling _compile(), reset the code object to None so that if an
406 408 # exception is raised in compilation, we don't mislead by having
407 409 # inconsistent code/source attributes.
408 410 self.code, self._is_complete = None, None
409 411 self._is_invalid = False
410 412
411 413 # Honor termination lines properly
412 414 if source.endswith('\\\n'):
413 415 return False
414 416
415 417 try:
416 418 with warnings.catch_warnings():
417 419 warnings.simplefilter('error', SyntaxWarning)
418 420 self.code = self._compile(source, symbol="exec")
419 421 # Invalid syntax can produce any of a number of different errors from
420 422 # inside the compiler, so we have to catch them all. Syntax errors
421 423 # immediately produce a 'ready' block, so the invalid Python can be
422 424 # sent to the kernel for evaluation with possible ipython
423 425 # special-syntax conversion.
424 426 except (SyntaxError, OverflowError, ValueError, TypeError,
425 427 MemoryError, SyntaxWarning):
426 428 self._is_complete = True
427 429 self._is_invalid = True
428 430 else:
429 431 # Compilation didn't produce any exceptions (though it may not have
430 432 # given a complete code object)
431 433 self._is_complete = self.code is not None
432 434
433 435 return self._is_complete
434 436
435 437 def push_accepts_more(self):
436 438 """Return whether a block of interactive input can accept more input.
437 439
438 440 This method is meant to be used by line-oriented frontends, who need to
439 441 guess whether a block is complete or not based solely on prior and
440 442 current input lines. The InputSplitter considers it has a complete
441 443 interactive block and will not accept more input when either:
442 444
443 445 * A SyntaxError is raised
444 446
445 447 * The code is complete and consists of a single line or a single
446 448 non-compound statement
447 449
448 450 * The code is complete and has a blank line at the end
449 451
450 452 If the current input produces a syntax error, this method immediately
451 453 returns False but does *not* raise the syntax error exception, as
452 454 typically clients will want to send invalid syntax to an execution
453 455 backend which might convert the invalid syntax into valid Python via
454 456 one of the dynamic IPython mechanisms.
455 457 """
456 458
457 459 # With incomplete input, unconditionally accept more
458 460 # A syntax error also sets _is_complete to True - see push()
459 461 if not self._is_complete:
460 462 #print("Not complete") # debug
461 463 return True
462 464
463 465 # The user can make any (complete) input execute by leaving a blank line
464 466 last_line = self.source.splitlines()[-1]
465 467 if (not last_line) or last_line.isspace():
466 468 #print("Blank line") # debug
467 469 return False
468 470
469 471 # If there's just a single line or AST node, and we're flush left, as is
470 472 # the case after a simple statement such as 'a=1', we want to execute it
471 473 # straight away.
472 474 if self.get_indent_spaces() == 0:
473 475 if len(self.source.splitlines()) <= 1:
474 476 return False
475 477
476 478 try:
477 479 code_ast = ast.parse(u''.join(self._buffer))
478 480 except Exception:
479 481 #print("Can't parse AST") # debug
480 482 return False
481 483 else:
482 484 if len(code_ast.body) == 1 and \
483 485 not hasattr(code_ast.body[0], 'body'):
484 486 #print("Simple statement") # debug
485 487 return False
486 488
487 489 # General fallback - accept more code
488 490 return True
489 491
490 492 def get_indent_spaces(self):
491 493 sourcefor, n = self._indent_spaces_cache
492 494 if sourcefor == self.source:
493 495 return n
494 496
495 497 # self.source always has a trailing newline
496 498 n = find_next_indent(self.source[:-1])
497 499 self._indent_spaces_cache = (self.source, n)
498 500 return n
499 501
500 502 def _store(self, lines, buffer=None, store='source'):
501 503 """Store one or more lines of input.
502 504
503 505 If input lines are not newline-terminated, a newline is automatically
504 506 appended."""
505 507
506 508 if buffer is None:
507 509 buffer = self._buffer
508 510
509 511 if lines.endswith('\n'):
510 512 buffer.append(lines)
511 513 else:
512 514 buffer.append(lines+'\n')
513 515 setattr(self, store, self._set_source(buffer))
514 516
515 517 def _set_source(self, buffer):
516 518 return u''.join(buffer)
517 519
518 520
519 521 class IPythonInputSplitter(InputSplitter):
520 522 """An input splitter that recognizes all of IPython's special syntax."""
521 523
522 524 # String with raw, untransformed input.
523 525 source_raw = ''
524 526
525 527 # Flag to track when a transformer has stored input that it hasn't given
526 528 # back yet.
527 529 transformer_accumulating = False
528 530
529 531 # Flag to track when assemble_python_lines has stored input that it hasn't
530 532 # given back yet.
531 533 within_python_line = False
532 534
533 535 # Private attributes
534 536
535 537 # List with lines of raw input accumulated so far.
536 538 _buffer_raw = None
537 539
538 540 def __init__(self, line_input_checker=True, physical_line_transforms=None,
539 541 logical_line_transforms=None, python_line_transforms=None):
540 542 super(IPythonInputSplitter, self).__init__()
541 543 self._buffer_raw = []
542 544 self._validate = True
543 545
544 546 if physical_line_transforms is not None:
545 547 self.physical_line_transforms = physical_line_transforms
546 548 else:
547 549 self.physical_line_transforms = [
548 550 leading_indent(),
549 551 classic_prompt(),
550 552 ipy_prompt(),
551 553 cellmagic(end_on_blank_line=line_input_checker),
552 554 ]
553 555
554 556 self.assemble_logical_lines = assemble_logical_lines()
555 557 if logical_line_transforms is not None:
556 558 self.logical_line_transforms = logical_line_transforms
557 559 else:
558 560 self.logical_line_transforms = [
559 561 help_end(),
560 562 escaped_commands(),
561 563 assign_from_magic(),
562 564 assign_from_system(),
563 565 ]
564 566
565 567 self.assemble_python_lines = assemble_python_lines()
566 568 if python_line_transforms is not None:
567 569 self.python_line_transforms = python_line_transforms
568 570 else:
569 571 # We don't use any of these at present
570 572 self.python_line_transforms = []
571 573
572 574 @property
573 575 def transforms(self):
574 576 "Quick access to all transformers."
575 577 return self.physical_line_transforms + \
576 578 [self.assemble_logical_lines] + self.logical_line_transforms + \
577 579 [self.assemble_python_lines] + self.python_line_transforms
578 580
579 581 @property
580 582 def transforms_in_use(self):
581 583 """Transformers, excluding logical line transformers if we're in a
582 584 Python line."""
583 585 t = self.physical_line_transforms[:]
584 586 if not self.within_python_line:
585 587 t += [self.assemble_logical_lines] + self.logical_line_transforms
586 588 return t + [self.assemble_python_lines] + self.python_line_transforms
587 589
588 590 def reset(self):
589 591 """Reset the input buffer and associated state."""
590 592 super(IPythonInputSplitter, self).reset()
591 593 self._buffer_raw[:] = []
592 594 self.source_raw = ''
593 595 self.transformer_accumulating = False
594 596 self.within_python_line = False
595 597
596 598 for t in self.transforms:
597 599 try:
598 600 t.reset()
599 601 except SyntaxError:
600 602 # Nothing that calls reset() expects to handle transformer
601 603 # errors
602 604 pass
603 605
604 606 def flush_transformers(self):
605 607 def _flush(transform, outs):
606 608 """yield transformed lines
607 609
608 610 always strings, never None
609 611
610 612 transform: the current transform
611 613 outs: an iterable of previously transformed inputs.
612 614 Each may be multiline, which will be passed
613 615 one line at a time to transform.
614 616 """
615 617 for out in outs:
616 618 for line in out.splitlines():
617 619 # push one line at a time
618 620 tmp = transform.push(line)
619 621 if tmp is not None:
620 622 yield tmp
621 623
622 624 # reset the transform
623 625 tmp = transform.reset()
624 626 if tmp is not None:
625 627 yield tmp
626 628
627 629 out = []
628 630 for t in self.transforms_in_use:
629 631 out = _flush(t, out)
630 632
631 633 out = list(out)
632 634 if out:
633 635 self._store('\n'.join(out))
634 636
635 637 def raw_reset(self):
636 638 """Return raw input only and perform a full reset.
637 639 """
638 640 out = self.source_raw
639 641 self.reset()
640 642 return out
641 643
642 644 def source_reset(self):
643 645 try:
644 646 self.flush_transformers()
645 647 return self.source
646 648 finally:
647 649 self.reset()
648 650
649 651 def push_accepts_more(self):
650 652 if self.transformer_accumulating:
651 653 return True
652 654 else:
653 655 return super(IPythonInputSplitter, self).push_accepts_more()
654 656
655 657 def transform_cell(self, cell):
656 658 """Process and translate a cell of input.
657 659 """
658 660 self.reset()
659 661 try:
660 662 self.push(cell)
661 663 self.flush_transformers()
662 664 return self.source
663 665 finally:
664 666 self.reset()
665 667
666 668 def push(self, lines):
667 669 """Push one or more lines of IPython input.
668 670
669 671 This stores the given lines and returns a status code indicating
670 672 whether the code forms a complete Python block or not, after processing
671 673 all input lines for special IPython syntax.
672 674
673 675 Any exceptions generated in compilation are swallowed, but if an
674 676 exception was produced, the method returns True.
675 677
676 678 Parameters
677 679 ----------
678 680 lines : string
679 681 One or more lines of Python input.
680 682
681 683 Returns
682 684 -------
683 685 is_complete : boolean
684 686 True if the current input source (the result of the current input
685 687 plus prior inputs) forms a complete Python execution block. Note that
686 688 this value is also stored as a private attribute (_is_complete), so it
687 689 can be queried at any time.
688 690 """
689 691
690 692 # We must ensure all input is pure unicode
691 693 lines = cast_unicode(lines, self.encoding)
692 694 # ''.splitlines() --> [], but we need to push the empty line to transformers
693 695 lines_list = lines.splitlines()
694 696 if not lines_list:
695 697 lines_list = ['']
696 698
697 699 # Store raw source before applying any transformations to it. Note
698 700 # that this must be done *after* the reset() call that would otherwise
699 701 # flush the buffer.
700 702 self._store(lines, self._buffer_raw, 'source_raw')
701 703
702 704 transformed_lines_list = []
703 705 for line in lines_list:
704 706 transformed = self._transform_line(line)
705 707 if transformed is not None:
706 708 transformed_lines_list.append(transformed)
707 709
708 710 if transformed_lines_list:
709 711 transformed_lines = '\n'.join(transformed_lines_list)
710 712 return super(IPythonInputSplitter, self).push(transformed_lines)
711 713 else:
712 714 # Got nothing back from transformers - they must be waiting for
713 715 # more input.
714 716 return False
715 717
716 718 def _transform_line(self, line):
717 719 """Push a line of input code through the various transformers.
718 720
719 721 Returns any output from the transformers, or None if a transformer
720 722 is accumulating lines.
721 723
722 724 Sets self.transformer_accumulating as a side effect.
723 725 """
724 726 def _accumulating(dbg):
725 727 #print(dbg)
726 728 self.transformer_accumulating = True
727 729 return None
728 730
729 731 for transformer in self.physical_line_transforms:
730 732 line = transformer.push(line)
731 733 if line is None:
732 734 return _accumulating(transformer)
733 735
734 736 if not self.within_python_line:
735 737 line = self.assemble_logical_lines.push(line)
736 738 if line is None:
737 739 return _accumulating('acc logical line')
738 740
739 741 for transformer in self.logical_line_transforms:
740 742 line = transformer.push(line)
741 743 if line is None:
742 744 return _accumulating(transformer)
743 745
744 746 line = self.assemble_python_lines.push(line)
745 747 if line is None:
746 748 self.within_python_line = True
747 749 return _accumulating('acc python line')
748 750 else:
749 751 self.within_python_line = False
750 752
751 753 for transformer in self.python_line_transforms:
752 754 line = transformer.push(line)
753 755 if line is None:
754 756 return _accumulating(transformer)
755 757
756 758 #print("transformers clear") #debug
757 759 self.transformer_accumulating = False
758 760 return line
759 761
General Comments 0
You need to be logged in to leave comments. Login now