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