Show More
@@ -82,6 +82,7 b' from IPython.core.inputtransformer import (leading_indent,' | |||||
82 | escaped_transformer, |
|
82 | escaped_transformer, | |
83 | assign_from_magic, |
|
83 | assign_from_magic, | |
84 | assign_from_system, |
|
84 | assign_from_system, | |
|
85 | assemble_python_lines, | |||
85 | ) |
|
86 | ) | |
86 |
|
87 | |||
87 | # Temporary! |
|
88 | # Temporary! | |
@@ -499,29 +500,55 b' class IPythonInputSplitter(InputSplitter):' | |||||
499 | # Flag to track when a transformer has stored input that it hasn't given |
|
500 | # Flag to track when a transformer has stored input that it hasn't given | |
500 | # back yet. |
|
501 | # back yet. | |
501 | transformer_accumulating = False |
|
502 | transformer_accumulating = False | |
|
503 | ||||
|
504 | # Flag to track when assemble_python_lines has stored input that it hasn't | |||
|
505 | # given back yet. | |||
|
506 | within_python_line = False | |||
502 |
|
507 | |||
503 | # Private attributes |
|
508 | # Private attributes | |
504 |
|
509 | |||
505 | # List with lines of raw input accumulated so far. |
|
510 | # List with lines of raw input accumulated so far. | |
506 | _buffer_raw = None |
|
511 | _buffer_raw = None | |
507 |
|
512 | |||
508 |
def __init__(self, input_mode=None, transforms=None |
|
513 | def __init__(self, input_mode=None, physical_line_transforms=None, | |
|
514 | logical_line_transforms=None, python_line_transforms=None): | |||
509 | super(IPythonInputSplitter, self).__init__(input_mode) |
|
515 | super(IPythonInputSplitter, self).__init__(input_mode) | |
510 | self._buffer_raw = [] |
|
516 | self._buffer_raw = [] | |
511 | self._validate = True |
|
517 | self._validate = True | |
512 | if transforms is not None: |
|
518 | ||
513 |
|
|
519 | self.physical_line_transforms = physical_line_transforms or \ | |
514 | else: |
|
520 | [leading_indent(), | |
515 | self.transforms = [leading_indent(), |
|
521 | classic_prompt(), | |
516 |
|
|
522 | ipy_prompt(), | |
517 | ipy_prompt(), |
|
523 | ] | |
518 | cellmagic(), |
|
524 | ||
519 | assemble_logical_lines(), |
|
525 | self.assemble_logical_lines = assemble_logical_lines() | |
520 | help_end(), |
|
526 | self.logical_line_transforms = logical_line_transforms or \ | |
521 | escaped_transformer(), |
|
527 | [cellmagic(), | |
522 | assign_from_magic(), |
|
528 | help_end(), | |
523 | assign_from_system(), |
|
529 | escaped_transformer(), | |
524 | ] |
|
530 | assign_from_magic(), | |
|
531 | assign_from_system(), | |||
|
532 | ] | |||
|
533 | ||||
|
534 | self.assemble_python_lines = assemble_python_lines() | |||
|
535 | self.python_line_transforms = python_line_transforms or [] | |||
|
536 | ||||
|
537 | @property | |||
|
538 | def transforms(self): | |||
|
539 | "Quick access to all transformers." | |||
|
540 | return self.physical_line_transforms + \ | |||
|
541 | [self.assemble_logical_lines] + self.logical_line_transforms + \ | |||
|
542 | [self.assemble_python_lines] + self.python_line_transforms | |||
|
543 | ||||
|
544 | @property | |||
|
545 | def transforms_in_use(self): | |||
|
546 | """Transformers, excluding logical line transformers if we're in a | |||
|
547 | Python line.""" | |||
|
548 | t = self.physical_line_transforms + [self.assemble_logical_lines] | |||
|
549 | if not self.within_python_line: | |||
|
550 | t += self.logical_line_transforms | |||
|
551 | return t + [self.assemble_python_lines] + self.python_line_transforms | |||
525 |
|
552 | |||
526 | def reset(self): |
|
553 | def reset(self): | |
527 | """Reset the input buffer and associated state.""" |
|
554 | """Reset the input buffer and associated state.""" | |
@@ -533,12 +560,18 b' class IPythonInputSplitter(InputSplitter):' | |||||
533 | t.reset() |
|
560 | t.reset() | |
534 |
|
561 | |||
535 | def flush_transformers(self): |
|
562 | def flush_transformers(self): | |
|
563 | def _flush(transform, out): | |||
|
564 | if out is not None: | |||
|
565 | tmp = transform.push(out) | |||
|
566 | return tmp or transform.reset() or None | |||
|
567 | else: | |||
|
568 | return transform.reset() or None | |||
|
569 | ||||
536 | out = None |
|
570 | out = None | |
537 | for t in self.transforms: |
|
571 | for t in self.transforms_in_use: | |
538 |
t |
|
572 | out = _flush(t, out) | |
539 | if tmp: |
|
573 | ||
540 | out = tmp |
|
574 | if out is not None: | |
541 | if out: |
|
|||
542 | self._store(out) |
|
575 | self._store(out) | |
543 |
|
576 | |||
544 | def source_raw_reset(self): |
|
577 | def source_raw_reset(self): | |
@@ -641,11 +674,39 b' class IPythonInputSplitter(InputSplitter):' | |||||
641 |
|
674 | |||
642 | def push_line(self, line): |
|
675 | def push_line(self, line): | |
643 | buf = self._buffer |
|
676 | buf = self._buffer | |
644 | for transformer in self.transforms: |
|
677 | ||
|
678 | def _accumulating(dbg): | |||
|
679 | #print(dbg) | |||
|
680 | self.transformer_accumulating = True | |||
|
681 | return False | |||
|
682 | ||||
|
683 | for transformer in self.physical_line_transforms: | |||
|
684 | line = transformer.push(line) | |||
|
685 | if line is None: | |||
|
686 | return _accumulating(transformer) | |||
|
687 | ||||
|
688 | line = self.assemble_logical_lines.push(line) | |||
|
689 | if line is None: | |||
|
690 | return _accumulating('acc logical line') | |||
|
691 | ||||
|
692 | if not self.within_python_line: | |||
|
693 | for transformer in self.logical_line_transforms: | |||
|
694 | line = transformer.push(line) | |||
|
695 | if line is None: | |||
|
696 | return _accumulating(transformer) | |||
|
697 | ||||
|
698 | line = self.assemble_python_lines.push(line) | |||
|
699 | if line is None: | |||
|
700 | self.within_python_line = True | |||
|
701 | return _accumulating('acc python line') | |||
|
702 | else: | |||
|
703 | self.within_python_line = False | |||
|
704 | ||||
|
705 | for transformer in self.python_line_transforms: | |||
645 | line = transformer.push(line) |
|
706 | line = transformer.push(line) | |
646 | if line is None: |
|
707 | if line is None: | |
647 |
|
|
708 | return _accumulating(transformer) | |
648 | return False |
|
|||
649 |
|
709 | |||
|
710 | #print("transformers clear") #debug | |||
650 | self.transformer_accumulating = False |
|
711 | self.transformer_accumulating = False | |
651 | return super(IPythonInputSplitter, self).push(line) |
|
712 | return super(IPythonInputSplitter, self).push(line) |
@@ -127,7 +127,7 b' class TokenInputTransformer(InputTransformer):' | |||||
127 | def __init__(self, func): |
|
127 | def __init__(self, func): | |
128 | self.func = func |
|
128 | self.func = func | |
129 | self.current_line = "" |
|
129 | self.current_line = "" | |
130 | self.line_used= False |
|
130 | self.line_used = False | |
131 | self.reset_tokenizer() |
|
131 | self.reset_tokenizer() | |
132 |
|
132 | |||
133 | def reset_tokenizer(self): |
|
133 | def reset_tokenizer(self): | |
@@ -141,19 +141,29 b' class TokenInputTransformer(InputTransformer):' | |||||
141 |
|
141 | |||
142 | def push(self, line): |
|
142 | def push(self, line): | |
143 | self.current_line += line + "\n" |
|
143 | self.current_line += line + "\n" | |
|
144 | if self.current_line.isspace(): | |||
|
145 | return self.reset() | |||
|
146 | ||||
144 | self.line_used = False |
|
147 | self.line_used = False | |
145 | tokens = [] |
|
148 | tokens = [] | |
|
149 | stop_at_NL = False | |||
146 | try: |
|
150 | try: | |
147 | for intok in self.tokenizer: |
|
151 | for intok in self.tokenizer: | |
148 | tokens.append(intok) |
|
152 | tokens.append(intok) | |
149 | if intok[0] in (tokenize.NEWLINE, tokenize.NL): |
|
153 | t = intok[0] | |
|
154 | if t == tokenize.NEWLINE or (stop_at_NL and t == tokenize.NL): | |||
150 | # Stop before we try to pull a line we don't have yet |
|
155 | # Stop before we try to pull a line we don't have yet | |
151 | break |
|
156 | break | |
|
157 | elif t in (tokenize.COMMENT, tokenize.ERRORTOKEN): | |||
|
158 | stop_at_NL = True | |||
152 | except tokenize.TokenError: |
|
159 | except tokenize.TokenError: | |
153 | # Multi-line statement - stop and try again with the next line |
|
160 | # Multi-line statement - stop and try again with the next line | |
154 | self.reset_tokenizer() |
|
161 | self.reset_tokenizer() | |
155 | return None |
|
162 | return None | |
156 |
|
163 | |||
|
164 | return self.output(tokens) | |||
|
165 | ||||
|
166 | def output(self, tokens): | |||
157 | self.current_line = "" |
|
167 | self.current_line = "" | |
158 | self.reset_tokenizer() |
|
168 | self.reset_tokenizer() | |
159 | return untokenize(self.func(tokens)).rstrip('\n') |
|
169 | return untokenize(self.func(tokens)).rstrip('\n') | |
@@ -161,12 +171,35 b' class TokenInputTransformer(InputTransformer):' | |||||
161 | def reset(self): |
|
171 | def reset(self): | |
162 | l = self.current_line |
|
172 | l = self.current_line | |
163 | self.current_line = "" |
|
173 | self.current_line = "" | |
|
174 | self.reset_tokenizer() | |||
164 | if l: |
|
175 | if l: | |
165 | return l.rstrip('\n') |
|
176 | return l.rstrip('\n') | |
166 |
|
177 | |||
167 | @TokenInputTransformer.wrap |
|
178 | class assemble_python_lines(TokenInputTransformer): | |
168 | def assemble_logical_lines(tokens): |
|
179 | def __init__(self): | |
169 | return tokens |
|
180 | super(assemble_python_lines, self).__init__(None) | |
|
181 | ||||
|
182 | def output(self, tokens): | |||
|
183 | return self.reset() | |||
|
184 | ||||
|
185 | @CoroutineInputTransformer.wrap | |||
|
186 | def assemble_logical_lines(): | |||
|
187 | """Join lines following explicit line continuations (\)""" | |||
|
188 | line = '' | |||
|
189 | while True: | |||
|
190 | line = (yield line) | |||
|
191 | if not line or line.isspace(): | |||
|
192 | continue | |||
|
193 | ||||
|
194 | parts = [] | |||
|
195 | while line is not None: | |||
|
196 | parts.append(line.rstrip('\\')) | |||
|
197 | if not line.endswith('\\'): | |||
|
198 | break | |||
|
199 | line = (yield None) | |||
|
200 | ||||
|
201 | # Output | |||
|
202 | line = ' '.join(parts) | |||
170 |
|
203 | |||
171 | # Utilities |
|
204 | # Utilities | |
172 | def _make_help_call(target, esc, lspace, next_input=None): |
|
205 | def _make_help_call(target, esc, lspace, next_input=None): |
@@ -270,6 +270,7 b' class InputSplitterTestCase(unittest.TestCase):' | |||||
270 | isp = self.isp |
|
270 | isp = self.isp | |
271 | self.assertFalse(isp.push('if 1:')) |
|
271 | self.assertFalse(isp.push('if 1:')) | |
272 | for line in [' x=1', '# a comment', ' y=2']: |
|
272 | for line in [' x=1', '# a comment', ' y=2']: | |
|
273 | print(line) | |||
273 | self.assertTrue(isp.push(line)) |
|
274 | self.assertTrue(isp.push(line)) | |
274 |
|
275 | |||
275 | def test_push3(self): |
|
276 | def test_push3(self): |
@@ -356,8 +356,8 b' def test_token_input_transformer():' | |||||
356 | [ [(u"a = 1.2; b = '''x", None), |
|
356 | [ [(u"a = 1.2; b = '''x", None), | |
357 | (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")), |
|
357 | (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")), | |
358 | ], |
|
358 | ], | |
359 | [(u"a = [1.2,", u_fmt(u"a =[Decimal ({u}'1.2'),")), |
|
359 | [(u"a = [1.2,", None), | |
360 | (u"3]", u"3 ]"), |
|
360 | (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")), | |
361 | ], |
|
361 | ], | |
362 | [(u"a = '''foo", None), # Test resetting when within a multi-line string |
|
362 | [(u"a = '''foo", None), # Test resetting when within a multi-line string | |
363 | (u"bar", None), |
|
363 | (u"bar", None), |
General Comments 0
You need to be logged in to leave comments.
Login now