Show More
@@ -142,6 +142,21 b' def num_ini_spaces(s):' | |||||
142 | return 0 |
|
142 | return 0 | |
143 |
|
143 | |||
144 |
|
144 | |||
|
145 | def last_blank(src): | |||
|
146 | """Determine if the input source ends in a blank. | |||
|
147 | ||||
|
148 | A blank is either a newline or a line consisting of whitespace. | |||
|
149 | ||||
|
150 | Parameters | |||
|
151 | ---------- | |||
|
152 | src : string | |||
|
153 | A single or multiline string. | |||
|
154 | """ | |||
|
155 | if not src: return False | |||
|
156 | ll = src.splitlines()[-1] | |||
|
157 | return (ll == '') or ll.isspace() | |||
|
158 | ||||
|
159 | ||||
145 | def remove_comments(src): |
|
160 | def remove_comments(src): | |
146 | """Remove all comments from input source. |
|
161 | """Remove all comments from input source. | |
147 |
|
162 | |||
@@ -685,7 +700,9 b' class IPythonInputSplitter(InputSplitter):' | |||||
685 | # String with raw, untransformed input. |
|
700 | # String with raw, untransformed input. | |
686 | source_raw = '' |
|
701 | source_raw = '' | |
687 |
|
702 | |||
688 |
cell_magic_ |
|
703 | cell_magic_parts = [] | |
|
704 | ||||
|
705 | cell_magic_mode = False | |||
689 |
|
706 | |||
690 | # Private attributes |
|
707 | # Private attributes | |
691 |
|
708 | |||
@@ -695,13 +712,15 b' class IPythonInputSplitter(InputSplitter):' | |||||
695 | def __init__(self, input_mode=None): |
|
712 | def __init__(self, input_mode=None): | |
696 | super(IPythonInputSplitter, self).__init__(input_mode) |
|
713 | super(IPythonInputSplitter, self).__init__(input_mode) | |
697 | self._buffer_raw = [] |
|
714 | self._buffer_raw = [] | |
|
715 | self._validate = True | |||
698 |
|
716 | |||
699 | def reset(self): |
|
717 | def reset(self): | |
700 | """Reset the input buffer and associated state.""" |
|
718 | """Reset the input buffer and associated state.""" | |
701 | super(IPythonInputSplitter, self).reset() |
|
719 | super(IPythonInputSplitter, self).reset() | |
702 | self._buffer_raw[:] = [] |
|
720 | self._buffer_raw[:] = [] | |
703 | self.source_raw = '' |
|
721 | self.source_raw = '' | |
704 |
self.cell_magic_ |
|
722 | self.cell_magic_parts = [] | |
|
723 | self.cell_magic_mode = False | |||
705 |
|
724 | |||
706 | def source_raw_reset(self): |
|
725 | def source_raw_reset(self): | |
707 | """Return input and raw source and perform a full reset. |
|
726 | """Return input and raw source and perform a full reset. | |
@@ -711,6 +730,96 b' class IPythonInputSplitter(InputSplitter):' | |||||
711 | self.reset() |
|
730 | self.reset() | |
712 | return out, out_r |
|
731 | return out, out_r | |
713 |
|
732 | |||
|
733 | def push_accepts_more(self): | |||
|
734 | if self.cell_magic_mode: | |||
|
735 | return not self._is_complete | |||
|
736 | else: | |||
|
737 | return super(IPythonInputSplitter, self).push_accepts_more() | |||
|
738 | ||||
|
739 | def _push_line_mode(self, lines): | |||
|
740 | """Push in line mode. | |||
|
741 | ||||
|
742 | This means that we only get individual 'lines' with each call, though | |||
|
743 | in practice each input may be multiline. But this is in contrast to | |||
|
744 | cell mode, which feeds the entirety of the cell from the start with | |||
|
745 | each call. | |||
|
746 | """ | |||
|
747 | # cell magic support | |||
|
748 | #print('#'*10) | |||
|
749 | #print(lines+'\n---') # dbg | |||
|
750 | #print (repr(lines)+'\n+++') | |||
|
751 | #print('raw', self._buffer_raw, 'validate', self.cell_magic_mode) | |||
|
752 | # Only trigger this block if we're at a 'fresh' pumping start. | |||
|
753 | if lines.startswith('%%') and (not self.cell_magic_mode) and \ | |||
|
754 | not self._buffer_raw: | |||
|
755 | # Cell magics bypass all further transformations | |||
|
756 | self.cell_magic_mode = True | |||
|
757 | first, _, body = lines.partition('\n') | |||
|
758 | magic_name, _, line = first.partition(' ') | |||
|
759 | magic_name = magic_name.lstrip(ESC_MAGIC) | |||
|
760 | # We store the body of the cell and create a call to a method that | |||
|
761 | # will use this stored value. This is ugly, but it's a first cut to | |||
|
762 | # get it all working, as right now changing the return API of our | |||
|
763 | # methods would require major refactoring. | |||
|
764 | self.cell_magic_parts = [body] | |||
|
765 | tpl = 'get_ipython()._cell_magic(%r, %r)' | |||
|
766 | tlines = tpl % (magic_name, line) | |||
|
767 | self._store(tlines) | |||
|
768 | self._store(lines, self._buffer_raw, 'source_raw') | |||
|
769 | self._is_complete = False | |||
|
770 | return False | |||
|
771 | ||||
|
772 | if self.cell_magic_mode: | |||
|
773 | # Find out if the last stored block has a whitespace line as its | |||
|
774 | # last line and also this line is whitespace, case in which we're | |||
|
775 | # done (two contiguous blank lines signal termination). Note that | |||
|
776 | # the storage logic *enforces* that every stored block is | |||
|
777 | # newline-terminated, so we grab everything but the last character | |||
|
778 | # so we can have the body of the block alone. | |||
|
779 | last_block = self.cell_magic_parts[-1] | |||
|
780 | self._is_complete = last_blank(last_block) and lines.isspace() | |||
|
781 | # Only store the raw input. For lines beyond the first one, we | |||
|
782 | # only store them for history purposes, and for execution we want | |||
|
783 | # the caller to only receive the _cell_magic() call. | |||
|
784 | self._store(lines, self._buffer_raw, 'source_raw') | |||
|
785 | self.cell_magic_parts.append(lines) | |||
|
786 | return self._is_complete | |||
|
787 | ||||
|
788 | lines_list = lines.splitlines() | |||
|
789 | ||||
|
790 | transforms = [transform_ipy_prompt, transform_classic_prompt, | |||
|
791 | transform_help_end, transform_escaped, | |||
|
792 | transform_assign_system, transform_assign_magic] | |||
|
793 | ||||
|
794 | # Transform logic | |||
|
795 | # | |||
|
796 | # We only apply the line transformers to the input if we have either no | |||
|
797 | # input yet, or complete input, or if the last line of the buffer ends | |||
|
798 | # with ':' (opening an indented block). This prevents the accidental | |||
|
799 | # transformation of escapes inside multiline expressions like | |||
|
800 | # triple-quoted strings or parenthesized expressions. | |||
|
801 | # | |||
|
802 | # The last heuristic, while ugly, ensures that the first line of an | |||
|
803 | # indented block is correctly transformed. | |||
|
804 | # | |||
|
805 | # FIXME: try to find a cleaner approach for this last bit. | |||
|
806 | ||||
|
807 | # Store raw source before applying any transformations to it. Note | |||
|
808 | # that this must be done *after* the reset() call that would otherwise | |||
|
809 | # flush the buffer. | |||
|
810 | self._store(lines, self._buffer_raw, 'source_raw') | |||
|
811 | ||||
|
812 | push = super(IPythonInputSplitter, self).push | |||
|
813 | buf = self._buffer | |||
|
814 | for line in lines_list: | |||
|
815 | if self._is_complete or not buf or \ | |||
|
816 | (buf and buf[-1].rstrip().endswith((':', ','))): | |||
|
817 | for f in transforms: | |||
|
818 | line = f(line) | |||
|
819 | ||||
|
820 | out = push(line) | |||
|
821 | return out | |||
|
822 | ||||
714 | def push(self, lines): |
|
823 | def push(self, lines): | |
715 | """Push one or more lines of IPython input. |
|
824 | """Push one or more lines of IPython input. | |
716 |
|
825 | |||
@@ -734,25 +843,19 b' class IPythonInputSplitter(InputSplitter):' | |||||
734 | this value is also stored as a private attribute (_is_complete), so it |
|
843 | this value is also stored as a private attribute (_is_complete), so it | |
735 | can be queried at any time. |
|
844 | can be queried at any time. | |
736 | """ |
|
845 | """ | |
|
846 | print('mode:', self.input_mode) | |||
|
847 | print('lines:',repr(lines)) | |||
737 | if not lines: |
|
848 | if not lines: | |
738 | return super(IPythonInputSplitter, self).push(lines) |
|
849 | return super(IPythonInputSplitter, self).push(lines) | |
739 |
|
850 | |||
740 | # We must ensure all input is pure unicode |
|
851 | # We must ensure all input is pure unicode | |
741 | lines = cast_unicode(lines, self.encoding) |
|
852 | lines = cast_unicode(lines, self.encoding) | |
742 |
|
853 | |||
743 | # cell magic support |
|
854 | if self.input_mode == 'line': | |
744 | #print('IM:', self.input_mode,'\n'+lines); print('---') # dbg |
|
855 | return self._push_line_mode(lines) | |
745 | #if self.input_mode == 'cell' and lines.startswith('%%'): |
|
856 | ||
746 | if lines.startswith('%%'): |
|
857 | ## else: | |
747 | # Cell magics bypass all further transformations |
|
858 | ## return self._push_cell_mode(lines) | |
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 |
|
859 | |||
757 | lines_list = lines.splitlines() |
|
860 | lines_list = lines.splitlines() | |
758 |
|
861 | |||
@@ -796,8 +899,7 b' class IPythonInputSplitter(InputSplitter):' | |||||
796 | buf = self._buffer |
|
899 | buf = self._buffer | |
797 | for line in lines_list: |
|
900 | for line in lines_list: | |
798 | if self._is_complete or not buf or \ |
|
901 | if self._is_complete or not buf or \ | |
799 |
(buf and |
|
902 | (buf and buf[-1].rstrip().endswith((':', ','))): | |
800 | buf[-1].rstrip().endswith(',')) ): |
|
|||
801 | for f in transforms: |
|
903 | for f in transforms: | |
802 | line = f(line) |
|
904 | line = f(line) | |
803 |
|
905 |
@@ -2027,7 +2027,12 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 |
e |
|
2030 | em = "Line magic function `%%%s` not found" % magic_name | |
|
2031 | cm = self.find_cell_magic(magic_name) | |||
|
2032 | if cm is not None: | |||
|
2033 | em += (' (Did you by chance mean the cell magic `%%%%%s` ' | |||
|
2034 | 'instead?).') | |||
|
2035 | error() | |||
2031 | else: |
|
2036 | else: | |
2032 | # Note: this is the distance in the stack to the user's frame. |
|
2037 | # 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 |
|
2038 | # This will need to be updated if the internal calling logic gets | |
@@ -2469,13 +2474,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
2469 | self.showtraceback() |
|
2474 | self.showtraceback() | |
2470 | warn('Unknown failure executing module: <%s>' % mod_name) |
|
2475 | warn('Unknown failure executing module: <%s>' % mod_name) | |
2471 |
|
2476 | |||
2472 | def call_cell_magic(self, raw_cell, store_history=False): |
|
|||
2473 | line, _, cell = raw_cell.partition(os.linesep) |
|
|||
2474 | magic_name, _, line = line.partition(' ') |
|
|||
2475 | magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) |
|
|||
2476 | return self.cell_magic(magic_name, line, cell) |
|
|||
2477 |
|
||||
2478 | def _cell_magic(self, magic_name, line): |
|
2477 | def _cell_magic(self, magic_name, line): | |
|
2478 | """Special method to call a cell magic with the data stored in self. | |||
|
2479 | """ | |||
2479 | cell = self._current_cell_magic_body |
|
2480 | cell = self._current_cell_magic_body | |
2480 | self._current_cell_magic_body = None |
|
2481 | self._current_cell_magic_body = None | |
2481 | return self.cell_magic(magic_name, line, cell) |
|
2482 | return self.cell_magic(magic_name, line, cell) | |
@@ -2507,8 +2508,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
2507 | # ugly, we need to do something cleaner later... Now the logic is |
|
2508 | # 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 | # simply that the input_splitter remembers if there was a cell magic, | |
2509 | # and in that case we grab the cell body. |
|
2510 | # and in that case we grab the cell body. | |
2510 |
if self.input_splitter.cell_magic_ |
|
2511 | if self.input_splitter.cell_magic_parts: | |
2511 |
self._current_cell_magic_body = |
|
2512 | self._current_cell_magic_body = \ | |
|
2513 | ''.join(self.input_splitter.cell_magic_parts) | |||
2512 | cell = self.input_splitter.source_reset() |
|
2514 | cell = self.input_splitter.source_reset() | |
2513 |
|
2515 | |||
2514 | with self.builtin_trap: |
|
2516 | with self.builtin_trap: |
@@ -599,6 +599,41 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 | nt.assert_true(isp.last_blank('\n')) | |||
|
608 | nt.assert_true(isp.last_blank('\n ')) | |||
|
609 | nt.assert_true(isp.last_blank('abc\n ')) | |||
|
610 | nt.assert_true(isp.last_blank('abc\n\n')) | |||
|
611 | ||||
|
612 | ||||
|
613 | def test_cell_magics(): | |||
|
614 | from IPython.core import magic | |||
|
615 | ||||
|
616 | cell = """\ | |||
|
617 | %%cellm line | |||
|
618 | body | |||
|
619 | """ | |||
|
620 | sp = isp.IPythonInputSplitter(input_mode='line') | |||
|
621 | sp.push(cell) | |||
|
622 | nt.assert_equal(sp.cell_magic_parts, ['body\n']) | |||
|
623 | out = sp.source | |||
|
624 | ref = u"get_ipython()._cell_magic(u'cellm', u'line')\n" | |||
|
625 | nt.assert_equal(out, ref) | |||
|
626 | ||||
|
627 | sp.reset() | |||
|
628 | ||||
|
629 | sp.push('%%cellm line2\n') | |||
|
630 | nt.assert_true(sp.push_accepts_more()) #1 | |||
|
631 | sp.push('\n') | |||
|
632 | nt.assert_true(sp.push_accepts_more()) #2 | |||
|
633 | sp.push('\n') | |||
|
634 | nt.assert_false(sp.push_accepts_more()) #3 | |||
|
635 | ||||
|
636 | ||||
602 | class IPythonInputTestCase(InputSplitterTestCase): |
|
637 | class IPythonInputTestCase(InputSplitterTestCase): | |
603 | """By just creating a new class whose .isp is a different instance, we |
|
638 | """By just creating a new class whose .isp is a different instance, we | |
604 | re-run the same test battery on the new input splitter. |
|
639 | re-run the same test battery on the new input splitter. |
General Comments 0
You need to be logged in to leave comments.
Login now