##// END OF EJS Templates
only reset the transform once...
MinRK -
Show More
@@ -1,646 +1,638 b''
1 """Input handling and transformation machinery.
1 """Input handling and transformation machinery.
2
2
3 The first class in this module, :class:`InputSplitter`, is designed to tell when
3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 input from a line-oriented frontend is complete and should be executed, and when
4 input from a line-oriented frontend is complete and should be executed, and when
5 the user should be prompted for another line of code instead. The name 'input
5 the user should be prompted for another line of code instead. The name 'input
6 splitter' is largely for historical reasons.
6 splitter' is largely for historical reasons.
7
7
8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
8 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
9 with full support for the extended IPython syntax (magics, system calls, etc).
9 with full support for the extended IPython syntax (magics, system calls, etc).
10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 and stores the results.
12 and stores the results.
13
13
14 For more details, see the class docstrings below.
14 For more details, see the class docstrings below.
15 """
15 """
16
16
17 # Copyright (c) IPython Development Team.
17 # Copyright (c) IPython Development Team.
18 # Distributed under the terms of the Modified BSD License.
18 # Distributed under the terms of the Modified BSD License.
19 import ast
19 import ast
20 import codeop
20 import codeop
21 import re
21 import re
22 import sys
22 import sys
23
23
24 from IPython.utils.py3compat import cast_unicode
24 from IPython.utils.py3compat import cast_unicode
25 from IPython.core.inputtransformer import (leading_indent,
25 from IPython.core.inputtransformer import (leading_indent,
26 classic_prompt,
26 classic_prompt,
27 ipy_prompt,
27 ipy_prompt,
28 strip_encoding_cookie,
28 strip_encoding_cookie,
29 cellmagic,
29 cellmagic,
30 assemble_logical_lines,
30 assemble_logical_lines,
31 help_end,
31 help_end,
32 escaped_commands,
32 escaped_commands,
33 assign_from_magic,
33 assign_from_magic,
34 assign_from_system,
34 assign_from_system,
35 assemble_python_lines,
35 assemble_python_lines,
36 )
36 )
37
37
38 # These are available in this module for backwards compatibility.
38 # These are available in this module for backwards compatibility.
39 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
39 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
40 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
40 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
41 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
41 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Utilities
44 # Utilities
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 # FIXME: These are general-purpose utilities that later can be moved to the
47 # FIXME: These are general-purpose utilities that later can be moved to the
48 # general ward. Kept here for now because we're being very strict about test
48 # general ward. Kept here for now because we're being very strict about test
49 # coverage with this code, and this lets us ensure that we keep 100% coverage
49 # coverage with this code, and this lets us ensure that we keep 100% coverage
50 # while developing.
50 # while developing.
51
51
52 # compiled regexps for autoindent management
52 # compiled regexps for autoindent management
53 dedent_re = re.compile('|'.join([
53 dedent_re = re.compile('|'.join([
54 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
54 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
55 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
55 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
56 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
56 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
57 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
57 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
58 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
58 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
59 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
59 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
60 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
60 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
61 ]))
61 ]))
62 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
62 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
63
63
64 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
64 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
65 # before pure comments
65 # before pure comments
66 comment_line_re = re.compile('^\s*\#')
66 comment_line_re = re.compile('^\s*\#')
67
67
68
68
69 def num_ini_spaces(s):
69 def num_ini_spaces(s):
70 """Return the number of initial spaces in a string.
70 """Return the number of initial spaces in a string.
71
71
72 Note that tabs are counted as a single space. For now, we do *not* support
72 Note that tabs are counted as a single space. For now, we do *not* support
73 mixing of tabs and spaces in the user's input.
73 mixing of tabs and spaces in the user's input.
74
74
75 Parameters
75 Parameters
76 ----------
76 ----------
77 s : string
77 s : string
78
78
79 Returns
79 Returns
80 -------
80 -------
81 n : int
81 n : int
82 """
82 """
83
83
84 ini_spaces = ini_spaces_re.match(s)
84 ini_spaces = ini_spaces_re.match(s)
85 if ini_spaces:
85 if ini_spaces:
86 return ini_spaces.end()
86 return ini_spaces.end()
87 else:
87 else:
88 return 0
88 return 0
89
89
90 def last_blank(src):
90 def last_blank(src):
91 """Determine if the input source ends in a blank.
91 """Determine if the input source ends in a blank.
92
92
93 A blank is either a newline or a line consisting of whitespace.
93 A blank is either a newline or a line consisting of whitespace.
94
94
95 Parameters
95 Parameters
96 ----------
96 ----------
97 src : string
97 src : string
98 A single or multiline string.
98 A single or multiline string.
99 """
99 """
100 if not src: return False
100 if not src: return False
101 ll = src.splitlines()[-1]
101 ll = src.splitlines()[-1]
102 return (ll == '') or ll.isspace()
102 return (ll == '') or ll.isspace()
103
103
104
104
105 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
105 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
106 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
106 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
107
107
108 def last_two_blanks(src):
108 def last_two_blanks(src):
109 """Determine if the input source ends in two blanks.
109 """Determine if the input source ends in two blanks.
110
110
111 A blank is either a newline or a line consisting of whitespace.
111 A blank is either a newline or a line consisting of whitespace.
112
112
113 Parameters
113 Parameters
114 ----------
114 ----------
115 src : string
115 src : string
116 A single or multiline string.
116 A single or multiline string.
117 """
117 """
118 if not src: return False
118 if not src: return False
119 # The logic here is tricky: I couldn't get a regexp to work and pass all
119 # The logic here is tricky: I couldn't get a regexp to work and pass all
120 # the tests, so I took a different approach: split the source by lines,
120 # the tests, so I took a different approach: split the source by lines,
121 # grab the last two and prepend '###\n' as a stand-in for whatever was in
121 # grab the last two and prepend '###\n' as a stand-in for whatever was in
122 # the body before the last two lines. Then, with that structure, it's
122 # the body before the last two lines. Then, with that structure, it's
123 # possible to analyze with two regexps. Not the most elegant solution, but
123 # possible to analyze with two regexps. Not the most elegant solution, but
124 # it works. If anyone tries to change this logic, make sure to validate
124 # it works. If anyone tries to change this logic, make sure to validate
125 # the whole test suite first!
125 # the whole test suite first!
126 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
126 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
127 return (bool(last_two_blanks_re.match(new_src)) or
127 return (bool(last_two_blanks_re.match(new_src)) or
128 bool(last_two_blanks_re2.match(new_src)) )
128 bool(last_two_blanks_re2.match(new_src)) )
129
129
130
130
131 def remove_comments(src):
131 def remove_comments(src):
132 """Remove all comments from input source.
132 """Remove all comments from input source.
133
133
134 Note: comments are NOT recognized inside of strings!
134 Note: comments are NOT recognized inside of strings!
135
135
136 Parameters
136 Parameters
137 ----------
137 ----------
138 src : string
138 src : string
139 A single or multiline input string.
139 A single or multiline input string.
140
140
141 Returns
141 Returns
142 -------
142 -------
143 String with all Python comments removed.
143 String with all Python comments removed.
144 """
144 """
145
145
146 return re.sub('#.*', '', src)
146 return re.sub('#.*', '', src)
147
147
148
148
149 def get_input_encoding():
149 def get_input_encoding():
150 """Return the default standard input encoding.
150 """Return the default standard input encoding.
151
151
152 If sys.stdin has no encoding, 'ascii' is returned."""
152 If sys.stdin has no encoding, 'ascii' is returned."""
153 # There are strange environments for which sys.stdin.encoding is None. We
153 # There are strange environments for which sys.stdin.encoding is None. We
154 # ensure that a valid encoding is returned.
154 # ensure that a valid encoding is returned.
155 encoding = getattr(sys.stdin, 'encoding', None)
155 encoding = getattr(sys.stdin, 'encoding', None)
156 if encoding is None:
156 if encoding is None:
157 encoding = 'ascii'
157 encoding = 'ascii'
158 return encoding
158 return encoding
159
159
160 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
161 # Classes and functions for normal Python syntax handling
161 # Classes and functions for normal Python syntax handling
162 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
163
163
164 class InputSplitter(object):
164 class InputSplitter(object):
165 r"""An object that can accumulate lines of Python source before execution.
165 r"""An object that can accumulate lines of Python source before execution.
166
166
167 This object is designed to be fed python source line-by-line, using
167 This object is designed to be fed python source line-by-line, using
168 :meth:`push`. It will return on each push whether the currently pushed
168 :meth:`push`. It will return on each push whether the currently pushed
169 code could be executed already. In addition, it provides a method called
169 code could be executed already. In addition, it provides a method called
170 :meth:`push_accepts_more` that can be used to query whether more input
170 :meth:`push_accepts_more` that can be used to query whether more input
171 can be pushed into a single interactive block.
171 can be pushed into a single interactive block.
172
172
173 This is a simple example of how an interactive terminal-based client can use
173 This is a simple example of how an interactive terminal-based client can use
174 this tool::
174 this tool::
175
175
176 isp = InputSplitter()
176 isp = InputSplitter()
177 while isp.push_accepts_more():
177 while isp.push_accepts_more():
178 indent = ' '*isp.indent_spaces
178 indent = ' '*isp.indent_spaces
179 prompt = '>>> ' + indent
179 prompt = '>>> ' + indent
180 line = indent + raw_input(prompt)
180 line = indent + raw_input(prompt)
181 isp.push(line)
181 isp.push(line)
182 print 'Input source was:\n', isp.source_reset(),
182 print 'Input source was:\n', isp.source_reset(),
183 """
183 """
184 # Number of spaces of indentation computed from input that has been pushed
184 # Number of spaces of indentation computed from input that has been pushed
185 # so far. This is the attributes callers should query to get the current
185 # so far. This is the attributes callers should query to get the current
186 # indentation level, in order to provide auto-indent facilities.
186 # indentation level, in order to provide auto-indent facilities.
187 indent_spaces = 0
187 indent_spaces = 0
188 # String, indicating the default input encoding. It is computed by default
188 # String, indicating the default input encoding. It is computed by default
189 # at initialization time via get_input_encoding(), but it can be reset by a
189 # at initialization time via get_input_encoding(), but it can be reset by a
190 # client with specific knowledge of the encoding.
190 # client with specific knowledge of the encoding.
191 encoding = ''
191 encoding = ''
192 # String where the current full source input is stored, properly encoded.
192 # String where the current full source input is stored, properly encoded.
193 # Reading this attribute is the normal way of querying the currently pushed
193 # Reading this attribute is the normal way of querying the currently pushed
194 # source code, that has been properly encoded.
194 # source code, that has been properly encoded.
195 source = ''
195 source = ''
196 # Code object corresponding to the current source. It is automatically
196 # Code object corresponding to the current source. It is automatically
197 # synced to the source, so it can be queried at any time to obtain the code
197 # synced to the source, so it can be queried at any time to obtain the code
198 # object; it will be None if the source doesn't compile to valid Python.
198 # object; it will be None if the source doesn't compile to valid Python.
199 code = None
199 code = None
200
200
201 # Private attributes
201 # Private attributes
202
202
203 # List with lines of input accumulated so far
203 # List with lines of input accumulated so far
204 _buffer = None
204 _buffer = None
205 # Command compiler
205 # Command compiler
206 _compile = None
206 _compile = None
207 # Mark when input has changed indentation all the way back to flush-left
207 # Mark when input has changed indentation all the way back to flush-left
208 _full_dedent = False
208 _full_dedent = False
209 # Boolean indicating whether the current block is complete
209 # Boolean indicating whether the current block is complete
210 _is_complete = None
210 _is_complete = None
211
211
212 def __init__(self):
212 def __init__(self):
213 """Create a new InputSplitter instance.
213 """Create a new InputSplitter instance.
214 """
214 """
215 self._buffer = []
215 self._buffer = []
216 self._compile = codeop.CommandCompiler()
216 self._compile = codeop.CommandCompiler()
217 self.encoding = get_input_encoding()
217 self.encoding = get_input_encoding()
218
218
219 def reset(self):
219 def reset(self):
220 """Reset the input buffer and associated state."""
220 """Reset the input buffer and associated state."""
221 self.indent_spaces = 0
221 self.indent_spaces = 0
222 self._buffer[:] = []
222 self._buffer[:] = []
223 self.source = ''
223 self.source = ''
224 self.code = None
224 self.code = None
225 self._is_complete = False
225 self._is_complete = False
226 self._full_dedent = False
226 self._full_dedent = False
227
227
228 def source_reset(self):
228 def source_reset(self):
229 """Return the input source and perform a full reset.
229 """Return the input source and perform a full reset.
230 """
230 """
231 out = self.source
231 out = self.source
232 self.reset()
232 self.reset()
233 return out
233 return out
234
234
235 def push(self, lines):
235 def push(self, lines):
236 """Push one or more lines of input.
236 """Push one or more lines of input.
237
237
238 This stores the given lines and returns a status code indicating
238 This stores the given lines and returns a status code indicating
239 whether the code forms a complete Python block or not.
239 whether the code forms a complete Python block or not.
240
240
241 Any exceptions generated in compilation are swallowed, but if an
241 Any exceptions generated in compilation are swallowed, but if an
242 exception was produced, the method returns True.
242 exception was produced, the method returns True.
243
243
244 Parameters
244 Parameters
245 ----------
245 ----------
246 lines : string
246 lines : string
247 One or more lines of Python input.
247 One or more lines of Python input.
248
248
249 Returns
249 Returns
250 -------
250 -------
251 is_complete : boolean
251 is_complete : boolean
252 True if the current input source (the result of the current input
252 True if the current input source (the result of the current input
253 plus prior inputs) forms a complete Python execution block. Note that
253 plus prior inputs) forms a complete Python execution block. Note that
254 this value is also stored as a private attribute (``_is_complete``), so it
254 this value is also stored as a private attribute (``_is_complete``), so it
255 can be queried at any time.
255 can be queried at any time.
256 """
256 """
257 self._store(lines)
257 self._store(lines)
258 source = self.source
258 source = self.source
259
259
260 # Before calling _compile(), reset the code object to None so that if an
260 # Before calling _compile(), reset the code object to None so that if an
261 # exception is raised in compilation, we don't mislead by having
261 # exception is raised in compilation, we don't mislead by having
262 # inconsistent code/source attributes.
262 # inconsistent code/source attributes.
263 self.code, self._is_complete = None, None
263 self.code, self._is_complete = None, None
264
264
265 # Honor termination lines properly
265 # Honor termination lines properly
266 if source.endswith('\\\n'):
266 if source.endswith('\\\n'):
267 return False
267 return False
268
268
269 self._update_indent(lines)
269 self._update_indent(lines)
270 try:
270 try:
271 self.code = self._compile(source, symbol="exec")
271 self.code = self._compile(source, symbol="exec")
272 # Invalid syntax can produce any of a number of different errors from
272 # Invalid syntax can produce any of a number of different errors from
273 # inside the compiler, so we have to catch them all. Syntax errors
273 # inside the compiler, so we have to catch them all. Syntax errors
274 # immediately produce a 'ready' block, so the invalid Python can be
274 # immediately produce a 'ready' block, so the invalid Python can be
275 # sent to the kernel for evaluation with possible ipython
275 # sent to the kernel for evaluation with possible ipython
276 # special-syntax conversion.
276 # special-syntax conversion.
277 except (SyntaxError, OverflowError, ValueError, TypeError,
277 except (SyntaxError, OverflowError, ValueError, TypeError,
278 MemoryError):
278 MemoryError):
279 self._is_complete = True
279 self._is_complete = True
280 else:
280 else:
281 # Compilation didn't produce any exceptions (though it may not have
281 # Compilation didn't produce any exceptions (though it may not have
282 # given a complete code object)
282 # given a complete code object)
283 self._is_complete = self.code is not None
283 self._is_complete = self.code is not None
284
284
285 return self._is_complete
285 return self._is_complete
286
286
287 def push_accepts_more(self):
287 def push_accepts_more(self):
288 """Return whether a block of interactive input can accept more input.
288 """Return whether a block of interactive input can accept more input.
289
289
290 This method is meant to be used by line-oriented frontends, who need to
290 This method is meant to be used by line-oriented frontends, who need to
291 guess whether a block is complete or not based solely on prior and
291 guess whether a block is complete or not based solely on prior and
292 current input lines. The InputSplitter considers it has a complete
292 current input lines. The InputSplitter considers it has a complete
293 interactive block and will not accept more input when either:
293 interactive block and will not accept more input when either:
294
294
295 * A SyntaxError is raised
295 * A SyntaxError is raised
296
296
297 * The code is complete and consists of a single line or a single
297 * The code is complete and consists of a single line or a single
298 non-compound statement
298 non-compound statement
299
299
300 * The code is complete and has a blank line at the end
300 * The code is complete and has a blank line at the end
301
301
302 If the current input produces a syntax error, this method immediately
302 If the current input produces a syntax error, this method immediately
303 returns False but does *not* raise the syntax error exception, as
303 returns False but does *not* raise the syntax error exception, as
304 typically clients will want to send invalid syntax to an execution
304 typically clients will want to send invalid syntax to an execution
305 backend which might convert the invalid syntax into valid Python via
305 backend which might convert the invalid syntax into valid Python via
306 one of the dynamic IPython mechanisms.
306 one of the dynamic IPython mechanisms.
307 """
307 """
308
308
309 # With incomplete input, unconditionally accept more
309 # With incomplete input, unconditionally accept more
310 # A syntax error also sets _is_complete to True - see push()
310 # A syntax error also sets _is_complete to True - see push()
311 if not self._is_complete:
311 if not self._is_complete:
312 #print("Not complete") # debug
312 #print("Not complete") # debug
313 return True
313 return True
314
314
315 # The user can make any (complete) input execute by leaving a blank line
315 # The user can make any (complete) input execute by leaving a blank line
316 last_line = self.source.splitlines()[-1]
316 last_line = self.source.splitlines()[-1]
317 if (not last_line) or last_line.isspace():
317 if (not last_line) or last_line.isspace():
318 #print("Blank line") # debug
318 #print("Blank line") # debug
319 return False
319 return False
320
320
321 # If there's just a single line or AST node, and we're flush left, as is
321 # If there's just a single line or AST node, and we're flush left, as is
322 # the case after a simple statement such as 'a=1', we want to execute it
322 # the case after a simple statement such as 'a=1', we want to execute it
323 # straight away.
323 # straight away.
324 if self.indent_spaces==0:
324 if self.indent_spaces==0:
325 if len(self.source.splitlines()) <= 1:
325 if len(self.source.splitlines()) <= 1:
326 return False
326 return False
327
327
328 try:
328 try:
329 code_ast = ast.parse(u''.join(self._buffer))
329 code_ast = ast.parse(u''.join(self._buffer))
330 except Exception:
330 except Exception:
331 #print("Can't parse AST") # debug
331 #print("Can't parse AST") # debug
332 return False
332 return False
333 else:
333 else:
334 if len(code_ast.body) == 1 and \
334 if len(code_ast.body) == 1 and \
335 not hasattr(code_ast.body[0], 'body'):
335 not hasattr(code_ast.body[0], 'body'):
336 #print("Simple statement") # debug
336 #print("Simple statement") # debug
337 return False
337 return False
338
338
339 # General fallback - accept more code
339 # General fallback - accept more code
340 return True
340 return True
341
341
342 #------------------------------------------------------------------------
342 #------------------------------------------------------------------------
343 # Private interface
343 # Private interface
344 #------------------------------------------------------------------------
344 #------------------------------------------------------------------------
345
345
346 def _find_indent(self, line):
346 def _find_indent(self, line):
347 """Compute the new indentation level for a single line.
347 """Compute the new indentation level for a single line.
348
348
349 Parameters
349 Parameters
350 ----------
350 ----------
351 line : str
351 line : str
352 A single new line of non-whitespace, non-comment Python input.
352 A single new line of non-whitespace, non-comment Python input.
353
353
354 Returns
354 Returns
355 -------
355 -------
356 indent_spaces : int
356 indent_spaces : int
357 New value for the indent level (it may be equal to self.indent_spaces
357 New value for the indent level (it may be equal to self.indent_spaces
358 if indentation doesn't change.
358 if indentation doesn't change.
359
359
360 full_dedent : boolean
360 full_dedent : boolean
361 Whether the new line causes a full flush-left dedent.
361 Whether the new line causes a full flush-left dedent.
362 """
362 """
363 indent_spaces = self.indent_spaces
363 indent_spaces = self.indent_spaces
364 full_dedent = self._full_dedent
364 full_dedent = self._full_dedent
365
365
366 inisp = num_ini_spaces(line)
366 inisp = num_ini_spaces(line)
367 if inisp < indent_spaces:
367 if inisp < indent_spaces:
368 indent_spaces = inisp
368 indent_spaces = inisp
369 if indent_spaces <= 0:
369 if indent_spaces <= 0:
370 #print 'Full dedent in text',self.source # dbg
370 #print 'Full dedent in text',self.source # dbg
371 full_dedent = True
371 full_dedent = True
372
372
373 if line.rstrip()[-1] == ':':
373 if line.rstrip()[-1] == ':':
374 indent_spaces += 4
374 indent_spaces += 4
375 elif dedent_re.match(line):
375 elif dedent_re.match(line):
376 indent_spaces -= 4
376 indent_spaces -= 4
377 if indent_spaces <= 0:
377 if indent_spaces <= 0:
378 full_dedent = True
378 full_dedent = True
379
379
380 # Safety
380 # Safety
381 if indent_spaces < 0:
381 if indent_spaces < 0:
382 indent_spaces = 0
382 indent_spaces = 0
383 #print 'safety' # dbg
383 #print 'safety' # dbg
384
384
385 return indent_spaces, full_dedent
385 return indent_spaces, full_dedent
386
386
387 def _update_indent(self, lines):
387 def _update_indent(self, lines):
388 for line in remove_comments(lines).splitlines():
388 for line in remove_comments(lines).splitlines():
389 if line and not line.isspace():
389 if line and not line.isspace():
390 self.indent_spaces, self._full_dedent = self._find_indent(line)
390 self.indent_spaces, self._full_dedent = self._find_indent(line)
391
391
392 def _store(self, lines, buffer=None, store='source'):
392 def _store(self, lines, buffer=None, store='source'):
393 """Store one or more lines of input.
393 """Store one or more lines of input.
394
394
395 If input lines are not newline-terminated, a newline is automatically
395 If input lines are not newline-terminated, a newline is automatically
396 appended."""
396 appended."""
397
397
398 if buffer is None:
398 if buffer is None:
399 buffer = self._buffer
399 buffer = self._buffer
400
400
401 if lines.endswith('\n'):
401 if lines.endswith('\n'):
402 buffer.append(lines)
402 buffer.append(lines)
403 else:
403 else:
404 buffer.append(lines+'\n')
404 buffer.append(lines+'\n')
405 setattr(self, store, self._set_source(buffer))
405 setattr(self, store, self._set_source(buffer))
406
406
407 def _set_source(self, buffer):
407 def _set_source(self, buffer):
408 return u''.join(buffer)
408 return u''.join(buffer)
409
409
410
410
411 class IPythonInputSplitter(InputSplitter):
411 class IPythonInputSplitter(InputSplitter):
412 """An input splitter that recognizes all of IPython's special syntax."""
412 """An input splitter that recognizes all of IPython's special syntax."""
413
413
414 # String with raw, untransformed input.
414 # String with raw, untransformed input.
415 source_raw = ''
415 source_raw = ''
416
416
417 # Flag to track when a transformer has stored input that it hasn't given
417 # Flag to track when a transformer has stored input that it hasn't given
418 # back yet.
418 # back yet.
419 transformer_accumulating = False
419 transformer_accumulating = False
420
420
421 # Flag to track when assemble_python_lines has stored input that it hasn't
421 # Flag to track when assemble_python_lines has stored input that it hasn't
422 # given back yet.
422 # given back yet.
423 within_python_line = False
423 within_python_line = False
424
424
425 # Private attributes
425 # Private attributes
426
426
427 # List with lines of raw input accumulated so far.
427 # List with lines of raw input accumulated so far.
428 _buffer_raw = None
428 _buffer_raw = None
429
429
430 def __init__(self, line_input_checker=True, physical_line_transforms=None,
430 def __init__(self, line_input_checker=True, physical_line_transforms=None,
431 logical_line_transforms=None, python_line_transforms=None):
431 logical_line_transforms=None, python_line_transforms=None):
432 super(IPythonInputSplitter, self).__init__()
432 super(IPythonInputSplitter, self).__init__()
433 self._buffer_raw = []
433 self._buffer_raw = []
434 self._validate = True
434 self._validate = True
435
435
436 if physical_line_transforms is not None:
436 if physical_line_transforms is not None:
437 self.physical_line_transforms = physical_line_transforms
437 self.physical_line_transforms = physical_line_transforms
438 else:
438 else:
439 self.physical_line_transforms = [
439 self.physical_line_transforms = [
440 leading_indent(),
440 leading_indent(),
441 classic_prompt(),
441 classic_prompt(),
442 ipy_prompt(),
442 ipy_prompt(),
443 strip_encoding_cookie(),
443 strip_encoding_cookie(),
444 cellmagic(end_on_blank_line=line_input_checker),
444 cellmagic(end_on_blank_line=line_input_checker),
445 ]
445 ]
446
446
447 self.assemble_logical_lines = assemble_logical_lines()
447 self.assemble_logical_lines = assemble_logical_lines()
448 if logical_line_transforms is not None:
448 if logical_line_transforms is not None:
449 self.logical_line_transforms = logical_line_transforms
449 self.logical_line_transforms = logical_line_transforms
450 else:
450 else:
451 self.logical_line_transforms = [
451 self.logical_line_transforms = [
452 help_end(),
452 help_end(),
453 escaped_commands(),
453 escaped_commands(),
454 assign_from_magic(),
454 assign_from_magic(),
455 assign_from_system(),
455 assign_from_system(),
456 ]
456 ]
457
457
458 self.assemble_python_lines = assemble_python_lines()
458 self.assemble_python_lines = assemble_python_lines()
459 if python_line_transforms is not None:
459 if python_line_transforms is not None:
460 self.python_line_transforms = python_line_transforms
460 self.python_line_transforms = python_line_transforms
461 else:
461 else:
462 # We don't use any of these at present
462 # We don't use any of these at present
463 self.python_line_transforms = []
463 self.python_line_transforms = []
464
464
465 @property
465 @property
466 def transforms(self):
466 def transforms(self):
467 "Quick access to all transformers."
467 "Quick access to all transformers."
468 return self.physical_line_transforms + \
468 return self.physical_line_transforms + \
469 [self.assemble_logical_lines] + self.logical_line_transforms + \
469 [self.assemble_logical_lines] + self.logical_line_transforms + \
470 [self.assemble_python_lines] + self.python_line_transforms
470 [self.assemble_python_lines] + self.python_line_transforms
471
471
472 @property
472 @property
473 def transforms_in_use(self):
473 def transforms_in_use(self):
474 """Transformers, excluding logical line transformers if we're in a
474 """Transformers, excluding logical line transformers if we're in a
475 Python line."""
475 Python line."""
476 t = self.physical_line_transforms[:]
476 t = self.physical_line_transforms[:]
477 if not self.within_python_line:
477 if not self.within_python_line:
478 t += [self.assemble_logical_lines] + self.logical_line_transforms
478 t += [self.assemble_logical_lines] + self.logical_line_transforms
479 return t + [self.assemble_python_lines] + self.python_line_transforms
479 return t + [self.assemble_python_lines] + self.python_line_transforms
480
480
481 def reset(self):
481 def reset(self):
482 """Reset the input buffer and associated state."""
482 """Reset the input buffer and associated state."""
483 super(IPythonInputSplitter, self).reset()
483 super(IPythonInputSplitter, self).reset()
484 self._buffer_raw[:] = []
484 self._buffer_raw[:] = []
485 self.source_raw = ''
485 self.source_raw = ''
486 self.transformer_accumulating = False
486 self.transformer_accumulating = False
487 self.within_python_line = False
487 self.within_python_line = False
488
488
489 for t in self.transforms:
489 for t in self.transforms:
490 try:
490 try:
491 t.reset()
491 t.reset()
492 except SyntaxError:
492 except SyntaxError:
493 # Nothing that calls reset() expects to handle transformer
493 # Nothing that calls reset() expects to handle transformer
494 # errors
494 # errors
495 pass
495 pass
496
496
497 def flush_transformers(self):
497 def flush_transformers(self):
498 def _flush(transform, out):
498 def _flush(transform, outs):
499 """yield transformed lines
499 """yield transformed lines
500
500
501 always strings, never None
501 always strings, never None
502
502
503 transform: the current transform
503 transform: the current transform
504 out: an iterable of previously transformed inputs.
504 outs: an iterable of previously transformed inputs.
505 Each may be multiline, which will be passed
505 Each may be multiline, which will be passed
506 one line at a time to transform.
506 one line at a time to transform.
507 """
507 """
508 anything = False
508 for out in outs:
509 for out in out:
510 anything = True
511 tmp = None
512 for line in out.splitlines():
509 for line in out.splitlines():
513 # push one line at a time
510 # push one line at a time
514 tmp = transform.push(line)
511 tmp = transform.push(line)
515 if tmp is not None:
512 if tmp is not None:
516 yield tmp
513 yield tmp
517 if tmp is None:
514
518 # transformer is still consuming, reset
515 # reset the transform
519 tmp = transform.reset()
516 tmp = transform.reset()
520 if tmp is not None:
517 if tmp is not None:
521 yield tmp
518 yield tmp
522 if not anything:
523 # nothing was pushed, reset
524 tmp = transform.reset()
525 if tmp is not None:
526 yield tmp
527
519
528 out = []
520 out = []
529 for t in self.transforms_in_use:
521 for t in self.transforms_in_use:
530 out = _flush(t, out)
522 out = _flush(t, out)
531
523
532 out = list(out)
524 out = list(out)
533 if out:
525 if out:
534 self._store('\n'.join(out))
526 self._store('\n'.join(out))
535
527
536 def raw_reset(self):
528 def raw_reset(self):
537 """Return raw input only and perform a full reset.
529 """Return raw input only and perform a full reset.
538 """
530 """
539 out = self.source_raw
531 out = self.source_raw
540 self.reset()
532 self.reset()
541 return out
533 return out
542
534
543 def source_reset(self):
535 def source_reset(self):
544 try:
536 try:
545 self.flush_transformers()
537 self.flush_transformers()
546 return self.source
538 return self.source
547 finally:
539 finally:
548 self.reset()
540 self.reset()
549
541
550 def push_accepts_more(self):
542 def push_accepts_more(self):
551 if self.transformer_accumulating:
543 if self.transformer_accumulating:
552 return True
544 return True
553 else:
545 else:
554 return super(IPythonInputSplitter, self).push_accepts_more()
546 return super(IPythonInputSplitter, self).push_accepts_more()
555
547
556 def transform_cell(self, cell):
548 def transform_cell(self, cell):
557 """Process and translate a cell of input.
549 """Process and translate a cell of input.
558 """
550 """
559 self.reset()
551 self.reset()
560 try:
552 try:
561 self.push(cell)
553 self.push(cell)
562 self.flush_transformers()
554 self.flush_transformers()
563 return self.source
555 return self.source
564 finally:
556 finally:
565 self.reset()
557 self.reset()
566
558
567 def push(self, lines):
559 def push(self, lines):
568 """Push one or more lines of IPython input.
560 """Push one or more lines of IPython input.
569
561
570 This stores the given lines and returns a status code indicating
562 This stores the given lines and returns a status code indicating
571 whether the code forms a complete Python block or not, after processing
563 whether the code forms a complete Python block or not, after processing
572 all input lines for special IPython syntax.
564 all input lines for special IPython syntax.
573
565
574 Any exceptions generated in compilation are swallowed, but if an
566 Any exceptions generated in compilation are swallowed, but if an
575 exception was produced, the method returns True.
567 exception was produced, the method returns True.
576
568
577 Parameters
569 Parameters
578 ----------
570 ----------
579 lines : string
571 lines : string
580 One or more lines of Python input.
572 One or more lines of Python input.
581
573
582 Returns
574 Returns
583 -------
575 -------
584 is_complete : boolean
576 is_complete : boolean
585 True if the current input source (the result of the current input
577 True if the current input source (the result of the current input
586 plus prior inputs) forms a complete Python execution block. Note that
578 plus prior inputs) forms a complete Python execution block. Note that
587 this value is also stored as a private attribute (_is_complete), so it
579 this value is also stored as a private attribute (_is_complete), so it
588 can be queried at any time.
580 can be queried at any time.
589 """
581 """
590
582
591 # We must ensure all input is pure unicode
583 # We must ensure all input is pure unicode
592 lines = cast_unicode(lines, self.encoding)
584 lines = cast_unicode(lines, self.encoding)
593
585
594 # ''.splitlines() --> [], but we need to push the empty line to transformers
586 # ''.splitlines() --> [], but we need to push the empty line to transformers
595 lines_list = lines.splitlines()
587 lines_list = lines.splitlines()
596 if not lines_list:
588 if not lines_list:
597 lines_list = ['']
589 lines_list = ['']
598
590
599 # Store raw source before applying any transformations to it. Note
591 # Store raw source before applying any transformations to it. Note
600 # that this must be done *after* the reset() call that would otherwise
592 # that this must be done *after* the reset() call that would otherwise
601 # flush the buffer.
593 # flush the buffer.
602 self._store(lines, self._buffer_raw, 'source_raw')
594 self._store(lines, self._buffer_raw, 'source_raw')
603
595
604 for line in lines_list:
596 for line in lines_list:
605 out = self.push_line(line)
597 out = self.push_line(line)
606
598
607 return out
599 return out
608
600
609 def push_line(self, line):
601 def push_line(self, line):
610 buf = self._buffer
602 buf = self._buffer
611
603
612 def _accumulating(dbg):
604 def _accumulating(dbg):
613 #print(dbg)
605 #print(dbg)
614 self.transformer_accumulating = True
606 self.transformer_accumulating = True
615 return False
607 return False
616
608
617 for transformer in self.physical_line_transforms:
609 for transformer in self.physical_line_transforms:
618 line = transformer.push(line)
610 line = transformer.push(line)
619 if line is None:
611 if line is None:
620 return _accumulating(transformer)
612 return _accumulating(transformer)
621
613
622 if not self.within_python_line:
614 if not self.within_python_line:
623 line = self.assemble_logical_lines.push(line)
615 line = self.assemble_logical_lines.push(line)
624 if line is None:
616 if line is None:
625 return _accumulating('acc logical line')
617 return _accumulating('acc logical line')
626
618
627 for transformer in self.logical_line_transforms:
619 for transformer in self.logical_line_transforms:
628 line = transformer.push(line)
620 line = transformer.push(line)
629 if line is None:
621 if line is None:
630 return _accumulating(transformer)
622 return _accumulating(transformer)
631
623
632 line = self.assemble_python_lines.push(line)
624 line = self.assemble_python_lines.push(line)
633 if line is None:
625 if line is None:
634 self.within_python_line = True
626 self.within_python_line = True
635 return _accumulating('acc python line')
627 return _accumulating('acc python line')
636 else:
628 else:
637 self.within_python_line = False
629 self.within_python_line = False
638
630
639 for transformer in self.python_line_transforms:
631 for transformer in self.python_line_transforms:
640 line = transformer.push(line)
632 line = transformer.push(line)
641 if line is None:
633 if line is None:
642 return _accumulating(transformer)
634 return _accumulating(transformer)
643
635
644 #print("transformers clear") #debug
636 #print("transformers clear") #debug
645 self.transformer_accumulating = False
637 self.transformer_accumulating = False
646 return super(IPythonInputSplitter, self).push(line)
638 return super(IPythonInputSplitter, self).push(line)
General Comments 0
You need to be logged in to leave comments. Login now