##// 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 5 into standalone blocks that can be executed by Python as 'single' statements
6 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 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 30 Authors
31 -------
11 32
12 33 * Fernando Perez
13 34 * Brian Granger
@@ -557,9 +578,6 b' class LineInfo(object):'
557 578 The initial esc character (or characters, for double-char escapes like
558 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 581 fpart
564 582 The 'function part', which is basically the maximal initial sequence
565 583 of valid python identifiers and the '.' character. This is what is
@@ -648,48 +666,37 b' def transform_ipy_prompt(line):'
648 666 return line
649 667
650 668
651 def transform_unescaped(line):
652 """Transform lines that are explicitly escaped out.
669 class EscapedTransformer(object):
670 """Class to transform lines that are explicitly escaped out."""
653 671
654 This calls to the above transform_* functions for the actual line
655 translations.
656
657 Parameters
658 ----------
659 line : str
660 A single line of input to be transformed.
661
662 Returns
663 -------
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
672 def __init__(self):
673 tr = { ESC_SHELL : self.tr_system,
674 ESC_SH_CAP : self.tr_system2,
675 ESC_HELP : self.tr_help,
676 ESC_HELP2 : self.tr_help,
677 ESC_MAGIC : self.tr_magic,
678 ESC_QUOTE : self.tr_quote,
679 ESC_QUOTE2 : self.tr_quote2,
680 ESC_PAREN : self.tr_paren }
681 self.tr = tr
675 682
676 683 # Support for syntax transformations that use explicit escapes typed by the
677 684 # user at the beginning of a line
678
685 @staticmethod
679 686 def tr_system(line_info):
680 687 "Translate lines escaped with: !"
681 688 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
682 689 return '%sget_ipython().system(%s)' % (line_info.lspace,
683 690 make_quoted_expr(cmd))
684 691
685
692 @staticmethod
686 693 def tr_system2(line_info):
687 694 "Translate lines escaped with: !!"
688 695 cmd = line_info.line.lstrip()[2:]
689 696 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
690 697 make_quoted_expr(cmd))
691 698
692
699 @staticmethod
693 700 def tr_help(line_info):
694 701 "Translate lines escaped with: ?/??"
695 702 # A naked help line should just fire the intro help screen
@@ -715,7 +722,7 b' def tr_help(line_info):'
715 722 return tpl % (line_info.lspace, pinfo,
716 723 ' '.join([line_info.fpart, line_info.rest]).strip())
717 724
718
725 @staticmethod
719 726 def tr_magic(line_info):
720 727 "Translate lines escaped with: %"
721 728 tpl = '%sget_ipython().magic(%s)'
@@ -723,38 +730,29 b' def tr_magic(line_info):'
723 730 line_info.rest])).strip()
724 731 return tpl % (line_info.lspace, cmd)
725 732
726
733 @staticmethod
727 734 def tr_quote(line_info):
728 735 "Translate lines escaped with: ,"
729 736 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
730 737 '", "'.join(line_info.rest.split()) )
731 738
732
739 @staticmethod
733 740 def tr_quote2(line_info):
734 741 "Translate lines escaped with: ;"
735 742 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
736 743 line_info.rest)
737 744
738
745 @staticmethod
739 746 def tr_paren(line_info):
740 747 "Translate lines escaped with: /"
741 748 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
742 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):
746 """Transform lines that are explicitly escaped out.
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 }
754 This calls the above tr_* static methods for the actual line
755 translations."""
758 756
759 757 # Empty lines just get returned unmodified
760 758 if not line or line.isspace():
@@ -765,14 +763,19 b' def transform_escaped(line):'
765 763
766 764 # If the escape is not at the start, only '?' needs to be special-cased.
767 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 767 if line.endswith(ESC_HELP):
770 return tr_help(line_info)
768 return self.tr_help(line_info)
771 769 else:
772 770 # If we don't recognize the escape, don't modify the line
773 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 781 class IPythonInputSplitter(InputSplitter):
@@ -781,18 +784,34 b' class IPythonInputSplitter(InputSplitter):'
781 784 def push(self, lines):
782 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 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 801 # transformation of escapes inside multiline expressions like
787 802 # triple-quoted strings or parenthesized expressions.
788 lines_list = lines.splitlines()
789 if self._is_complete or not self._buffer:
803 #
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)
792 else:
793 new_list = lines_list
809 for line in lines_list:
810 if self._is_complete or not self._buffer or \
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
796 new_list = map(transform_unescaped, new_list)
797 newlines = '\n'.join(new_list)
798 return super(IPythonInputSplitter, self).push(newlines)
815 out = super(IPythonInputSplitter, self).push(line)
816
817 return out
@@ -594,15 +594,13 b" if __name__ == '__main__':"
594 594 # development.
595 595 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
596 596
597 # configure here the syntax to use, prompt and whether to autoindent
597 598 #isp, start_prompt = InputSplitter(), '>>> '
598 599 isp, start_prompt = IPythonInputSplitter(), 'In> '
599 600
600 601 autoindent = True
601 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 604 try:
607 605 while True:
608 606 prompt = start_prompt
@@ -618,6 +616,6 b" if __name__ == '__main__':"
618 616 # Here we just return input so we can use it in a test suite, but a
619 617 # real interpreter would instead send it for execution somewhere.
620 618 src = isp.source_reset()
621 print 'Input source was:\n', src # dbg
619 print 'Input source was:\n', src
622 620 except EOFError:
623 621 print 'Bye'
General Comments 0
You need to be logged in to leave comments. Login now