Show More
@@ -69,36 +69,24 b' import ast' | |||||
69 | import codeop |
|
69 | import codeop | |
70 | import re |
|
70 | import re | |
71 | import sys |
|
71 | import sys | |
72 | import tokenize |
|
|||
73 | from StringIO import StringIO |
|
|||
74 |
|
72 | |||
75 | # IPython modules |
|
73 | # IPython modules | |
76 | from IPython.core.splitinput import split_user_input, LineInfo |
|
74 | from IPython.core.splitinput import split_user_input, LineInfo | |
77 | from IPython.utils.py3compat import cast_unicode |
|
75 | from IPython.utils.py3compat import cast_unicode | |
78 |
|
76 | from IPython.core.inputtransformer import (leading_indent, | ||
79 | #----------------------------------------------------------------------------- |
|
77 | classic_prompt, | |
80 | # Globals |
|
78 | ipy_prompt, | |
81 | #----------------------------------------------------------------------------- |
|
79 | cellmagic, | |
82 |
|
80 | help_end, | ||
83 | # The escape sequences that define the syntax transformations IPython will |
|
81 | escaped_transformer, | |
84 | # apply to user input. These can NOT be just changed here: many regular |
|
82 | assign_from_magic, | |
85 | # expressions and other parts of the code may use their hardcoded values, and |
|
83 | assign_from_system, | |
86 | # for all intents and purposes they constitute the 'IPython syntax', so they |
|
84 | ) | |
87 | # should be considered fixed. |
|
85 | ||
88 |
|
86 | # Temporary! | ||
89 | ESC_SHELL = '!' # Send line to underlying system shell |
|
87 | from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP, | |
90 | ESC_SH_CAP = '!!' # Send line to system shell and capture output |
|
88 | ESC_HELP2, ESC_MAGIC, ESC_MAGIC2, | |
91 | ESC_HELP = '?' # Find information about object |
|
89 | ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES) | |
92 | ESC_HELP2 = '??' # Find extra-detailed information about object |
|
|||
93 | ESC_MAGIC = '%' # Call magic function |
|
|||
94 | ESC_MAGIC2 = '%%' # Call cell-magic function |
|
|||
95 | ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call |
|
|||
96 | ESC_QUOTE2 = ';' # Quote all args as a single string, call |
|
|||
97 | ESC_PAREN = '/' # Call first argument with rest of line as arguments |
|
|||
98 |
|
||||
99 | ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\ |
|
|||
100 | ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\ |
|
|||
101 | ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ] |
|
|||
102 |
|
90 | |||
103 | #----------------------------------------------------------------------------- |
|
91 | #----------------------------------------------------------------------------- | |
104 | # Utilities |
|
92 | # Utilities | |
@@ -205,29 +193,6 b' def remove_comments(src):' | |||||
205 |
|
193 | |||
206 | return re.sub('#.*', '', src) |
|
194 | return re.sub('#.*', '', src) | |
207 |
|
195 | |||
208 | def has_comment(src): |
|
|||
209 | """Indicate whether an input line has (i.e. ends in, or is) a comment. |
|
|||
210 |
|
||||
211 | This uses tokenize, so it can distinguish comments from # inside strings. |
|
|||
212 |
|
||||
213 | Parameters |
|
|||
214 | ---------- |
|
|||
215 | src : string |
|
|||
216 | A single line input string. |
|
|||
217 |
|
||||
218 | Returns |
|
|||
219 | ------- |
|
|||
220 | Boolean: True if source has a comment. |
|
|||
221 | """ |
|
|||
222 | readline = StringIO(src).readline |
|
|||
223 | toktypes = set() |
|
|||
224 | try: |
|
|||
225 | for t in tokenize.generate_tokens(readline): |
|
|||
226 | toktypes.add(t[0]) |
|
|||
227 | except tokenize.TokenError: |
|
|||
228 | pass |
|
|||
229 | return(tokenize.COMMENT in toktypes) |
|
|||
230 |
|
||||
231 |
|
196 | |||
232 | def get_input_encoding(): |
|
197 | def get_input_encoding(): | |
233 | """Return the default standard input encoding. |
|
198 | """Return the default standard input encoding. | |
@@ -524,219 +489,15 b' class InputSplitter(object):' | |||||
524 | return u''.join(buffer) |
|
489 | return u''.join(buffer) | |
525 |
|
490 | |||
526 |
|
491 | |||
527 | #----------------------------------------------------------------------------- |
|
|||
528 | # Functions and classes for IPython-specific syntactic support |
|
|||
529 | #----------------------------------------------------------------------------- |
|
|||
530 |
|
||||
531 | # The escaped translators ALL receive a line where their own escape has been |
|
|||
532 | # stripped. Only '?' is valid at the end of the line, all others can only be |
|
|||
533 | # placed at the start. |
|
|||
534 |
|
||||
535 | # Transformations of the special syntaxes that don't rely on an explicit escape |
|
|||
536 | # character but instead on patterns on the input line |
|
|||
537 |
|
||||
538 | # The core transformations are implemented as standalone functions that can be |
|
|||
539 | # tested and validated in isolation. Each of these uses a regexp, we |
|
|||
540 | # pre-compile these and keep them close to each function definition for clarity |
|
|||
541 |
|
||||
542 | _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' |
|
|||
543 | r'\s*=\s*!\s*(?P<cmd>.*)') |
|
|||
544 |
|
||||
545 | def transform_assign_system(line): |
|
|||
546 | """Handle the `files = !ls` syntax.""" |
|
|||
547 | m = _assign_system_re.match(line) |
|
|||
548 | if m is not None: |
|
|||
549 | cmd = m.group('cmd') |
|
|||
550 | lhs = m.group('lhs') |
|
|||
551 | new_line = '%s = get_ipython().getoutput(%r)' % (lhs, cmd) |
|
|||
552 | return new_line |
|
|||
553 | return line |
|
|||
554 |
|
||||
555 |
|
||||
556 | _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))' |
|
|||
557 | r'\s*=\s*%\s*(?P<cmd>.*)') |
|
|||
558 |
|
||||
559 | def transform_assign_magic(line): |
|
|||
560 | """Handle the `a = %who` syntax.""" |
|
|||
561 | m = _assign_magic_re.match(line) |
|
|||
562 | if m is not None: |
|
|||
563 | cmd = m.group('cmd') |
|
|||
564 | lhs = m.group('lhs') |
|
|||
565 | new_line = '%s = get_ipython().magic(%r)' % (lhs, cmd) |
|
|||
566 | return new_line |
|
|||
567 | return line |
|
|||
568 |
|
||||
569 |
|
||||
570 | _classic_prompt_re = re.compile(r'^([ \t]*>>> |^[ \t]*\.\.\. )') |
|
|||
571 |
|
||||
572 | def transform_classic_prompt(line): |
|
|||
573 | """Handle inputs that start with '>>> ' syntax.""" |
|
|||
574 |
|
||||
575 | if not line or line.isspace(): |
|
|||
576 | return line |
|
|||
577 | m = _classic_prompt_re.match(line) |
|
|||
578 | if m: |
|
|||
579 | return line[len(m.group(0)):] |
|
|||
580 | else: |
|
|||
581 | return line |
|
|||
582 |
|
||||
583 |
|
||||
584 | _ipy_prompt_re = re.compile(r'^([ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )') |
|
|||
585 |
|
||||
586 | def transform_ipy_prompt(line): |
|
|||
587 | """Handle inputs that start classic IPython prompt syntax.""" |
|
|||
588 |
|
||||
589 | if not line or line.isspace(): |
|
|||
590 | return line |
|
|||
591 | #print 'LINE: %r' % line # dbg |
|
|||
592 | m = _ipy_prompt_re.match(line) |
|
|||
593 | if m: |
|
|||
594 | #print 'MATCH! %r -> %r' % (line, line[len(m.group(0)):]) # dbg |
|
|||
595 | return line[len(m.group(0)):] |
|
|||
596 | else: |
|
|||
597 | return line |
|
|||
598 |
|
||||
599 |
|
||||
600 | def _make_help_call(target, esc, lspace, next_input=None): |
|
|||
601 | """Prepares a pinfo(2)/psearch call from a target name and the escape |
|
|||
602 | (i.e. ? or ??)""" |
|
|||
603 | method = 'pinfo2' if esc == '??' \ |
|
|||
604 | else 'psearch' if '*' in target \ |
|
|||
605 | else 'pinfo' |
|
|||
606 | arg = " ".join([method, target]) |
|
|||
607 | if next_input is None: |
|
|||
608 | return '%sget_ipython().magic(%r)' % (lspace, arg) |
|
|||
609 | else: |
|
|||
610 | return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \ |
|
|||
611 | (lspace, next_input, arg) |
|
|||
612 |
|
||||
613 |
|
||||
614 | _initial_space_re = re.compile(r'\s*') |
|
|||
615 |
|
||||
616 | _help_end_re = re.compile(r"""(%{0,2} |
|
|||
617 | [a-zA-Z_*][\w*]* # Variable name |
|
|||
618 | (\.[a-zA-Z_*][\w*]*)* # .etc.etc |
|
|||
619 | ) |
|
|||
620 | (\?\??)$ # ? or ??""", |
|
|||
621 | re.VERBOSE) |
|
|||
622 |
|
||||
623 |
|
||||
624 | def transform_help_end(line): |
|
|||
625 | """Translate lines with ?/?? at the end""" |
|
|||
626 | m = _help_end_re.search(line) |
|
|||
627 | if m is None or has_comment(line): |
|
|||
628 | return line |
|
|||
629 | target = m.group(1) |
|
|||
630 | esc = m.group(3) |
|
|||
631 | lspace = _initial_space_re.match(line).group(0) |
|
|||
632 |
|
||||
633 | # If we're mid-command, put it back on the next prompt for the user. |
|
|||
634 | next_input = line.rstrip('?') if line.strip() != m.group(0) else None |
|
|||
635 |
|
||||
636 | return _make_help_call(target, esc, lspace, next_input) |
|
|||
637 |
|
||||
638 |
|
||||
639 | class EscapedTransformer(object): |
|
|||
640 | """Class to transform lines that are explicitly escaped out.""" |
|
|||
641 |
|
||||
642 | def __init__(self): |
|
|||
643 | tr = { ESC_SHELL : self._tr_system, |
|
|||
644 | ESC_SH_CAP : self._tr_system2, |
|
|||
645 | ESC_HELP : self._tr_help, |
|
|||
646 | ESC_HELP2 : self._tr_help, |
|
|||
647 | ESC_MAGIC : self._tr_magic, |
|
|||
648 | ESC_QUOTE : self._tr_quote, |
|
|||
649 | ESC_QUOTE2 : self._tr_quote2, |
|
|||
650 | ESC_PAREN : self._tr_paren } |
|
|||
651 | self.tr = tr |
|
|||
652 |
|
||||
653 | # Support for syntax transformations that use explicit escapes typed by the |
|
|||
654 | # user at the beginning of a line |
|
|||
655 | @staticmethod |
|
|||
656 | def _tr_system(line_info): |
|
|||
657 | "Translate lines escaped with: !" |
|
|||
658 | cmd = line_info.line.lstrip().lstrip(ESC_SHELL) |
|
|||
659 | return '%sget_ipython().system(%r)' % (line_info.pre, cmd) |
|
|||
660 |
|
||||
661 | @staticmethod |
|
|||
662 | def _tr_system2(line_info): |
|
|||
663 | "Translate lines escaped with: !!" |
|
|||
664 | cmd = line_info.line.lstrip()[2:] |
|
|||
665 | return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd) |
|
|||
666 |
|
||||
667 | @staticmethod |
|
|||
668 | def _tr_help(line_info): |
|
|||
669 | "Translate lines escaped with: ?/??" |
|
|||
670 | # A naked help line should just fire the intro help screen |
|
|||
671 | if not line_info.line[1:]: |
|
|||
672 | return 'get_ipython().show_usage()' |
|
|||
673 |
|
||||
674 | return _make_help_call(line_info.ifun, line_info.esc, line_info.pre) |
|
|||
675 |
|
||||
676 | @staticmethod |
|
|||
677 | def _tr_magic(line_info): |
|
|||
678 | "Translate lines escaped with: %" |
|
|||
679 | tpl = '%sget_ipython().magic(%r)' |
|
|||
680 | cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip() |
|
|||
681 | return tpl % (line_info.pre, cmd) |
|
|||
682 |
|
||||
683 | @staticmethod |
|
|||
684 | def _tr_quote(line_info): |
|
|||
685 | "Translate lines escaped with: ," |
|
|||
686 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, |
|
|||
687 | '", "'.join(line_info.the_rest.split()) ) |
|
|||
688 |
|
||||
689 | @staticmethod |
|
|||
690 | def _tr_quote2(line_info): |
|
|||
691 | "Translate lines escaped with: ;" |
|
|||
692 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, |
|
|||
693 | line_info.the_rest) |
|
|||
694 |
|
||||
695 | @staticmethod |
|
|||
696 | def _tr_paren(line_info): |
|
|||
697 | "Translate lines escaped with: /" |
|
|||
698 | return '%s%s(%s)' % (line_info.pre, line_info.ifun, |
|
|||
699 | ", ".join(line_info.the_rest.split())) |
|
|||
700 |
|
||||
701 | def __call__(self, line): |
|
|||
702 | """Class to transform lines that are explicitly escaped out. |
|
|||
703 |
|
||||
704 | This calls the above _tr_* static methods for the actual line |
|
|||
705 | translations.""" |
|
|||
706 |
|
||||
707 | # Empty lines just get returned unmodified |
|
|||
708 | if not line or line.isspace(): |
|
|||
709 | return line |
|
|||
710 |
|
||||
711 | # Get line endpoints, where the escapes can be |
|
|||
712 | line_info = LineInfo(line) |
|
|||
713 |
|
||||
714 | if not line_info.esc in self.tr: |
|
|||
715 | # If we don't recognize the escape, don't modify the line |
|
|||
716 | return line |
|
|||
717 |
|
||||
718 | return self.tr[line_info.esc](line_info) |
|
|||
719 |
|
||||
720 |
|
||||
721 | # A function-looking object to be used by the rest of the code. The purpose of |
|
|||
722 | # the class in this case is to organize related functionality, more than to |
|
|||
723 | # manage state. |
|
|||
724 | transform_escaped = EscapedTransformer() |
|
|||
725 |
|
||||
726 |
|
||||
727 | class IPythonInputSplitter(InputSplitter): |
|
492 | class IPythonInputSplitter(InputSplitter): | |
728 | """An input splitter that recognizes all of IPython's special syntax.""" |
|
493 | """An input splitter that recognizes all of IPython's special syntax.""" | |
729 |
|
494 | |||
730 | # String with raw, untransformed input. |
|
495 | # String with raw, untransformed input. | |
731 | source_raw = '' |
|
496 | source_raw = '' | |
732 |
|
497 | |||
733 | # Flag to track when we're in the middle of processing a cell magic, since |
|
498 | # Flag to track when a transformer has stored input that it hasn't given | |
734 | # the logic has to change. In that case, we apply no transformations at |
|
499 | # back yet. | |
735 | # all. |
|
500 | transformer_accumulating = False | |
736 | processing_cell_magic = False |
|
|||
737 |
|
||||
738 | # Storage for all blocks of input that make up a cell magic |
|
|||
739 | cell_magic_parts = [] |
|
|||
740 |
|
501 | |||
741 | # Private attributes |
|
502 | # Private attributes | |
742 |
|
503 | |||
@@ -747,14 +508,22 b' class IPythonInputSplitter(InputSplitter):' | |||||
747 | super(IPythonInputSplitter, self).__init__(input_mode) |
|
508 | super(IPythonInputSplitter, self).__init__(input_mode) | |
748 | self._buffer_raw = [] |
|
509 | self._buffer_raw = [] | |
749 | self._validate = True |
|
510 | self._validate = True | |
|
511 | self.transforms = [leading_indent, | |||
|
512 | classic_prompt, | |||
|
513 | ipy_prompt, | |||
|
514 | cellmagic, | |||
|
515 | help_end, | |||
|
516 | escaped_transformer, | |||
|
517 | assign_from_magic, | |||
|
518 | assign_from_system, | |||
|
519 | ] | |||
750 |
|
520 | |||
751 | def reset(self): |
|
521 | def reset(self): | |
752 | """Reset the input buffer and associated state.""" |
|
522 | """Reset the input buffer and associated state.""" | |
753 | super(IPythonInputSplitter, self).reset() |
|
523 | super(IPythonInputSplitter, self).reset() | |
754 | self._buffer_raw[:] = [] |
|
524 | self._buffer_raw[:] = [] | |
755 | self.source_raw = '' |
|
525 | self.source_raw = '' | |
756 | self.cell_magic_parts = [] |
|
526 | self.transformer_accumulating = False | |
757 | self.processing_cell_magic = False |
|
|||
758 |
|
527 | |||
759 | def source_raw_reset(self): |
|
528 | def source_raw_reset(self): | |
760 | """Return input and raw source and perform a full reset. |
|
529 | """Return input and raw source and perform a full reset. | |
@@ -765,56 +534,11 b' class IPythonInputSplitter(InputSplitter):' | |||||
765 | return out, out_r |
|
534 | return out, out_r | |
766 |
|
535 | |||
767 | def push_accepts_more(self): |
|
536 | def push_accepts_more(self): | |
768 | if self.processing_cell_magic: |
|
537 | if self.transformer_accumulating: | |
769 |
return |
|
538 | return True | |
770 | else: |
|
539 | else: | |
771 | return super(IPythonInputSplitter, self).push_accepts_more() |
|
540 | return super(IPythonInputSplitter, self).push_accepts_more() | |
772 |
|
541 | |||
773 | def _handle_cell_magic(self, lines): |
|
|||
774 | """Process lines when they start with %%, which marks cell magics. |
|
|||
775 | """ |
|
|||
776 | self.processing_cell_magic = True |
|
|||
777 | first, _, body = lines.partition('\n') |
|
|||
778 | magic_name, _, line = first.partition(' ') |
|
|||
779 | magic_name = magic_name.lstrip(ESC_MAGIC) |
|
|||
780 | # We store the body of the cell and create a call to a method that |
|
|||
781 | # will use this stored value. This is ugly, but it's a first cut to |
|
|||
782 | # get it all working, as right now changing the return API of our |
|
|||
783 | # methods would require major refactoring. |
|
|||
784 | self.cell_magic_parts = [body] |
|
|||
785 | tpl = 'get_ipython()._run_cached_cell_magic(%r, %r)' |
|
|||
786 | tlines = tpl % (magic_name, line) |
|
|||
787 | self._store(tlines) |
|
|||
788 | self._store(lines, self._buffer_raw, 'source_raw') |
|
|||
789 | # We can actually choose whether to allow for single blank lines here |
|
|||
790 | # during input for clients that use cell mode to decide when to stop |
|
|||
791 | # pushing input (currently only the Qt console). |
|
|||
792 | # My first implementation did that, and then I realized it wasn't |
|
|||
793 | # consistent with the terminal behavior, so I've reverted it to one |
|
|||
794 | # line. But I'm leaving it here so we can easily test both behaviors, |
|
|||
795 | # I kind of liked having full blank lines allowed in the cell magics... |
|
|||
796 | #self._is_complete = last_two_blanks(lines) |
|
|||
797 | self._is_complete = last_blank(lines) |
|
|||
798 | return self._is_complete |
|
|||
799 |
|
||||
800 | def _line_mode_cell_append(self, lines): |
|
|||
801 | """Append new content for a cell magic in line mode. |
|
|||
802 | """ |
|
|||
803 | # Only store the raw input. Lines beyond the first one are only only |
|
|||
804 | # stored for history purposes; for execution the caller will grab the |
|
|||
805 | # magic pieces from cell_magic_parts and will assemble the cell body |
|
|||
806 | self._store(lines, self._buffer_raw, 'source_raw') |
|
|||
807 | self.cell_magic_parts.append(lines) |
|
|||
808 | # Find out if the last stored block has a whitespace line as its |
|
|||
809 | # last line and also this line is whitespace, case in which we're |
|
|||
810 | # done (two contiguous blank lines signal termination). Note that |
|
|||
811 | # the storage logic *enforces* that every stored block is |
|
|||
812 | # newline-terminated, so we grab everything but the last character |
|
|||
813 | # so we can have the body of the block alone. |
|
|||
814 | last_block = self.cell_magic_parts[-1] |
|
|||
815 | self._is_complete = last_blank(last_block) and lines.isspace() |
|
|||
816 | return self._is_complete |
|
|||
817 |
|
||||
818 | def transform_cell(self, cell): |
|
542 | def transform_cell(self, cell): | |
819 | """Process and translate a cell of input. |
|
543 | """Process and translate a cell of input. | |
820 | """ |
|
544 | """ | |
@@ -851,24 +575,10 b' class IPythonInputSplitter(InputSplitter):' | |||||
851 | # We must ensure all input is pure unicode |
|
575 | # We must ensure all input is pure unicode | |
852 | lines = cast_unicode(lines, self.encoding) |
|
576 | lines = cast_unicode(lines, self.encoding) | |
853 |
|
577 | |||
854 | # If the entire input block is a cell magic, return after handling it |
|
|||
855 | # as the rest of the transformation logic should be skipped. |
|
|||
856 | if lines.startswith('%%') and not \ |
|
|||
857 | (len(lines.splitlines()) == 1 and lines.strip().endswith('?')): |
|
|||
858 | return self._handle_cell_magic(lines) |
|
|||
859 |
|
||||
860 | # In line mode, a cell magic can arrive in separate pieces |
|
|||
861 | if self.input_mode == 'line' and self.processing_cell_magic: |
|
|||
862 | return self._line_mode_cell_append(lines) |
|
|||
863 |
|
||||
864 | # The rest of the processing is for 'normal' content, i.e. IPython |
|
578 | # The rest of the processing is for 'normal' content, i.e. IPython | |
865 | # source that we process through our transformations pipeline. |
|
579 | # source that we process through our transformations pipeline. | |
866 | lines_list = lines.splitlines() |
|
580 | lines_list = lines.splitlines() | |
867 |
|
581 | |||
868 | transforms = [transform_ipy_prompt, transform_classic_prompt, |
|
|||
869 | transform_help_end, transform_escaped, |
|
|||
870 | transform_assign_system, transform_assign_magic] |
|
|||
871 |
|
||||
872 | # Transform logic |
|
582 | # Transform logic | |
873 | # |
|
583 | # | |
874 | # We only apply the line transformers to the input if we have either no |
|
584 | # We only apply the line transformers to the input if we have either no | |
@@ -901,16 +611,24 b' class IPythonInputSplitter(InputSplitter):' | |||||
901 | self._store(lines, self._buffer_raw, 'source_raw') |
|
611 | self._store(lines, self._buffer_raw, 'source_raw') | |
902 |
|
612 | |||
903 | try: |
|
613 | try: | |
904 | push = super(IPythonInputSplitter, self).push |
|
|||
905 | buf = self._buffer |
|
|||
906 | for line in lines_list: |
|
614 | for line in lines_list: | |
907 | if self._is_complete or not buf or \ |
|
615 | out = self.push_line(line) | |
908 | (buf and buf[-1].rstrip().endswith((':', ','))): |
|
|||
909 | for f in transforms: |
|
|||
910 | line = f(line) |
|
|||
911 |
|
||||
912 | out = push(line) |
|
|||
913 | finally: |
|
616 | finally: | |
914 | if changed_input_mode: |
|
617 | if changed_input_mode: | |
915 | self.input_mode = saved_input_mode |
|
618 | self.input_mode = saved_input_mode | |
|
619 | ||||
916 | return out |
|
620 | return out | |
|
621 | ||||
|
622 | def push_line(self, line): | |||
|
623 | buf = self._buffer | |||
|
624 | not_in_string = self._is_complete or not buf or \ | |||
|
625 | (buf and buf[-1].rstrip().endswith((':', ','))) | |||
|
626 | for transformer in self.transforms: | |||
|
627 | if not_in_string or transformer.look_in_string: | |||
|
628 | line = transformer.push(line) | |||
|
629 | if line is None: | |||
|
630 | self.transformer_accumulating = True | |||
|
631 | return False | |||
|
632 | ||||
|
633 | self.transformer_accumulating = False | |||
|
634 | return super(IPythonInputSplitter, self).push(line) |
@@ -1,11 +1,35 b'' | |||||
1 | import abc |
|
1 | import abc | |
2 | import re |
|
2 | import re | |
|
3 | from StringIO import StringIO | |||
|
4 | import tokenize | |||
3 |
|
5 | |||
4 | from IPython.core.splitinput import split_user_input, LineInfo |
|
6 | from IPython.core.splitinput import split_user_input, LineInfo | |
5 | from IPython.core.inputsplitter import (ESC_SHELL, ESC_SH_CAP, ESC_HELP, |
|
7 | ||
6 | ESC_HELP2, ESC_MAGIC, ESC_MAGIC2, |
|
8 | #----------------------------------------------------------------------------- | |
7 | ESC_QUOTE, ESC_QUOTE2, ESC_PAREN) |
|
9 | # Globals | |
8 | from IPython.core.inputsplitter import EscapedTransformer, _make_help_call, has_comment |
|
10 | #----------------------------------------------------------------------------- | |
|
11 | ||||
|
12 | # The escape sequences that define the syntax transformations IPython will | |||
|
13 | # apply to user input. These can NOT be just changed here: many regular | |||
|
14 | # expressions and other parts of the code may use their hardcoded values, and | |||
|
15 | # for all intents and purposes they constitute the 'IPython syntax', so they | |||
|
16 | # should be considered fixed. | |||
|
17 | ||||
|
18 | ESC_SHELL = '!' # Send line to underlying system shell | |||
|
19 | ESC_SH_CAP = '!!' # Send line to system shell and capture output | |||
|
20 | ESC_HELP = '?' # Find information about object | |||
|
21 | ESC_HELP2 = '??' # Find extra-detailed information about object | |||
|
22 | ESC_MAGIC = '%' # Call magic function | |||
|
23 | ESC_MAGIC2 = '%%' # Call cell-magic function | |||
|
24 | ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call | |||
|
25 | ESC_QUOTE2 = ';' # Quote all args as a single string, call | |||
|
26 | ESC_PAREN = '/' # Call first argument with rest of line as arguments | |||
|
27 | ||||
|
28 | ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\ | |||
|
29 | ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\ | |||
|
30 | ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ] | |||
|
31 | ||||
|
32 | ||||
9 |
|
33 | |||
10 | class InputTransformer(object): |
|
34 | class InputTransformer(object): | |
11 | __metaclass__ = abc.ABCMeta |
|
35 | __metaclass__ = abc.ABCMeta | |
@@ -44,16 +68,81 b' class CoroutineInputTransformer(InputTransformer):' | |||||
44 | def reset(self): |
|
68 | def reset(self): | |
45 | self.coro.send(None) |
|
69 | self.coro.send(None) | |
46 |
|
70 | |||
|
71 | ||||
|
72 | # Utilities | |||
|
73 | def _make_help_call(target, esc, lspace, next_input=None): | |||
|
74 | """Prepares a pinfo(2)/psearch call from a target name and the escape | |||
|
75 | (i.e. ? or ??)""" | |||
|
76 | method = 'pinfo2' if esc == '??' \ | |||
|
77 | else 'psearch' if '*' in target \ | |||
|
78 | else 'pinfo' | |||
|
79 | arg = " ".join([method, target]) | |||
|
80 | if next_input is None: | |||
|
81 | return '%sget_ipython().magic(%r)' % (lspace, arg) | |||
|
82 | else: | |||
|
83 | return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \ | |||
|
84 | (lspace, next_input, arg) | |||
|
85 | ||||
47 | @CoroutineInputTransformer |
|
86 | @CoroutineInputTransformer | |
48 | def escaped_transformer(): |
|
87 | def escaped_transformer(): | |
49 | et = EscapedTransformer() |
|
88 | """Translate lines beginning with one of IPython's escape characters.""" | |
|
89 | ||||
|
90 | # These define the transformations for the different escape characters. | |||
|
91 | def _tr_system(line_info): | |||
|
92 | "Translate lines escaped with: !" | |||
|
93 | cmd = line_info.line.lstrip().lstrip(ESC_SHELL) | |||
|
94 | return '%sget_ipython().system(%r)' % (line_info.pre, cmd) | |||
|
95 | ||||
|
96 | def _tr_system2(line_info): | |||
|
97 | "Translate lines escaped with: !!" | |||
|
98 | cmd = line_info.line.lstrip()[2:] | |||
|
99 | return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd) | |||
|
100 | ||||
|
101 | def _tr_help(line_info): | |||
|
102 | "Translate lines escaped with: ?/??" | |||
|
103 | # A naked help line should just fire the intro help screen | |||
|
104 | if not line_info.line[1:]: | |||
|
105 | return 'get_ipython().show_usage()' | |||
|
106 | ||||
|
107 | return _make_help_call(line_info.ifun, line_info.esc, line_info.pre) | |||
|
108 | ||||
|
109 | def _tr_magic(line_info): | |||
|
110 | "Translate lines escaped with: %" | |||
|
111 | tpl = '%sget_ipython().magic(%r)' | |||
|
112 | cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip() | |||
|
113 | return tpl % (line_info.pre, cmd) | |||
|
114 | ||||
|
115 | def _tr_quote(line_info): | |||
|
116 | "Translate lines escaped with: ," | |||
|
117 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, | |||
|
118 | '", "'.join(line_info.the_rest.split()) ) | |||
|
119 | ||||
|
120 | def _tr_quote2(line_info): | |||
|
121 | "Translate lines escaped with: ;" | |||
|
122 | return '%s%s("%s")' % (line_info.pre, line_info.ifun, | |||
|
123 | line_info.the_rest) | |||
|
124 | ||||
|
125 | def _tr_paren(line_info): | |||
|
126 | "Translate lines escaped with: /" | |||
|
127 | return '%s%s(%s)' % (line_info.pre, line_info.ifun, | |||
|
128 | ", ".join(line_info.the_rest.split())) | |||
|
129 | ||||
|
130 | tr = { ESC_SHELL : _tr_system, | |||
|
131 | ESC_SH_CAP : _tr_system2, | |||
|
132 | ESC_HELP : _tr_help, | |||
|
133 | ESC_HELP2 : _tr_help, | |||
|
134 | ESC_MAGIC : _tr_magic, | |||
|
135 | ESC_QUOTE : _tr_quote, | |||
|
136 | ESC_QUOTE2 : _tr_quote2, | |||
|
137 | ESC_PAREN : _tr_paren } | |||
|
138 | ||||
50 | line = '' |
|
139 | line = '' | |
51 | while True: |
|
140 | while True: | |
52 | line = (yield line) |
|
141 | line = (yield line) | |
53 | if not line or line.isspace(): |
|
142 | if not line or line.isspace(): | |
54 | continue |
|
143 | continue | |
55 | lineinf = LineInfo(line) |
|
144 | lineinf = LineInfo(line) | |
56 |
if lineinf.esc not in |
|
145 | if lineinf.esc not in tr: | |
57 | continue |
|
146 | continue | |
58 |
|
147 | |||
59 | parts = [] |
|
148 | parts = [] | |
@@ -65,7 +154,7 b' def escaped_transformer():' | |||||
65 |
|
154 | |||
66 | # Output |
|
155 | # Output | |
67 | lineinf = LineInfo(' '.join(parts)) |
|
156 | lineinf = LineInfo(' '.join(parts)) | |
68 |
line = |
|
157 | line = tr[lineinf.esc](lineinf) | |
69 |
|
158 | |||
70 | _initial_space_re = re.compile(r'\s*') |
|
159 | _initial_space_re = re.compile(r'\s*') | |
71 |
|
160 | |||
@@ -76,8 +165,31 b' _help_end_re = re.compile(r"""(%{0,2}' | |||||
76 | (\?\??)$ # ? or ??""", |
|
165 | (\?\??)$ # ? or ??""", | |
77 | re.VERBOSE) |
|
166 | re.VERBOSE) | |
78 |
|
167 | |||
|
168 | def has_comment(src): | |||
|
169 | """Indicate whether an input line has (i.e. ends in, or is) a comment. | |||
|
170 | ||||
|
171 | This uses tokenize, so it can distinguish comments from # inside strings. | |||
|
172 | ||||
|
173 | Parameters | |||
|
174 | ---------- | |||
|
175 | src : string | |||
|
176 | A single line input string. | |||
|
177 | ||||
|
178 | Returns | |||
|
179 | ------- | |||
|
180 | Boolean: True if source has a comment. | |||
|
181 | """ | |||
|
182 | readline = StringIO(src).readline | |||
|
183 | toktypes = set() | |||
|
184 | try: | |||
|
185 | for t in tokenize.generate_tokens(readline): | |||
|
186 | toktypes.add(t[0]) | |||
|
187 | except tokenize.TokenError: | |||
|
188 | pass | |||
|
189 | return(tokenize.COMMENT in toktypes) | |||
|
190 | ||||
79 | @StatelessInputTransformer |
|
191 | @StatelessInputTransformer | |
80 |
def |
|
192 | def help_end(line): | |
81 | """Translate lines with ?/?? at the end""" |
|
193 | """Translate lines with ?/?? at the end""" | |
82 | m = _help_end_re.search(line) |
|
194 | m = _help_end_re.search(line) | |
83 | if m is None or has_comment(line): |
|
195 | if m is None or has_comment(line): |
@@ -2591,13 +2591,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
2591 |
|
2591 | |||
2592 | self.input_splitter.push(raw_cell) |
|
2592 | self.input_splitter.push(raw_cell) | |
2593 |
|
2593 | |||
2594 | # Check for cell magics, which leave state behind. This interface is |
|
|||
2595 | # ugly, we need to do something cleaner later... Now the logic is |
|
|||
2596 | # simply that the input_splitter remembers if there was a cell magic, |
|
|||
2597 | # and in that case we grab the cell body. |
|
|||
2598 | if self.input_splitter.cell_magic_parts: |
|
|||
2599 | self._current_cell_magic_body = \ |
|
|||
2600 | ''.join(self.input_splitter.cell_magic_parts) |
|
|||
2601 | cell = self.input_splitter.source_reset() |
|
2594 | cell = self.input_splitter.source_reset() | |
2602 |
|
2595 | |||
2603 | # Our own compiler remembers the __future__ environment. If we want to |
|
2596 | # Our own compiler remembers the __future__ environment. If we want to |
@@ -37,7 +37,7 b' def test_transform_escaped():' | |||||
37 | tt.check_pairs(wrap_transform(inputtransformer.escaped_transformer), esctransform_tests) |
|
37 | tt.check_pairs(wrap_transform(inputtransformer.escaped_transformer), esctransform_tests) | |
38 |
|
38 | |||
39 | def endhelp_test(): |
|
39 | def endhelp_test(): | |
40 |
tt.check_pairs(inputtransformer. |
|
40 | tt.check_pairs(inputtransformer.help_end.push, syntax['end_help']) | |
41 |
|
41 | |||
42 | classic_prompt_tests = [ |
|
42 | classic_prompt_tests = [ | |
43 | (['>>> a=1'], ['a=1']), |
|
43 | (['>>> a=1'], ['a=1']), |
General Comments 0
You need to be logged in to leave comments.
Login now