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 | |
@@ -513,11 +534,11 b' def split_user_input(line):' | |||||
513 | else: |
|
534 | else: | |
514 | # print "match failed for line '%s'" % line |
|
535 | # print "match failed for line '%s'" % line | |
515 | try: |
|
536 | try: | |
516 | fpart, rest = line.split(None,1) |
|
537 | fpart, rest = line.split(None, 1) | |
517 | except ValueError: |
|
538 | except ValueError: | |
518 | # print "split failed for line '%s'" % line |
|
539 | # print "split failed for line '%s'" % line | |
519 | fpart, rest = line,'' |
|
540 | fpart, rest = line,'' | |
520 | lspace = re.match('^(\s*)(.*)',line).groups()[0] |
|
541 | lspace = re.match('^(\s*)(.*)', line).groups()[0] | |
521 | esc = '' |
|
542 | esc = '' | |
522 |
|
543 | |||
523 | # fpart has to be a valid python identifier, so it better be only pure |
|
544 | # fpart has to be a valid python identifier, so it better be only pure | |
@@ -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,131 +666,116 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 |
|
||||
654 | This calls to the above transform_* functions for the actual line |
|
|||
655 | translations. |
|
|||
656 |
|
671 | |||
657 | Parameters |
|
672 | def __init__(self): | |
658 | ---------- |
|
673 | tr = { ESC_SHELL : self.tr_system, | |
659 | line : str |
|
674 | ESC_SH_CAP : self.tr_system2, | |
660 | A single line of input to be transformed. |
|
675 | ESC_HELP : self.tr_help, | |
661 |
|
676 | ESC_HELP2 : self.tr_help, | ||
662 | Returns |
|
677 | ESC_MAGIC : self.tr_magic, | |
663 | ------- |
|
678 | ESC_QUOTE : self.tr_quote, | |
664 | new_line : str |
|
679 | ESC_QUOTE2 : self.tr_quote2, | |
665 | Transformed line, which may be identical to the original.""" |
|
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(): |
|
761 | # Get line endpoints, where the escapes can be | |
668 | return line |
|
762 | line_info = LineInfo(line) | |
669 |
|
763 | |||
670 | new_line = line |
|
764 | # If the escape is not at the start, only '?' needs to be special-cased. | |
671 | for f in [transform_assign_system, transform_assign_magic, |
|
765 | # All other escapes are only valid at the start | |
672 | transform_classic_prompt, transform_ipy_prompt ] : |
|
766 | if not line_info.esc in self.tr: | |
673 | new_line = f(new_line) |
|
767 | if line.endswith(ESC_HELP): | |
674 | return new_line |
|
768 | return self.tr_help(line_info) | |
675 |
|
769 | else: | ||
676 | # Support for syntax transformations that use explicit escapes typed by the |
|
770 | # If we don't recognize the escape, don't modify the line | |
677 | # user at the beginning of a line |
|
771 | return 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 |
|
|||
762 |
|
772 | |||
763 | # Get line endpoints, where the escapes can be |
|
773 | return self.tr[line_info.esc](line_info) | |
764 | line_info = LineInfo(line) |
|
|||
765 |
|
774 | |||
766 | # If the escape is not at the start, only '?' needs to be special-cased. |
|
775 | # A function-looking object to be used by the rest of the code. The purpose of | |
767 | # All other escapes are only valid at the start |
|
776 | # the class in this case is to organize related functionality, more than to | |
768 | if not line_info.esc in tr: |
|
777 | # manage state. | |
769 | if line.endswith(ESC_HELP): |
|
778 | transform_escaped = EscapedTransformer() | |
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) |
|
|||
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. | |||
|
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) |
|
815 | out = super(IPythonInputSplitter, self).push(line) | |
792 | else: |
|
|||
793 | new_list = lines_list |
|
|||
794 |
|
816 | |||
795 | # Now apply the unescaped transformations to each input line |
|
817 | return out | |
796 | new_list = map(transform_unescaped, new_list) |
|
|||
797 | newlines = '\n'.join(new_list) |
|
|||
798 | return super(IPythonInputSplitter, self).push(newlines) |
|
@@ -593,16 +593,14 b" if __name__ == '__main__':" | |||||
593 | # picked up by any test suite. Useful mostly for illustration and during |
|
593 | # picked up by any test suite. Useful mostly for illustration and during | |
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