##// END OF EJS Templates
Clean up implementation of cell magics in inputsplitter....
Fernando Perez -
Show More
@@ -725,9 +725,13 b' class IPythonInputSplitter(InputSplitter):'
725 # String with raw, untransformed input.
725 # String with raw, untransformed input.
726 source_raw = ''
726 source_raw = ''
727
727
728 cell_magic_parts = []
728 # Flag to track when we're in the middle of processing a cell magic, since
729 # the logic has to change. In that case, we apply no transformations at
730 # all.
731 processing_cell_magic = False
729
732
730 cell_magic_mode = False
733 # Storage for all blocks of input that make up a cell magic
734 cell_magic_parts = []
731
735
732 # Private attributes
736 # Private attributes
733
737
@@ -745,7 +749,7 b' class IPythonInputSplitter(InputSplitter):'
745 self._buffer_raw[:] = []
749 self._buffer_raw[:] = []
746 self.source_raw = ''
750 self.source_raw = ''
747 self.cell_magic_parts = []
751 self.cell_magic_parts = []
748 self.cell_magic_mode = False
752 self.processing_cell_magic = False
749
753
750 def source_raw_reset(self):
754 def source_raw_reset(self):
751 """Return input and raw source and perform a full reset.
755 """Return input and raw source and perform a full reset.
@@ -756,172 +760,55 b' class IPythonInputSplitter(InputSplitter):'
756 return out, out_r
760 return out, out_r
757
761
758 def push_accepts_more(self):
762 def push_accepts_more(self):
759 if self.cell_magic_mode:
763 if self.processing_cell_magic:
760 return not self._is_complete
764 return not self._is_complete
761 else:
765 else:
762 return super(IPythonInputSplitter, self).push_accepts_more()
766 return super(IPythonInputSplitter, self).push_accepts_more()
763
767
764 def _push_line_mode(self, lines):
768 def _handle_cell_magic(self, lines):
765 """Push in line mode.
769 """Process lines when they start with %%, which marks cell magics.
766
767 This means that we only get individual 'lines' with each call, though
768 in practice each input may be multiline. But this is in contrast to
769 cell mode, which feeds the entirety of the cell from the start with
770 each call.
771 """
770 """
772 # cell magic support
771 self.processing_cell_magic = True
773 #print('#'*10)
772 first, _, body = lines.partition('\n')
774 #print(lines+'\n---') # dbg
773 magic_name, _, line = first.partition(' ')
775 #print (repr(lines)+'\n+++')
774 magic_name = magic_name.lstrip(ESC_MAGIC)
776 #print('raw', self._buffer_raw, 'validate', self.cell_magic_mode)
775 # We store the body of the cell and create a call to a method that
777 # Only trigger this block if we're at a 'fresh' pumping start.
776 # will use this stored value. This is ugly, but it's a first cut to
778 if lines.startswith('%%'):
777 # get it all working, as right now changing the return API of our
779 # Cell magics bypass all further transformations
778 # methods would require major refactoring.
780 self.cell_magic_mode = True
779 self.cell_magic_parts = [body]
781 first, _, body = lines.partition('\n')
780 tpl = 'get_ipython()._cell_magic(%r, %r)'
782 magic_name, _, line = first.partition(' ')
781 tlines = tpl % (magic_name, line)
783 magic_name = magic_name.lstrip(ESC_MAGIC)
782 self._store(tlines)
784 # We store the body of the cell and create a call to a method that
785 # will use this stored value. This is ugly, but it's a first cut to
786 # get it all working, as right now changing the return API of our
787 # methods would require major refactoring.
788 self.cell_magic_parts = [body]
789 tpl = 'get_ipython()._cell_magic(%r, %r)'
790 tlines = tpl % (magic_name, line)
791 self._store(tlines)
792 self._store(lines, self._buffer_raw, 'source_raw')
793 self._is_complete = last_two_blanks(lines)
794 #print('IC', self._is_complete) # dbg
795 return self._is_complete
796
797 if self.cell_magic_mode:
798 #print('c2 lines', repr(lines)) # dbg
799 # Find out if the last stored block has a whitespace line as its
800 # last line and also this line is whitespace, case in which we're
801 # done (two contiguous blank lines signal termination). Note that
802 # the storage logic *enforces* that every stored block is
803 # newline-terminated, so we grab everything but the last character
804 # so we can have the body of the block alone.
805 last_block = self.cell_magic_parts[-1]
806 self._is_complete = last_blank(last_block) and lines.isspace()
807 # Only store the raw input. For lines beyond the first one, we
808 # only store them for history purposes, and for execution we want
809 # the caller to only receive the _cell_magic() call.
810 self._store(lines, self._buffer_raw, 'source_raw')
811 self.cell_magic_parts.append(lines)
812 return self._is_complete
813
814 lines_list = lines.splitlines()
815
816 transforms = [transform_ipy_prompt, transform_classic_prompt,
817 transform_help_end, transform_escaped,
818 transform_assign_system, transform_assign_magic]
819
820 # Transform logic
821 #
822 # We only apply the line transformers to the input if we have either no
823 # input yet, or complete input, or if the last line of the buffer ends
824 # with ':' (opening an indented block). This prevents the accidental
825 # transformation of escapes inside multiline expressions like
826 # triple-quoted strings or parenthesized expressions.
827 #
828 # The last heuristic, while ugly, ensures that the first line of an
829 # indented block is correctly transformed.
830 #
831 # FIXME: try to find a cleaner approach for this last bit.
832
833 # Store raw source before applying any transformations to it. Note
834 # that this must be done *after* the reset() call that would otherwise
835 # flush the buffer.
836 self._store(lines, self._buffer_raw, 'source_raw')
783 self._store(lines, self._buffer_raw, 'source_raw')
784 # We can actually choose whether to allow for single blank lines here
785 # during input for clients that use cell mode to decide when to stop
786 # pushing input (currently only the Qt console).
787 # My first implementation did that, and then I realized it wasn't
788 # consistent with the terminal behavior, so I've reverted it to one
789 # line. But I'm leaving it here so we can easily test both behaviors,
790 # I kind of liked having full blank lines allowed in the cell magics...
791 #self._is_complete = last_two_blanks(lines)
792 self._is_complete = last_blank(lines)
793 return self._is_complete
837
794
838 push = super(IPythonInputSplitter, self).push
795 def _line_mode_cell_append(self, lines):
839 buf = self._buffer
796 """Append new content for a cell magic in line mode.
840 for line in lines_list:
797 """
841 if self._is_complete or not buf or \
798 # Only store the raw input. Lines beyond the first one are only only
842 (buf and buf[-1].rstrip().endswith((':', ','))):
799 # stored for history purposes; for execution the caller will grab the
843 for f in transforms:
800 # magic pieces from cell_magic_parts and will assemble the cell body
844 line = f(line)
845
846 out = push(line)
847 return out
848
849
850 def _push_cell_mode(self, lines):
851 """Push in cell mode.
852
853 This means that we get the entire cell with each call. Between resets,
854 the calls simply add more text to the input."""
855 #print('lines', repr(lines)) # dbg
856 if lines.startswith('%%'):
857 # Cell magics bypass all further transformations
858 self.cell_magic_mode = True
859 first, _, body = lines.partition('\n')
860 magic_name, _, line = first.partition(' ')
861 magic_name = magic_name.lstrip(ESC_MAGIC)
862 # We store the body of the cell and create a call to a method that
863 # will use this stored value. This is ugly, but it's a first cut to
864 # get it all working, as right now changing the return API of our
865 # methods would require major refactoring.
866 self.cell_magic_parts = [body]
867 tpl = 'get_ipython()._cell_magic(%r, %r)'
868 tlines = tpl % (magic_name, line)
869 self._store(tlines)
870 self._store(lines, self._buffer_raw, 'source_raw')
871 # We can actually choose whether to allow for single blank lines
872 # here.. My first implementation did that, and then I realized it
873 # wasn't consistent with the console behavior, so I've reverted it
874 # to one line. But I'm leaving it here so we can easily test both
875 # behaviors, I kind of liked having the extra blanks be allowed in
876 # the cell magics...
877 #self._is_complete = last_two_blanks(lines)
878 self._is_complete = last_blank(lines)
879 return self._is_complete
880
881 lines_list = lines.splitlines()
882
883 transforms = [transform_ipy_prompt, transform_classic_prompt,
884 transform_help_end, transform_escaped,
885 transform_assign_system, transform_assign_magic]
886
887 # Transform logic
888 #
889 # We only apply the line transformers to the input if we have either no
890 # input yet, or complete input, or if the last line of the buffer ends
891 # with ':' (opening an indented block). This prevents the accidental
892 # transformation of escapes inside multiline expressions like
893 # triple-quoted strings or parenthesized expressions.
894 #
895 # The last heuristic, while ugly, ensures that the first line of an
896 # indented block is correctly transformed.
897 #
898 # FIXME: try to find a cleaner approach for this last bit.
899
900 # In cell mode, since we're going to pump the parent class by hand line
901 # by line, we need to temporarily switch out to 'line' mode, do a
902 # single manual reset and then feed the lines one by one. Note that
903 # this only matters if the input has more than one line.
904 self.reset()
905 self.input_mode = 'line'
906
907 # Store raw source before applying any transformations to it. Note
908 # that this must be done *after* the reset() call that would otherwise
909 # flush the buffer.
910 self._store(lines, self._buffer_raw, 'source_raw')
801 self._store(lines, self._buffer_raw, 'source_raw')
911
802 self.cell_magic_parts.append(lines)
912 try:
803 # Find out if the last stored block has a whitespace line as its
913 push = super(IPythonInputSplitter, self).push
804 # last line and also this line is whitespace, case in which we're
914 buf = self._buffer
805 # done (two contiguous blank lines signal termination). Note that
915 for line in lines_list:
806 # the storage logic *enforces* that every stored block is
916 if self._is_complete or not buf or \
807 # newline-terminated, so we grab everything but the last character
917 (buf and buf[-1].rstrip().endswith((':', ','))):
808 # so we can have the body of the block alone.
918 for f in transforms:
809 last_block = self.cell_magic_parts[-1]
919 line = f(line)
810 self._is_complete = last_blank(last_block) and lines.isspace()
920
811 return self._is_complete
921 out = push(line)
922 finally:
923 self.input_mode = 'cell'
924 return out
925
812
926 def push(self, lines):
813 def push(self, lines):
927 """Push one or more lines of IPython input.
814 """Push one or more lines of IPython input.
@@ -952,11 +839,17 b' class IPythonInputSplitter(InputSplitter):'
952 # We must ensure all input is pure unicode
839 # We must ensure all input is pure unicode
953 lines = cast_unicode(lines, self.encoding)
840 lines = cast_unicode(lines, self.encoding)
954
841
955 if self.input_mode == 'line':
842 # If the entire input block is a cell magic, return after handling it
956 return self._push_line_mode(lines)
843 # as the rest of the transformation logic should be skipped.
957 else:
844 if lines.startswith('%%'):
958 return self._push_cell_mode(lines)
845 return self._handle_cell_magic(lines)
846
847 # In line mode, a cell magic can arrive in separate pieces
848 if self.input_mode == 'line' and self.processing_cell_magic:
849 return self._line_mode_cell_append(lines)
959
850
851 # The rest of the processing is for 'normal' content, i.e. IPython
852 # source that we process through our transformations pipeline.
960 lines_list = lines.splitlines()
853 lines_list = lines.splitlines()
961
854
962 transforms = [transform_ipy_prompt, transform_classic_prompt,
855 transforms = [transform_ipy_prompt, transform_classic_prompt,
@@ -599,90 +599,6 b' def test_escaped_paren():'
599 tt.check_pairs(isp.transform_escaped, syntax['escaped_paren'])
599 tt.check_pairs(isp.transform_escaped, syntax['escaped_paren'])
600
600
601
601
602 def test_last_blank():
603 nt.assert_false(isp.last_blank(''))
604 nt.assert_false(isp.last_blank('abc'))
605 nt.assert_false(isp.last_blank('abc\n'))
606 nt.assert_false(isp.last_blank('abc\na'))
607
608 nt.assert_true(isp.last_blank('\n'))
609 nt.assert_true(isp.last_blank('\n '))
610 nt.assert_true(isp.last_blank('abc\n '))
611 nt.assert_true(isp.last_blank('abc\n\n'))
612 nt.assert_true(isp.last_blank('abc\nd\n\n'))
613 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
614 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
615
616
617 def test_last_two_blanks():
618 nt.assert_false(isp.last_two_blanks(''))
619 nt.assert_false(isp.last_two_blanks('abc'))
620 nt.assert_false(isp.last_two_blanks('abc\n'))
621 nt.assert_false(isp.last_two_blanks('abc\n\na'))
622 nt.assert_false(isp.last_two_blanks('abc\n \n'))
623 nt.assert_false(isp.last_two_blanks('abc\n\n'))
624
625 nt.assert_true(isp.last_two_blanks('\n\n'))
626 nt.assert_true(isp.last_two_blanks('\n\n '))
627 nt.assert_true(isp.last_two_blanks('\n \n'))
628 nt.assert_true(isp.last_two_blanks('abc\n\n '))
629 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
630 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
631 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
632 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
633 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
634 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
635
636
637 def test_cell_magics_line_mode():
638
639 cell = """\
640 %%cellm line
641 body
642 """
643 sp = isp.IPythonInputSplitter(input_mode='line')
644 sp.push(cell)
645 nt.assert_equal(sp.cell_magic_parts, ['body\n'])
646 out = sp.source
647 ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n"
648 nt.assert_equal(out, ref)
649
650 sp.reset()
651
652 sp.push('%%cellm line2\n')
653 nt.assert_true(sp.push_accepts_more()) #1
654 sp.push('\n')
655 nt.assert_true(sp.push_accepts_more()) #2
656 sp.push('\n')
657 nt.assert_false(sp.push_accepts_more()) #3
658
659
660 def test_cell_magics_cell_mode():
661
662 cell = """\
663 %%cellm line
664 body
665 """
666 sp = isp.IPythonInputSplitter(input_mode='cell')
667 sp.push(cell)
668 nt.assert_equal(sp.cell_magic_parts, ['body\n'])
669 out = sp.source
670 ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n"
671 nt.assert_equal(out, ref)
672
673 sp.reset()
674
675 src = '%%cellm line2\n'
676 sp.push(src)
677 nt.assert_true(sp.push_accepts_more()) #1
678 src += '\n'
679 sp.push(src)
680 nt.assert_true(sp.push_accepts_more()) #2
681 src += '\n'
682 sp.push(src)
683 nt.assert_false(sp.push_accepts_more()) #3
684
685
686 class IPythonInputTestCase(InputSplitterTestCase):
602 class IPythonInputTestCase(InputSplitterTestCase):
687 """By just creating a new class whose .isp is a different instance, we
603 """By just creating a new class whose .isp is a different instance, we
688 re-run the same test battery on the new input splitter.
604 re-run the same test battery on the new input splitter.
@@ -792,3 +708,96 b" if __name__ == '__main__':"
792 print 'Raw source was:\n', raw
708 print 'Raw source was:\n', raw
793 except EOFError:
709 except EOFError:
794 print 'Bye'
710 print 'Bye'
711
712 # Tests for cell magics support
713
714 def test_last_blank():
715 nt.assert_false(isp.last_blank(''))
716 nt.assert_false(isp.last_blank('abc'))
717 nt.assert_false(isp.last_blank('abc\n'))
718 nt.assert_false(isp.last_blank('abc\na'))
719
720 nt.assert_true(isp.last_blank('\n'))
721 nt.assert_true(isp.last_blank('\n '))
722 nt.assert_true(isp.last_blank('abc\n '))
723 nt.assert_true(isp.last_blank('abc\n\n'))
724 nt.assert_true(isp.last_blank('abc\nd\n\n'))
725 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
726 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
727
728
729 def test_last_two_blanks():
730 nt.assert_false(isp.last_two_blanks(''))
731 nt.assert_false(isp.last_two_blanks('abc'))
732 nt.assert_false(isp.last_two_blanks('abc\n'))
733 nt.assert_false(isp.last_two_blanks('abc\n\na'))
734 nt.assert_false(isp.last_two_blanks('abc\n \n'))
735 nt.assert_false(isp.last_two_blanks('abc\n\n'))
736
737 nt.assert_true(isp.last_two_blanks('\n\n'))
738 nt.assert_true(isp.last_two_blanks('\n\n '))
739 nt.assert_true(isp.last_two_blanks('\n \n'))
740 nt.assert_true(isp.last_two_blanks('abc\n\n '))
741 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
742 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
743 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
744 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
745 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
746 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
747
748
749 class CellModeCellMagics(unittest.TestCase):
750 sp = isp.IPythonInputSplitter(input_mode='cell')
751
752 def test_whole_cell(self):
753 src = "%%cellm line\nbody\n"
754 sp = self.sp
755 sp.push(src)
756 nt.assert_equal(sp.cell_magic_parts, ['body\n'])
757 out = sp.source
758 ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n"
759 nt.assert_equal(out, ref)
760
761 def test_incremental(self):
762 sp = self.sp
763 src = '%%cellm line2\n'
764 sp.push(src)
765 nt.assert_true(sp.push_accepts_more()) #1
766 src += '\n'
767 sp.push(src)
768 # Note: if we ever change the logic to allow full blank lines (see
769 # _handle_cell_magic), then the following test should change to true
770 nt.assert_false(sp.push_accepts_more()) #2
771 # By now, even with full blanks allowed, a second blank should signal
772 # the end. For now this test is only a redundancy safety, but don't
773 # delete it in case we change our mind and the previous one goes to
774 # true.
775 src += '\n'
776 sp.push(src)
777 nt.assert_false(sp.push_accepts_more()) #3
778
779 def tearDown(self):
780 self.sp.reset()
781
782
783 class LineModeCellMagics(unittest.TestCase):
784 sp = isp.IPythonInputSplitter(input_mode='line')
785
786 def test_whole_cell(self):
787 src = "%%cellm line\nbody\n"
788 sp = self.sp
789 sp.push(src)
790 nt.assert_equal(sp.cell_magic_parts, ['body\n'])
791 out = sp.source
792 ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n"
793 nt.assert_equal(out, ref)
794
795 def test_incremental(self):
796 sp = self.sp
797 sp.push('%%cellm line2\n')
798 nt.assert_true(sp.push_accepts_more()) #1
799 sp.push('\n')
800 nt.assert_false(sp.push_accepts_more()) #2
801
802 def tearDown(self):
803 self.sp.reset()
General Comments 0
You need to be logged in to leave comments. Login now