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