##// END OF EJS Templates
Final cleanups responding to Brian's code review....
Fernando Perez -
Show More
@@ -5,9 +5,30 b' input from either interactive, line-by-line environments or block-based ones,'
5 into standalone blocks that can be executed by Python as 'single' statements
5 into standalone blocks that can be executed by Python as 'single' statements
6 (thus triggering sys.displayhook).
6 (thus triggering sys.displayhook).
7
7
8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 with full support for the extended IPython syntax (magics, system calls, etc).
10
8 For more details, see the class docstring below.
11 For more details, see the class docstring below.
9
12
13 ToDo
14 ----
15
16 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
17 at least just attributes of a class so not really very exposed.
18
19 - Think about the best way to support dynamic things: automagic, autocall,
20 macros, etc.
21
22 - Think of a better heuristic for the application of the transforms in
23 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
24 track indentation change events (indent, dedent, nothing) and apply them only
25 if the indentation went up, but not otherwise.
26
27 - Think of the cleanest way for supporting user-specified transformations (the
28 user prefilters we had before).
29
10 Authors
30 Authors
31 -------
11
32
12 * Fernando Perez
33 * Fernando Perez
13 * Brian Granger
34 * Brian Granger
@@ -557,9 +578,6 b' class LineInfo(object):'
557 The initial esc character (or characters, for double-char escapes like
578 The initial esc character (or characters, for double-char escapes like
558 '??' or '!!').
579 '??' or '!!').
559
580
560 pre_char
561 The escape character(s) in esc or the empty string if there isn't one.
562
563 fpart
581 fpart
564 The 'function part', which is basically the maximal initial sequence
582 The 'function part', which is basically the maximal initial sequence
565 of valid python identifiers and the '.' character. This is what is
583 of valid python identifiers and the '.' character. This is what is
@@ -648,48 +666,37 b' def transform_ipy_prompt(line):'
648 return line
666 return line
649
667
650
668
651 def transform_unescaped(line):
669 class EscapedTransformer(object):
652 """Transform lines that are explicitly escaped out.
670 """Class to transform lines that are explicitly escaped out."""
653
671
654 This calls to the above transform_* functions for the actual line
672 def __init__(self):
655 translations.
673 tr = { ESC_SHELL : self.tr_system,
656
674 ESC_SH_CAP : self.tr_system2,
657 Parameters
675 ESC_HELP : self.tr_help,
658 ----------
676 ESC_HELP2 : self.tr_help,
659 line : str
677 ESC_MAGIC : self.tr_magic,
660 A single line of input to be transformed.
678 ESC_QUOTE : self.tr_quote,
661
679 ESC_QUOTE2 : self.tr_quote2,
662 Returns
680 ESC_PAREN : self.tr_paren }
663 -------
681 self.tr = tr
664 new_line : str
665 Transformed line, which may be identical to the original."""
666
667 if not line or line.isspace():
668 return line
669
670 new_line = line
671 for f in [transform_assign_system, transform_assign_magic,
672 transform_classic_prompt, transform_ipy_prompt ] :
673 new_line = f(new_line)
674 return new_line
675
682
676 # Support for syntax transformations that use explicit escapes typed by the
683 # Support for syntax transformations that use explicit escapes typed by the
677 # user at the beginning of a line
684 # user at the beginning of a line
678
685 @staticmethod
679 def tr_system(line_info):
686 def tr_system(line_info):
680 "Translate lines escaped with: !"
687 "Translate lines escaped with: !"
681 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
688 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
682 return '%sget_ipython().system(%s)' % (line_info.lspace,
689 return '%sget_ipython().system(%s)' % (line_info.lspace,
683 make_quoted_expr(cmd))
690 make_quoted_expr(cmd))
684
691
685
692 @staticmethod
686 def tr_system2(line_info):
693 def tr_system2(line_info):
687 "Translate lines escaped with: !!"
694 "Translate lines escaped with: !!"
688 cmd = line_info.line.lstrip()[2:]
695 cmd = line_info.line.lstrip()[2:]
689 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
696 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
690 make_quoted_expr(cmd))
697 make_quoted_expr(cmd))
691
698
692
699 @staticmethod
693 def tr_help(line_info):
700 def tr_help(line_info):
694 "Translate lines escaped with: ?/??"
701 "Translate lines escaped with: ?/??"
695 # A naked help line should just fire the intro help screen
702 # A naked help line should just fire the intro help screen
@@ -715,7 +722,7 b' def tr_help(line_info):'
715 return tpl % (line_info.lspace, pinfo,
722 return tpl % (line_info.lspace, pinfo,
716 ' '.join([line_info.fpart, line_info.rest]).strip())
723 ' '.join([line_info.fpart, line_info.rest]).strip())
717
724
718
725 @staticmethod
719 def tr_magic(line_info):
726 def tr_magic(line_info):
720 "Translate lines escaped with: %"
727 "Translate lines escaped with: %"
721 tpl = '%sget_ipython().magic(%s)'
728 tpl = '%sget_ipython().magic(%s)'
@@ -723,38 +730,29 b' def tr_magic(line_info):'
723 line_info.rest])).strip()
730 line_info.rest])).strip()
724 return tpl % (line_info.lspace, cmd)
731 return tpl % (line_info.lspace, cmd)
725
732
726
733 @staticmethod
727 def tr_quote(line_info):
734 def tr_quote(line_info):
728 "Translate lines escaped with: ,"
735 "Translate lines escaped with: ,"
729 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
736 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
730 '", "'.join(line_info.rest.split()) )
737 '", "'.join(line_info.rest.split()) )
731
738
732
739 @staticmethod
733 def tr_quote2(line_info):
740 def tr_quote2(line_info):
734 "Translate lines escaped with: ;"
741 "Translate lines escaped with: ;"
735 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
742 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
736 line_info.rest)
743 line_info.rest)
737
744
738
745 @staticmethod
739 def tr_paren(line_info):
746 def tr_paren(line_info):
740 "Translate lines escaped with: /"
747 "Translate lines escaped with: /"
741 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
748 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
742 ", ".join(line_info.rest.split()))
749 ", ".join(line_info.rest.split()))
743
750
751 def __call__(self, line):
752 """Class to transform lines that are explicitly escaped out.
744
753
745 def transform_escaped(line):
754 This calls the above tr_* static methods for the actual line
746 """Transform lines that are explicitly escaped out.
755 translations."""
747
748 This calls to the above tr_* functions for the actual line translations."""
749
750 tr = { ESC_SHELL : tr_system,
751 ESC_SH_CAP : tr_system2,
752 ESC_HELP : tr_help,
753 ESC_HELP2 : tr_help,
754 ESC_MAGIC : tr_magic,
755 ESC_QUOTE : tr_quote,
756 ESC_QUOTE2 : tr_quote2,
757 ESC_PAREN : tr_paren }
758
756
759 # Empty lines just get returned unmodified
757 # Empty lines just get returned unmodified
760 if not line or line.isspace():
758 if not line or line.isspace():
@@ -765,14 +763,19 b' def transform_escaped(line):'
765
763
766 # If the escape is not at the start, only '?' needs to be special-cased.
764 # If the escape is not at the start, only '?' needs to be special-cased.
767 # All other escapes are only valid at the start
765 # All other escapes are only valid at the start
768 if not line_info.esc in tr:
766 if not line_info.esc in self.tr:
769 if line.endswith(ESC_HELP):
767 if line.endswith(ESC_HELP):
770 return tr_help(line_info)
768 return self.tr_help(line_info)
771 else:
769 else:
772 # If we don't recognize the escape, don't modify the line
770 # If we don't recognize the escape, don't modify the line
773 return line
771 return line
774
772
775 return tr[line_info.esc](line_info)
773 return self.tr[line_info.esc](line_info)
774
775 # A function-looking object to be used by the rest of the code. The purpose of
776 # the class in this case is to organize related functionality, more than to
777 # manage state.
778 transform_escaped = EscapedTransformer()
776
779
777
780
778 class IPythonInputSplitter(InputSplitter):
781 class IPythonInputSplitter(InputSplitter):
@@ -781,18 +784,34 b' class IPythonInputSplitter(InputSplitter):'
781 def push(self, lines):
784 def push(self, lines):
782 """Push one or more lines of IPython input.
785 """Push one or more lines of IPython input.
783 """
786 """
787 if not lines:
788 return super(IPythonInputSplitter, self).push(lines)
789
790 lines_list = lines.splitlines()
791
792 transforms = [transform_escaped, transform_assign_system,
793 transform_assign_magic, transform_ipy_prompt,
794 transform_classic_prompt]
795
796 # Transform logic
797 #
784 # We only apply the line transformers to the input if we have either no
798 # We only apply the line transformers to the input if we have either no
785 # input yet, or complete input. This prevents the accidental
799 # input yet, or complete input, or if the last line of the buffer ends
800 # with ':' (opening an indented block). This prevents the accidental
786 # transformation of escapes inside multiline expressions like
801 # transformation of escapes inside multiline expressions like
787 # triple-quoted strings or parenthesized expressions.
802 # triple-quoted strings or parenthesized expressions.
788 lines_list = lines.splitlines()
803 #
789 if self._is_complete or not self._buffer:
804 # The last heuristic, while ugly, ensures that the first line of an
805 # indented block is correctly transformed.
806 #
807 # FIXME: try to find a cleaner approach for this last bit.
790
808
791 new_list = map(transform_escaped, lines_list)
809 for line in lines_list:
792 else:
810 if self._is_complete or not self._buffer or \
793 new_list = lines_list
811 (self._buffer and self._buffer[-1].rstrip().endswith(':')):
812 for f in transforms:
813 line = f(line)
794
814
795 # Now apply the unescaped transformations to each input line
815 out = super(IPythonInputSplitter, self).push(line)
796 new_list = map(transform_unescaped, new_list)
816
797 newlines = '\n'.join(new_list)
817 return out
798 return super(IPythonInputSplitter, self).push(newlines)
@@ -594,15 +594,13 b" if __name__ == '__main__':"
594 # development.
594 # development.
595 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
595 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
596
596
597 # configure here the syntax to use, prompt and whether to autoindent
597 #isp, start_prompt = InputSplitter(), '>>> '
598 #isp, start_prompt = InputSplitter(), '>>> '
598 isp, start_prompt = IPythonInputSplitter(), 'In> '
599 isp, start_prompt = IPythonInputSplitter(), 'In> '
599
600
600 autoindent = True
601 autoindent = True
601 #autoindent = False
602 #autoindent = False
602
603
603 # In practice, this input loop would be wrapped in an outside loop to read
604 # input indefinitely, until some exit/quit command was issued. Here we
605 # only illustrate the basic inner loop.
606 try:
604 try:
607 while True:
605 while True:
608 prompt = start_prompt
606 prompt = start_prompt
@@ -618,6 +616,6 b" if __name__ == '__main__':"
618 # Here we just return input so we can use it in a test suite, but a
616 # Here we just return input so we can use it in a test suite, but a
619 # real interpreter would instead send it for execution somewhere.
617 # real interpreter would instead send it for execution somewhere.
620 src = isp.source_reset()
618 src = isp.source_reset()
621 print 'Input source was:\n', src # dbg
619 print 'Input source was:\n', src
622 except EOFError:
620 except EOFError:
623 print 'Bye'
621 print 'Bye'
General Comments 0
You need to be logged in to leave comments. Login now