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 |
""" |
|
|
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 |
|
|
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 |
|
|
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