Show More
@@ -55,7 +55,7 b' Authors' | |||||
55 | * Brian Granger |
|
55 | * Brian Granger | |
56 | """ |
|
56 | """ | |
57 | #----------------------------------------------------------------------------- |
|
57 | #----------------------------------------------------------------------------- | |
58 |
# Copyright (C) 2010 |
|
58 | # Copyright (C) 2010 The IPython Development Team | |
59 | # |
|
59 | # | |
60 | # Distributed under the terms of the BSD License. The full license is in |
|
60 | # Distributed under the terms of the BSD License. The full license is in | |
61 | # the file COPYING, distributed as part of this software. |
|
61 | # the file COPYING, distributed as part of this software. | |
@@ -685,20 +685,23 b' class IPythonInputSplitter(InputSplitter):' | |||||
685 | # String with raw, untransformed input. |
|
685 | # String with raw, untransformed input. | |
686 | source_raw = '' |
|
686 | source_raw = '' | |
687 |
|
687 | |||
|
688 | cell_magic_body = None | |||
|
689 | ||||
688 | # Private attributes |
|
690 | # Private attributes | |
689 |
|
691 | |||
690 | # List with lines of raw input accumulated so far. |
|
692 | # List with lines of raw input accumulated so far. | |
691 | _buffer_raw = None |
|
693 | _buffer_raw = None | |
692 |
|
694 | |||
693 | def __init__(self, input_mode=None): |
|
695 | def __init__(self, input_mode=None): | |
694 |
InputSplitter.__init__( |
|
696 | super(IPythonInputSplitter, self).__init__(input_mode) | |
695 | self._buffer_raw = [] |
|
697 | self._buffer_raw = [] | |
696 |
|
698 | |||
697 | def reset(self): |
|
699 | def reset(self): | |
698 | """Reset the input buffer and associated state.""" |
|
700 | """Reset the input buffer and associated state.""" | |
699 |
InputSplitter.reset( |
|
701 | super(IPythonInputSplitter, self).reset() | |
700 | self._buffer_raw[:] = [] |
|
702 | self._buffer_raw[:] = [] | |
701 | self.source_raw = '' |
|
703 | self.source_raw = '' | |
|
704 | self.cell_magic_body = None | |||
702 |
|
705 | |||
703 | def source_raw_reset(self): |
|
706 | def source_raw_reset(self): | |
704 | """Return input and raw source and perform a full reset. |
|
707 | """Return input and raw source and perform a full reset. | |
@@ -710,6 +713,26 b' class IPythonInputSplitter(InputSplitter):' | |||||
710 |
|
713 | |||
711 | def push(self, lines): |
|
714 | def push(self, lines): | |
712 | """Push one or more lines of IPython input. |
|
715 | """Push one or more lines of IPython input. | |
|
716 | ||||
|
717 | This stores the given lines and returns a status code indicating | |||
|
718 | whether the code forms a complete Python block or not, after processing | |||
|
719 | all input lines for special IPython syntax. | |||
|
720 | ||||
|
721 | Any exceptions generated in compilation are swallowed, but if an | |||
|
722 | exception was produced, the method returns True. | |||
|
723 | ||||
|
724 | Parameters | |||
|
725 | ---------- | |||
|
726 | lines : string | |||
|
727 | One or more lines of Python input. | |||
|
728 | ||||
|
729 | Returns | |||
|
730 | ------- | |||
|
731 | is_complete : boolean | |||
|
732 | True if the current input source (the result of the current input | |||
|
733 | plus prior inputs) forms a complete Python execution block. Note that | |||
|
734 | this value is also stored as a private attribute (_is_complete), so it | |||
|
735 | can be queried at any time. | |||
713 | """ |
|
736 | """ | |
714 | if not lines: |
|
737 | if not lines: | |
715 | return super(IPythonInputSplitter, self).push(lines) |
|
738 | return super(IPythonInputSplitter, self).push(lines) | |
@@ -717,6 +740,20 b' class IPythonInputSplitter(InputSplitter):' | |||||
717 | # We must ensure all input is pure unicode |
|
740 | # We must ensure all input is pure unicode | |
718 | lines = cast_unicode(lines, self.encoding) |
|
741 | lines = cast_unicode(lines, self.encoding) | |
719 |
|
742 | |||
|
743 | # cell magic support | |||
|
744 | #print('IM:', self.input_mode,'\n'+lines); print('---') # dbg | |||
|
745 | #if self.input_mode == 'cell' and lines.startswith('%%'): | |||
|
746 | if lines.startswith('%%'): | |||
|
747 | # Cell magics bypass all further transformations | |||
|
748 | self.reset() | |||
|
749 | self._is_complete = is_complete = True | |||
|
750 | first, _, body = lines.partition('\n') | |||
|
751 | magic_name, _, line = first.partition(' ') | |||
|
752 | magic_name = magic_name.lstrip(ESC_MAGIC) | |||
|
753 | self.cell_magic_body = body | |||
|
754 | tpl = 'get_ipython()._cell_magic(%r, %r)' | |||
|
755 | lines = tpl % (magic_name, line) | |||
|
756 | ||||
720 | lines_list = lines.splitlines() |
|
757 | lines_list = lines.splitlines() | |
721 |
|
758 | |||
722 | transforms = [transform_ipy_prompt, transform_classic_prompt, |
|
759 | transforms = [transform_ipy_prompt, transform_classic_prompt, |
@@ -2027,7 +2027,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2027 | """ |
|
2027 | """ | |
2028 | fn = self.find_line_magic(magic_name) |
|
2028 | fn = self.find_line_magic(magic_name) | |
2029 | if fn is None: |
|
2029 | if fn is None: | |
2030 |
error(" |
|
2030 | error("Line magic function `%%%s` not found." % magic_name) | |
2031 | else: |
|
2031 | else: | |
2032 | # Note: this is the distance in the stack to the user's frame. |
|
2032 | # Note: this is the distance in the stack to the user's frame. | |
2033 | # This will need to be updated if the internal calling logic gets |
|
2033 | # This will need to be updated if the internal calling logic gets | |
@@ -2048,7 +2048,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2048 | """ |
|
2048 | """ | |
2049 | fn = self.find_cell_magic(magic_name) |
|
2049 | fn = self.find_cell_magic(magic_name) | |
2050 | if fn is None: |
|
2050 | if fn is None: | |
2051 |
error(" |
|
2051 | error("Cell magic function `%%%%%s` not found." % magic_name) | |
2052 | else: |
|
2052 | else: | |
2053 | # Note: this is the distance in the stack to the user's frame. |
|
2053 | # Note: this is the distance in the stack to the user's frame. | |
2054 | # This will need to be updated if the internal calling logic gets |
|
2054 | # This will need to be updated if the internal calling logic gets | |
@@ -2475,6 +2475,11 b' class InteractiveShell(SingletonConfigurable):' | |||||
2475 | magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) |
|
2475 | magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) | |
2476 | return self.cell_magic(magic_name, line, cell) |
|
2476 | return self.cell_magic(magic_name, line, cell) | |
2477 |
|
2477 | |||
|
2478 | def _cell_magic(self, magic_name, line): | |||
|
2479 | cell = self._current_cell_magic_body | |||
|
2480 | self._current_cell_magic_body = None | |||
|
2481 | return self.cell_magic(magic_name, line, cell) | |||
|
2482 | ||||
2478 | def run_cell(self, raw_cell, store_history=False, silent=False): |
|
2483 | def run_cell(self, raw_cell, store_history=False, silent=False): | |
2479 | """Run a complete IPython cell. |
|
2484 | """Run a complete IPython cell. | |
2480 |
|
2485 | |||
@@ -2496,11 +2501,14 b' class InteractiveShell(SingletonConfigurable):' | |||||
2496 | if silent: |
|
2501 | if silent: | |
2497 | store_history = False |
|
2502 | store_history = False | |
2498 |
|
2503 | |||
2499 | if raw_cell.startswith('%%'): |
|
2504 | self.input_splitter.push(raw_cell) | |
2500 | return self.call_cell_magic(raw_cell, store_history) |
|
|||
2501 |
|
2505 | |||
2502 | for line in raw_cell.splitlines(): |
|
2506 | # Check for cell magics, which leave state behind. This interface is | |
2503 | self.input_splitter.push(line) |
|
2507 | # ugly, we need to do something cleaner later... Now the logic is | |
|
2508 | # simply that the input_splitter remembers if there was a cell magic, | |||
|
2509 | # and in that case we grab the cell body. | |||
|
2510 | if self.input_splitter.cell_magic_body is not None: | |||
|
2511 | self._current_cell_magic_body = self.input_splitter.cell_magic_body | |||
2504 | cell = self.input_splitter.source_reset() |
|
2512 | cell = self.input_splitter.source_reset() | |
2505 |
|
2513 | |||
2506 | with self.builtin_trap: |
|
2514 | with self.builtin_trap: |
@@ -495,10 +495,12 b' def test_env():' | |||||
495 | class CellMagicTestCase(TestCase): |
|
495 | class CellMagicTestCase(TestCase): | |
496 |
|
496 | |||
497 | def check_ident(self, magic): |
|
497 | def check_ident(self, magic): | |
|
498 | # Manually called, we get the result | |||
498 | out = _ip.cell_magic(magic, 'a', 'b') |
|
499 | out = _ip.cell_magic(magic, 'a', 'b') | |
499 | nt.assert_equals(out, ('a','b')) |
|
500 | nt.assert_equals(out, ('a','b')) | |
500 | out = _ip.run_cell('%%' + magic +' a\nb') |
|
501 | # Via run_cell, it goes into the user's namespace via displayhook | |
501 | nt.assert_equals(out, ('a','b')) |
|
502 | _ip.run_cell('%%' + magic +' c\nd') | |
|
503 | nt.assert_equals(_ip.user_ns['_'], ('c','d')) | |||
502 |
|
504 | |||
503 | def test_cell_magic_func_deco(self): |
|
505 | def test_cell_magic_func_deco(self): | |
504 | "Cell magic using simple decorator" |
|
506 | "Cell magic using simple decorator" | |
@@ -525,12 +527,19 b' class CellMagicTestCase(TestCase):' | |||||
525 | def cellm3(self, line, cell): |
|
527 | def cellm3(self, line, cell): | |
526 | return line, cell |
|
528 | return line, cell | |
527 |
|
529 | |||
|
530 | _ip.register_magics(MyMagics) | |||
|
531 | self.check_ident('cellm3') | |||
|
532 | ||||
|
533 | def test_cell_magic_class2(self): | |||
|
534 | "Cell magics declared via a class, #2" | |||
|
535 | @magics_class | |||
|
536 | class MyMagics2(Magics): | |||
|
537 | ||||
528 | @cell_magic('cellm4') |
|
538 | @cell_magic('cellm4') | |
529 | def cellm33(self, line, cell): |
|
539 | def cellm33(self, line, cell): | |
530 | return line, cell |
|
540 | return line, cell | |
531 |
|
541 | |||
532 | _ip.register_magics(MyMagics) |
|
542 | _ip.register_magics(MyMagics2) | |
533 | self.check_ident('cellm3') |
|
|||
534 | self.check_ident('cellm4') |
|
543 | self.check_ident('cellm4') | |
535 | # Check that nothing is registered as 'cellm33' |
|
544 | # Check that nothing is registered as 'cellm33' | |
536 | c33 = _ip.find_cell_magic('cellm33') |
|
545 | c33 = _ip.find_cell_magic('cellm33') |
General Comments 0
You need to be logged in to leave comments.
Login now