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