##// 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
@@ -513,11 +534,11 b' def split_user_input(line):'
513 534 else:
514 535 # print "match failed for line '%s'" % line
515 536 try:
516 fpart, rest = line.split(None,1)
537 fpart, rest = line.split(None, 1)
517 538 except ValueError:
518 539 # print "split failed for line '%s'" % line
519 540 fpart, rest = line,''
520 lspace = re.match('^(\s*)(.*)',line).groups()[0]
541 lspace = re.match('^(\s*)(.*)', line).groups()[0]
521 542 esc = ''
522 543
523 544 # fpart has to be a valid python identifier, so it better be only pure
@@ -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,131 +666,116 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.
653
654 This calls to the above transform_* functions for the actual line
655 translations.
669 class EscapedTransformer(object):
670 """Class to transform lines that are explicitly escaped out."""
656 671
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."""
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
682
683 # Support for syntax transformations that use explicit escapes typed by the
684 # user at the beginning of a line
685 @staticmethod
686 def tr_system(line_info):
687 "Translate lines escaped with: !"
688 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
689 return '%sget_ipython().system(%s)' % (line_info.lspace,
690 make_quoted_expr(cmd))
691
692 @staticmethod
693 def tr_system2(line_info):
694 "Translate lines escaped with: !!"
695 cmd = line_info.line.lstrip()[2:]
696 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
697 make_quoted_expr(cmd))
698
699 @staticmethod
700 def tr_help(line_info):
701 "Translate lines escaped with: ?/??"
702 # A naked help line should just fire the intro help screen
703 if not line_info.line[1:]:
704 return 'get_ipython().show_usage()'
705
706 # There may be one or two '?' at the end, move them to the front so that
707 # the rest of the logic can assume escapes are at the start
708 line = line_info.line
709 if line.endswith('?'):
710 line = line[-1] + line[:-1]
711 if line.endswith('?'):
712 line = line[-1] + line[:-1]
713 line_info = LineInfo(line)
714
715 # From here on, simply choose which level of detail to get.
716 if line_info.esc == '?':
717 pinfo = 'pinfo'
718 elif line_info.esc == '??':
719 pinfo = 'pinfo2'
720
721 tpl = '%sget_ipython().magic("%s %s")'
722 return tpl % (line_info.lspace, pinfo,
723 ' '.join([line_info.fpart, line_info.rest]).strip())
724
725 @staticmethod
726 def tr_magic(line_info):
727 "Translate lines escaped with: %"
728 tpl = '%sget_ipython().magic(%s)'
729 cmd = make_quoted_expr(' '.join([line_info.fpart,
730 line_info.rest])).strip()
731 return tpl % (line_info.lspace, cmd)
732
733 @staticmethod
734 def tr_quote(line_info):
735 "Translate lines escaped with: ,"
736 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
737 '", "'.join(line_info.rest.split()) )
738
739 @staticmethod
740 def tr_quote2(line_info):
741 "Translate lines escaped with: ;"
742 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
743 line_info.rest)
744
745 @staticmethod
746 def tr_paren(line_info):
747 "Translate lines escaped with: /"
748 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
749 ", ".join(line_info.rest.split()))
750
751 def __call__(self, line):
752 """Class to transform lines that are explicitly escaped out.
753
754 This calls the above tr_* static methods for the actual line
755 translations."""
756
757 # Empty lines just get returned unmodified
758 if not line or line.isspace():
759 return line
666 760
667 if not line or line.isspace():
668 return line
761 # Get line endpoints, where the escapes can be
762 line_info = LineInfo(line)
669 763
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
676 # Support for syntax transformations that use explicit escapes typed by the
677 # user at the beginning of a line
678
679 def tr_system(line_info):
680 "Translate lines escaped with: !"
681 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
682 return '%sget_ipython().system(%s)' % (line_info.lspace,
683 make_quoted_expr(cmd))
684
685
686 def tr_system2(line_info):
687 "Translate lines escaped with: !!"
688 cmd = line_info.line.lstrip()[2:]
689 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
690 make_quoted_expr(cmd))
691
692
693 def tr_help(line_info):
694 "Translate lines escaped with: ?/??"
695 # A naked help line should just fire the intro help screen
696 if not line_info.line[1:]:
697 return 'get_ipython().show_usage()'
698
699 # There may be one or two '?' at the end, move them to the front so that
700 # the rest of the logic can assume escapes are at the start
701 line = line_info.line
702 if line.endswith('?'):
703 line = line[-1] + line[:-1]
704 if line.endswith('?'):
705 line = line[-1] + line[:-1]
706 line_info = LineInfo(line)
707
708 # From here on, simply choose which level of detail to get.
709 if line_info.esc == '?':
710 pinfo = 'pinfo'
711 elif line_info.esc == '??':
712 pinfo = 'pinfo2'
713
714 tpl = '%sget_ipython().magic("%s %s")'
715 return tpl % (line_info.lspace, pinfo,
716 ' '.join([line_info.fpart, line_info.rest]).strip())
717
718
719 def tr_magic(line_info):
720 "Translate lines escaped with: %"
721 tpl = '%sget_ipython().magic(%s)'
722 cmd = make_quoted_expr(' '.join([line_info.fpart,
723 line_info.rest])).strip()
724 return tpl % (line_info.lspace, cmd)
725
726
727 def tr_quote(line_info):
728 "Translate lines escaped with: ,"
729 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
730 '", "'.join(line_info.rest.split()) )
731
732
733 def tr_quote2(line_info):
734 "Translate lines escaped with: ;"
735 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
736 line_info.rest)
737
738
739 def tr_paren(line_info):
740 "Translate lines escaped with: /"
741 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
742 ", ".join(line_info.rest.split()))
743
744
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 }
758
759 # Empty lines just get returned unmodified
760 if not line or line.isspace():
761 return line
764 # If the escape is not at the start, only '?' needs to be special-cased.
765 # All other escapes are only valid at the start
766 if not line_info.esc in self.tr:
767 if line.endswith(ESC_HELP):
768 return self.tr_help(line_info)
769 else:
770 # If we don't recognize the escape, don't modify the line
771 return line
762 772
763 # Get line endpoints, where the escapes can be
764 line_info = LineInfo(line)
773 return self.tr[line_info.esc](line_info)
765 774
766 # If the escape is not at the start, only '?' needs to be special-cased.
767 # All other escapes are only valid at the start
768 if not line_info.esc in tr:
769 if line.endswith(ESC_HELP):
770 return tr_help(line_info)
771 else:
772 # If we don't recognize the escape, don't modify the line
773 return line
774
775 return tr[line_info.esc](line_info)
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.
808
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)
790 814
791 new_list = map(transform_escaped, lines_list)
792 else:
793 new_list = lines_list
815 out = super(IPythonInputSplitter, self).push(line)
794 816
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)
817 return out
@@ -593,16 +593,14 b" if __name__ == '__main__':"
593 593 # picked up by any test suite. Useful mostly for illustration and during
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