Show More
@@ -247,8 +247,6 b' class InputSplitter(object):' | |||
|
247 | 247 | # synced to the source, so it can be queried at any time to obtain the code |
|
248 | 248 | # object; it will be None if the source doesn't compile to valid Python. |
|
249 | 249 | code = None |
|
250 | # Input mode | |
|
251 | input_mode = 'line' | |
|
252 | 250 | |
|
253 | 251 | # Private attributes |
|
254 | 252 | |
@@ -261,32 +259,12 b' class InputSplitter(object):' | |||
|
261 | 259 | # Boolean indicating whether the current block is complete |
|
262 | 260 | _is_complete = None |
|
263 | 261 | |
|
264 |
def __init__(self |
|
|
262 | def __init__(self): | |
|
265 | 263 | """Create a new InputSplitter instance. |
|
266 | ||
|
267 | Parameters | |
|
268 | ---------- | |
|
269 | input_mode : str | |
|
270 | ||
|
271 | One of ['line', 'cell']; default is 'line'. | |
|
272 | ||
|
273 | The input_mode parameter controls how new inputs are used when fed via | |
|
274 | the :meth:`push` method: | |
|
275 | ||
|
276 | - 'line': meant for line-oriented clients, inputs are appended one at a | |
|
277 | time to the internal buffer and the whole buffer is compiled. | |
|
278 | ||
|
279 | - 'cell': meant for clients that can edit multi-line 'cells' of text at | |
|
280 | a time. A cell can contain one or more blocks that can be compile in | |
|
281 | 'single' mode by Python. In this mode, each new input new input | |
|
282 | completely replaces all prior inputs. Cell mode is thus equivalent | |
|
283 | to prepending a full reset() to every push() call. | |
|
284 | 264 | """ |
|
285 | 265 | self._buffer = [] |
|
286 | 266 | self._compile = codeop.CommandCompiler() |
|
287 | 267 | self.encoding = get_input_encoding() |
|
288 | self.input_mode = InputSplitter.input_mode if input_mode is None \ | |
|
289 | else input_mode | |
|
290 | 268 | |
|
291 | 269 | def reset(self): |
|
292 | 270 | """Reset the input buffer and associated state.""" |
@@ -326,9 +304,6 b' class InputSplitter(object):' | |||
|
326 | 304 | this value is also stored as a private attribute (``_is_complete``), so it |
|
327 | 305 | can be queried at any time. |
|
328 | 306 | """ |
|
329 | if self.input_mode == 'cell': | |
|
330 | self.reset() | |
|
331 | ||
|
332 | 307 | self._store(lines) |
|
333 | 308 | source = self.source |
|
334 | 309 | |
@@ -388,39 +363,34 b' class InputSplitter(object):' | |||
|
388 | 363 | """ |
|
389 | 364 | |
|
390 | 365 | # With incomplete input, unconditionally accept more |
|
366 | # A syntax error also sets _is_complete to True - see push() | |
|
391 | 367 | if not self._is_complete: |
|
368 | #print("Not complete") # debug | |
|
392 | 369 | return True |
|
393 | ||
|
394 | # If we already have complete input and we're flush left, the answer | |
|
395 | # depends. In line mode, if there hasn't been any indentation, | |
|
396 | # that's it. If we've come back from some indentation, we need | |
|
397 | # the blank final line to finish. | |
|
398 | # In cell mode, we need to check how many blocks the input so far | |
|
399 | # compiles into, because if there's already more than one full | |
|
400 | # independent block of input, then the client has entered full | |
|
401 | # 'cell' mode and is feeding lines that each is complete. In this | |
|
402 | # case we should then keep accepting. The Qt terminal-like console | |
|
403 | # does precisely this, to provide the convenience of terminal-like | |
|
404 | # input of single expressions, but allowing the user (with a | |
|
405 | # separate keystroke) to switch to 'cell' mode and type multiple | |
|
406 | # expressions in one shot. | |
|
370 | ||
|
371 | # The user can make any (complete) input execute by leaving a blank line | |
|
372 | last_line = self.source.splitlines()[-1] | |
|
373 | if (not last_line) or last_line.isspace(): | |
|
374 | #print("Blank line") # debug | |
|
375 | return False | |
|
376 | ||
|
377 | # If there's just a single AST node, and we're flush left, as is the | |
|
378 | # case after a simple statement such as 'a=1', we want to execute it | |
|
379 | # straight away. | |
|
407 | 380 | if self.indent_spaces==0: |
|
408 | if self.input_mode=='line': | |
|
409 | if not self._full_dedent: | |
|
410 | return False | |
|
381 | try: | |
|
382 | code_ast = ast.parse(u''.join(self._buffer)) | |
|
383 | except Exception: | |
|
384 | #print("Can't parse AST") # debug | |
|
385 | return False | |
|
411 | 386 | else: |
|
412 | try: | |
|
413 | code_ast = ast.parse(u''.join(self._buffer)) | |
|
414 | except Exception: | |
|
387 | if len(code_ast.body) == 1 and \ | |
|
388 | not hasattr(code_ast.body[0], 'body'): | |
|
389 | #print("Simple statement") # debug | |
|
415 | 390 | return False |
|
416 | else: | |
|
417 | if len(code_ast.body) == 1: | |
|
418 | return False | |
|
419 | 391 | |
|
420 | # When input is complete, then termination is marked by an extra blank | |
|
421 | # line at the end. | |
|
422 | last_line = self.source.splitlines()[-1] | |
|
423 | return bool(last_line and not last_line.isspace()) | |
|
392 | # General fallback - accept more code | |
|
393 | return True | |
|
424 | 394 | |
|
425 | 395 | #------------------------------------------------------------------------ |
|
426 | 396 | # Private interface |
@@ -510,9 +480,9 b' class IPythonInputSplitter(InputSplitter):' | |||
|
510 | 480 | # List with lines of raw input accumulated so far. |
|
511 | 481 | _buffer_raw = None |
|
512 | 482 | |
|
513 |
def __init__(self, |
|
|
483 | def __init__(self, physical_line_transforms=None, | |
|
514 | 484 | logical_line_transforms=None, python_line_transforms=None): |
|
515 |
super(IPythonInputSplitter, self).__init__( |
|
|
485 | super(IPythonInputSplitter, self).__init__() | |
|
516 | 486 | self._buffer_raw = [] |
|
517 | 487 | self._validate = True |
|
518 | 488 | |
@@ -641,44 +611,14 b' class IPythonInputSplitter(InputSplitter):' | |||
|
641 | 611 | if not lines_list: |
|
642 | 612 | lines_list = [''] |
|
643 | 613 | |
|
644 | # Transform logic | |
|
645 | # | |
|
646 | # We only apply the line transformers to the input if we have either no | |
|
647 | # input yet, or complete input, or if the last line of the buffer ends | |
|
648 | # with ':' (opening an indented block). This prevents the accidental | |
|
649 | # transformation of escapes inside multiline expressions like | |
|
650 | # triple-quoted strings or parenthesized expressions. | |
|
651 | # | |
|
652 | # The last heuristic, while ugly, ensures that the first line of an | |
|
653 | # indented block is correctly transformed. | |
|
654 | # | |
|
655 | # FIXME: try to find a cleaner approach for this last bit. | |
|
656 | ||
|
657 | # If we were in 'block' mode, since we're going to pump the parent | |
|
658 | # class by hand line by line, we need to temporarily switch out to | |
|
659 | # 'line' mode, do a single manual reset and then feed the lines one | |
|
660 | # by one. Note that this only matters if the input has more than one | |
|
661 | # line. | |
|
662 | changed_input_mode = False | |
|
663 | ||
|
664 | if self.input_mode == 'cell': | |
|
665 | self.reset() | |
|
666 | changed_input_mode = True | |
|
667 | saved_input_mode = 'cell' | |
|
668 | self.input_mode = 'line' | |
|
669 | ||
|
670 | 614 | # Store raw source before applying any transformations to it. Note |
|
671 | 615 | # that this must be done *after* the reset() call that would otherwise |
|
672 | 616 | # flush the buffer. |
|
673 | 617 | self._store(lines, self._buffer_raw, 'source_raw') |
|
674 | 618 | |
|
675 | try: | |
|
676 | for line in lines_list: | |
|
677 | out = self.push_line(line) | |
|
678 | finally: | |
|
679 | if changed_input_mode: | |
|
680 | self.input_mode = saved_input_mode | |
|
681 | ||
|
619 | for line in lines_list: | |
|
620 | out = self.push_line(line) | |
|
621 | ||
|
682 | 622 | return out |
|
683 | 623 | |
|
684 | 624 | def push_line(self, line): |
@@ -168,9 +168,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||
|
168 | 168 | self.assertEqual(isp.indent_spaces, 0) |
|
169 | 169 | |
|
170 | 170 | def test_indent2(self): |
|
171 | # In cell mode, inputs must be fed in whole blocks, so skip this test | |
|
172 | if self.isp.input_mode == 'cell': return | |
|
173 | ||
|
174 | 171 | isp = self.isp |
|
175 | 172 | isp.push('if 1:') |
|
176 | 173 | self.assertEqual(isp.indent_spaces, 4) |
@@ -181,9 +178,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||
|
181 | 178 | self.assertEqual(isp.indent_spaces, 4) |
|
182 | 179 | |
|
183 | 180 | def test_indent3(self): |
|
184 | # In cell mode, inputs must be fed in whole blocks, so skip this test | |
|
185 | if self.isp.input_mode == 'cell': return | |
|
186 | ||
|
187 | 181 | isp = self.isp |
|
188 | 182 | # When a multiline statement contains parens or multiline strings, we |
|
189 | 183 | # shouldn't get confused. |
@@ -192,9 +186,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||
|
192 | 186 | self.assertEqual(isp.indent_spaces, 4) |
|
193 | 187 | |
|
194 | 188 | def test_indent4(self): |
|
195 | # In cell mode, inputs must be fed in whole blocks, so skip this test | |
|
196 | if self.isp.input_mode == 'cell': return | |
|
197 | ||
|
198 | 189 | isp = self.isp |
|
199 | 190 | # whitespace after ':' should not screw up indent level |
|
200 | 191 | isp.push('if 1: \n x=1') |
@@ -279,23 +270,12 b' class InputSplitterTestCase(unittest.TestCase):' | |||
|
279 | 270 | isp.push(' a = 1') |
|
280 | 271 | self.assertFalse(isp.push('b = [1,')) |
|
281 | 272 | |
|
282 | def test_replace_mode(self): | |
|
283 | isp = self.isp | |
|
284 | isp.input_mode = 'cell' | |
|
285 | isp.push('x=1') | |
|
286 | self.assertEqual(isp.source, 'x=1\n') | |
|
287 | isp.push('x=2') | |
|
288 | self.assertEqual(isp.source, 'x=2\n') | |
|
289 | ||
|
290 | 273 | def test_push_accepts_more(self): |
|
291 | 274 | isp = self.isp |
|
292 | 275 | isp.push('x=1') |
|
293 | 276 | self.assertFalse(isp.push_accepts_more()) |
|
294 | 277 | |
|
295 | 278 | def test_push_accepts_more2(self): |
|
296 | # In cell mode, inputs must be fed in whole blocks, so skip this test | |
|
297 | if self.isp.input_mode == 'cell': return | |
|
298 | ||
|
299 | 279 | isp = self.isp |
|
300 | 280 | isp.push('if 1:') |
|
301 | 281 | self.assertTrue(isp.push_accepts_more()) |
@@ -310,9 +290,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||
|
310 | 290 | self.assertFalse(isp.push_accepts_more()) |
|
311 | 291 | |
|
312 | 292 | def test_push_accepts_more4(self): |
|
313 | # In cell mode, inputs must be fed in whole blocks, so skip this test | |
|
314 | if self.isp.input_mode == 'cell': return | |
|
315 | ||
|
316 | 293 | isp = self.isp |
|
317 | 294 | # When a multiline statement contains parens or multiline strings, we |
|
318 | 295 | # shouldn't get confused. |
@@ -331,14 +308,13 b' class InputSplitterTestCase(unittest.TestCase):' | |||
|
331 | 308 | self.assertFalse(isp.push_accepts_more()) |
|
332 | 309 | |
|
333 | 310 | def test_push_accepts_more5(self): |
|
334 | # In cell mode, inputs must be fed in whole blocks, so skip this test | |
|
335 | if self.isp.input_mode == 'cell': return | |
|
336 | ||
|
337 | 311 | isp = self.isp |
|
338 | 312 | isp.push('try:') |
|
339 | 313 | isp.push(' a = 5') |
|
340 | 314 | isp.push('except:') |
|
341 | 315 | isp.push(' raise') |
|
316 | # We want to be able to add an else: block at this point, so it should | |
|
317 | # wait for a blank line. | |
|
342 | 318 | self.assertTrue(isp.push_accepts_more()) |
|
343 | 319 | |
|
344 | 320 | def test_continuation(self): |
@@ -431,7 +407,7 b' class IPythonInputTestCase(InputSplitterTestCase):' | |||
|
431 | 407 | """ |
|
432 | 408 | |
|
433 | 409 | def setUp(self): |
|
434 |
self.isp = isp.IPythonInputSplitter( |
|
|
410 | self.isp = isp.IPythonInputSplitter() | |
|
435 | 411 | |
|
436 | 412 | def test_syntax(self): |
|
437 | 413 | """Call all single-line syntax tests from the main object""" |
@@ -467,32 +443,6 b' class IPythonInputTestCase(InputSplitterTestCase):' | |||
|
467 | 443 | self.assertEqual(out.rstrip(), out_t) |
|
468 | 444 | self.assertEqual(out_raw.rstrip(), raw) |
|
469 | 445 | |
|
470 | ||
|
471 | class BlockIPythonInputTestCase(IPythonInputTestCase): | |
|
472 | ||
|
473 | # Deactivate tests that don't make sense for the block mode | |
|
474 | test_push3 = test_split = lambda s: None | |
|
475 | ||
|
476 | def setUp(self): | |
|
477 | self.isp = isp.IPythonInputSplitter(input_mode='cell') | |
|
478 | ||
|
479 | def test_syntax_multiline(self): | |
|
480 | isp = self.isp | |
|
481 | for example in syntax_ml.itervalues(): | |
|
482 | raw_parts = [] | |
|
483 | out_t_parts = [] | |
|
484 | for line_pairs in example: | |
|
485 | raw_parts, out_t_parts = zip(*line_pairs) | |
|
486 | ||
|
487 | raw = '\n'.join(r for r in raw_parts if r is not None) | |
|
488 | out_t = '\n'.join(o for o in out_t_parts if o is not None) | |
|
489 | ||
|
490 | isp.push(raw) | |
|
491 | out, out_raw = isp.source_raw_reset() | |
|
492 | # Match ignoring trailing whitespace | |
|
493 | self.assertEqual(out.rstrip(), out_t.rstrip()) | |
|
494 | self.assertEqual(out_raw.rstrip(), raw.rstrip()) | |
|
495 | ||
|
496 | 446 | def test_syntax_multiline_cell(self): |
|
497 | 447 | isp = self.isp |
|
498 | 448 | for example in syntax_ml.itervalues(): |
@@ -146,7 +146,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
146 | 146 | self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None) |
|
147 | 147 | self._hidden = False |
|
148 | 148 | self._highlighter = FrontendHighlighter(self) |
|
149 |
self._input_splitter = self._input_splitter_class( |
|
|
149 | self._input_splitter = self._input_splitter_class() | |
|
150 | 150 | self._kernel_manager = None |
|
151 | 151 | self._request_info = {} |
|
152 | 152 | self._request_info['execute'] = {}; |
@@ -204,6 +204,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
204 | 204 | prompt created. When triggered by an Enter/Return key press, |
|
205 | 205 | 'interactive' is True; otherwise, it is False. |
|
206 | 206 | """ |
|
207 | self._input_splitter.reset() | |
|
207 | 208 | complete = self._input_splitter.push(source) |
|
208 | 209 | if interactive: |
|
209 | 210 | complete = not self._input_splitter.push_accepts_more() |
@@ -82,7 +82,7 b' def get_pasted_lines(sentinel, l_input=py3compat.input):' | |||
|
82 | 82 | class TerminalMagics(Magics): |
|
83 | 83 | def __init__(self, shell): |
|
84 | 84 | super(TerminalMagics, self).__init__(shell) |
|
85 |
self.input_splitter = IPythonInputSplitter( |
|
|
85 | self.input_splitter = IPythonInputSplitter() | |
|
86 | 86 | |
|
87 | 87 | def cleanup_input(self, block): |
|
88 | 88 | """Apply all possible IPython cleanups to an input block. |
General Comments 0
You need to be logged in to leave comments.
Login now