Show More
@@ -247,8 +247,6 b' class InputSplitter(object):' | |||||
247 | # synced to the source, so it can be queried at any time to obtain the code |
|
247 | # synced to the source, so it can be queried at any time to obtain the code | |
248 | # object; it will be None if the source doesn't compile to valid Python. |
|
248 | # object; it will be None if the source doesn't compile to valid Python. | |
249 | code = None |
|
249 | code = None | |
250 | # Input mode |
|
|||
251 | input_mode = 'line' |
|
|||
252 |
|
250 | |||
253 | # Private attributes |
|
251 | # Private attributes | |
254 |
|
252 | |||
@@ -261,32 +259,12 b' class InputSplitter(object):' | |||||
261 | # Boolean indicating whether the current block is complete |
|
259 | # Boolean indicating whether the current block is complete | |
262 | _is_complete = None |
|
260 | _is_complete = None | |
263 |
|
261 | |||
264 |
def __init__(self |
|
262 | def __init__(self): | |
265 | """Create a new InputSplitter instance. |
|
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 | self._buffer = [] |
|
265 | self._buffer = [] | |
286 | self._compile = codeop.CommandCompiler() |
|
266 | self._compile = codeop.CommandCompiler() | |
287 | self.encoding = get_input_encoding() |
|
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 | def reset(self): |
|
269 | def reset(self): | |
292 | """Reset the input buffer and associated state.""" |
|
270 | """Reset the input buffer and associated state.""" | |
@@ -326,9 +304,6 b' class InputSplitter(object):' | |||||
326 | this value is also stored as a private attribute (``_is_complete``), so it |
|
304 | this value is also stored as a private attribute (``_is_complete``), so it | |
327 | can be queried at any time. |
|
305 | can be queried at any time. | |
328 | """ |
|
306 | """ | |
329 | if self.input_mode == 'cell': |
|
|||
330 | self.reset() |
|
|||
331 |
|
||||
332 | self._store(lines) |
|
307 | self._store(lines) | |
333 | source = self.source |
|
308 | source = self.source | |
334 |
|
309 | |||
@@ -388,39 +363,34 b' class InputSplitter(object):' | |||||
388 | """ |
|
363 | """ | |
389 |
|
364 | |||
390 | # With incomplete input, unconditionally accept more |
|
365 | # With incomplete input, unconditionally accept more | |
|
366 | # A syntax error also sets _is_complete to True - see push() | |||
391 | if not self._is_complete: |
|
367 | if not self._is_complete: | |
|
368 | #print("Not complete") # debug | |||
392 | return True |
|
369 | return True | |
393 |
|
370 | |||
394 | # If we already have complete input and we're flush left, the answer |
|
371 | # The user can make any (complete) input execute by leaving a blank line | |
395 | # depends. In line mode, if there hasn't been any indentation, |
|
372 | last_line = self.source.splitlines()[-1] | |
396 | # that's it. If we've come back from some indentation, we need |
|
373 | if (not last_line) or last_line.isspace(): | |
397 | # the blank final line to finish. |
|
374 | #print("Blank line") # debug | |
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. |
|
|||
407 | if self.indent_spaces==0: |
|
|||
408 | if self.input_mode=='line': |
|
|||
409 | if not self._full_dedent: |
|
|||
410 |
|
|
375 | return False | |
411 |
|
|
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. | |||
|
380 | if self.indent_spaces==0: | |||
412 |
|
|
381 | try: | |
413 |
|
|
382 | code_ast = ast.parse(u''.join(self._buffer)) | |
414 |
|
|
383 | except Exception: | |
|
384 | #print("Can't parse AST") # debug | |||
415 |
|
|
385 | return False | |
416 |
|
|
386 | else: | |
417 |
|
|
387 | if len(code_ast.body) == 1 and \ | |
|
388 | not hasattr(code_ast.body[0], 'body'): | |||
|
389 | #print("Simple statement") # debug | |||
418 |
|
|
390 | return False | |
419 |
|
391 | |||
420 | # When input is complete, then termination is marked by an extra blank |
|
392 | # General fallback - accept more code | |
421 | # line at the end. |
|
393 | return True | |
422 | last_line = self.source.splitlines()[-1] |
|
|||
423 | return bool(last_line and not last_line.isspace()) |
|
|||
424 |
|
394 | |||
425 | #------------------------------------------------------------------------ |
|
395 | #------------------------------------------------------------------------ | |
426 | # Private interface |
|
396 | # Private interface | |
@@ -510,9 +480,9 b' class IPythonInputSplitter(InputSplitter):' | |||||
510 | # List with lines of raw input accumulated so far. |
|
480 | # List with lines of raw input accumulated so far. | |
511 | _buffer_raw = None |
|
481 | _buffer_raw = None | |
512 |
|
482 | |||
513 |
def __init__(self, |
|
483 | def __init__(self, physical_line_transforms=None, | |
514 | logical_line_transforms=None, python_line_transforms=None): |
|
484 | logical_line_transforms=None, python_line_transforms=None): | |
515 |
super(IPythonInputSplitter, self).__init__( |
|
485 | super(IPythonInputSplitter, self).__init__() | |
516 | self._buffer_raw = [] |
|
486 | self._buffer_raw = [] | |
517 | self._validate = True |
|
487 | self._validate = True | |
518 |
|
488 | |||
@@ -641,43 +611,13 b' class IPythonInputSplitter(InputSplitter):' | |||||
641 | if not lines_list: |
|
611 | if not lines_list: | |
642 | lines_list = [''] |
|
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 | # Store raw source before applying any transformations to it. Note |
|
614 | # Store raw source before applying any transformations to it. Note | |
671 | # that this must be done *after* the reset() call that would otherwise |
|
615 | # that this must be done *after* the reset() call that would otherwise | |
672 | # flush the buffer. |
|
616 | # flush the buffer. | |
673 | self._store(lines, self._buffer_raw, 'source_raw') |
|
617 | self._store(lines, self._buffer_raw, 'source_raw') | |
674 |
|
618 | |||
675 | try: |
|
|||
676 |
|
|
619 | for line in lines_list: | |
677 |
|
|
620 | out = self.push_line(line) | |
678 | finally: |
|
|||
679 | if changed_input_mode: |
|
|||
680 | self.input_mode = saved_input_mode |
|
|||
681 |
|
621 | |||
682 | return out |
|
622 | return out | |
683 |
|
623 |
@@ -168,9 +168,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
168 | self.assertEqual(isp.indent_spaces, 0) |
|
168 | self.assertEqual(isp.indent_spaces, 0) | |
169 |
|
169 | |||
170 | def test_indent2(self): |
|
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 | isp = self.isp |
|
171 | isp = self.isp | |
175 | isp.push('if 1:') |
|
172 | isp.push('if 1:') | |
176 | self.assertEqual(isp.indent_spaces, 4) |
|
173 | self.assertEqual(isp.indent_spaces, 4) | |
@@ -181,9 +178,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
181 | self.assertEqual(isp.indent_spaces, 4) |
|
178 | self.assertEqual(isp.indent_spaces, 4) | |
182 |
|
179 | |||
183 | def test_indent3(self): |
|
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 | isp = self.isp |
|
181 | isp = self.isp | |
188 | # When a multiline statement contains parens or multiline strings, we |
|
182 | # When a multiline statement contains parens or multiline strings, we | |
189 | # shouldn't get confused. |
|
183 | # shouldn't get confused. | |
@@ -192,9 +186,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
192 | self.assertEqual(isp.indent_spaces, 4) |
|
186 | self.assertEqual(isp.indent_spaces, 4) | |
193 |
|
187 | |||
194 | def test_indent4(self): |
|
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 | isp = self.isp |
|
189 | isp = self.isp | |
199 | # whitespace after ':' should not screw up indent level |
|
190 | # whitespace after ':' should not screw up indent level | |
200 | isp.push('if 1: \n x=1') |
|
191 | isp.push('if 1: \n x=1') | |
@@ -279,23 +270,12 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
279 | isp.push(' a = 1') |
|
270 | isp.push(' a = 1') | |
280 | self.assertFalse(isp.push('b = [1,')) |
|
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 | def test_push_accepts_more(self): |
|
273 | def test_push_accepts_more(self): | |
291 | isp = self.isp |
|
274 | isp = self.isp | |
292 | isp.push('x=1') |
|
275 | isp.push('x=1') | |
293 | self.assertFalse(isp.push_accepts_more()) |
|
276 | self.assertFalse(isp.push_accepts_more()) | |
294 |
|
277 | |||
295 | def test_push_accepts_more2(self): |
|
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 | isp = self.isp |
|
279 | isp = self.isp | |
300 | isp.push('if 1:') |
|
280 | isp.push('if 1:') | |
301 | self.assertTrue(isp.push_accepts_more()) |
|
281 | self.assertTrue(isp.push_accepts_more()) | |
@@ -310,9 +290,6 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
310 | self.assertFalse(isp.push_accepts_more()) |
|
290 | self.assertFalse(isp.push_accepts_more()) | |
311 |
|
291 | |||
312 | def test_push_accepts_more4(self): |
|
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 | isp = self.isp |
|
293 | isp = self.isp | |
317 | # When a multiline statement contains parens or multiline strings, we |
|
294 | # When a multiline statement contains parens or multiline strings, we | |
318 | # shouldn't get confused. |
|
295 | # shouldn't get confused. | |
@@ -331,14 +308,13 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
331 | self.assertFalse(isp.push_accepts_more()) |
|
308 | self.assertFalse(isp.push_accepts_more()) | |
332 |
|
309 | |||
333 | def test_push_accepts_more5(self): |
|
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 | isp = self.isp |
|
311 | isp = self.isp | |
338 | isp.push('try:') |
|
312 | isp.push('try:') | |
339 | isp.push(' a = 5') |
|
313 | isp.push(' a = 5') | |
340 | isp.push('except:') |
|
314 | isp.push('except:') | |
341 | isp.push(' raise') |
|
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 | self.assertTrue(isp.push_accepts_more()) |
|
318 | self.assertTrue(isp.push_accepts_more()) | |
343 |
|
319 | |||
344 | def test_continuation(self): |
|
320 | def test_continuation(self): | |
@@ -431,7 +407,7 b' class IPythonInputTestCase(InputSplitterTestCase):' | |||||
431 | """ |
|
407 | """ | |
432 |
|
408 | |||
433 | def setUp(self): |
|
409 | def setUp(self): | |
434 |
self.isp = isp.IPythonInputSplitter( |
|
410 | self.isp = isp.IPythonInputSplitter() | |
435 |
|
411 | |||
436 | def test_syntax(self): |
|
412 | def test_syntax(self): | |
437 | """Call all single-line syntax tests from the main object""" |
|
413 | """Call all single-line syntax tests from the main object""" | |
@@ -467,32 +443,6 b' class IPythonInputTestCase(InputSplitterTestCase):' | |||||
467 | self.assertEqual(out.rstrip(), out_t) |
|
443 | self.assertEqual(out.rstrip(), out_t) | |
468 | self.assertEqual(out_raw.rstrip(), raw) |
|
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 | def test_syntax_multiline_cell(self): |
|
446 | def test_syntax_multiline_cell(self): | |
497 | isp = self.isp |
|
447 | isp = self.isp | |
498 | for example in syntax_ml.itervalues(): |
|
448 | for example in syntax_ml.itervalues(): |
@@ -146,7 +146,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
146 | self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None) |
|
146 | self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None) | |
147 | self._hidden = False |
|
147 | self._hidden = False | |
148 | self._highlighter = FrontendHighlighter(self) |
|
148 | self._highlighter = FrontendHighlighter(self) | |
149 |
self._input_splitter = self._input_splitter_class( |
|
149 | self._input_splitter = self._input_splitter_class() | |
150 | self._kernel_manager = None |
|
150 | self._kernel_manager = None | |
151 | self._request_info = {} |
|
151 | self._request_info = {} | |
152 | self._request_info['execute'] = {}; |
|
152 | self._request_info['execute'] = {}; | |
@@ -204,6 +204,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
204 | prompt created. When triggered by an Enter/Return key press, |
|
204 | prompt created. When triggered by an Enter/Return key press, | |
205 | 'interactive' is True; otherwise, it is False. |
|
205 | 'interactive' is True; otherwise, it is False. | |
206 | """ |
|
206 | """ | |
|
207 | self._input_splitter.reset() | |||
207 | complete = self._input_splitter.push(source) |
|
208 | complete = self._input_splitter.push(source) | |
208 | if interactive: |
|
209 | if interactive: | |
209 | complete = not self._input_splitter.push_accepts_more() |
|
210 | complete = not self._input_splitter.push_accepts_more() |
@@ -82,7 +82,7 b' def get_pasted_lines(sentinel, l_input=py3compat.input):' | |||||
82 | class TerminalMagics(Magics): |
|
82 | class TerminalMagics(Magics): | |
83 | def __init__(self, shell): |
|
83 | def __init__(self, shell): | |
84 | super(TerminalMagics, self).__init__(shell) |
|
84 | super(TerminalMagics, self).__init__(shell) | |
85 |
self.input_splitter = IPythonInputSplitter( |
|
85 | self.input_splitter = IPythonInputSplitter() | |
86 |
|
86 | |||
87 | def cleanup_input(self, block): |
|
87 | def cleanup_input(self, block): | |
88 | """Apply all possible IPython cleanups to an input block. |
|
88 | """Apply all possible IPython cleanups to an input block. |
General Comments 0
You need to be logged in to leave comments.
Login now