##// END OF EJS Templates
Clarify comment per @willingc's suggestion
Thomas Kluyver -
Show More
@@ -1,759 +1,761 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 io
21 import io
22 import re
22 import re
23 import sys
23 import sys
24 import tokenize
24 import tokenize
25 import warnings
25 import warnings
26
26
27 from IPython.utils.py3compat import cast_unicode
27 from IPython.utils.py3compat import cast_unicode
28 from IPython.core.inputtransformer import (leading_indent,
28 from IPython.core.inputtransformer import (leading_indent,
29 classic_prompt,
29 classic_prompt,
30 ipy_prompt,
30 ipy_prompt,
31 cellmagic,
31 cellmagic,
32 assemble_logical_lines,
32 assemble_logical_lines,
33 help_end,
33 help_end,
34 escaped_commands,
34 escaped_commands,
35 assign_from_magic,
35 assign_from_magic,
36 assign_from_system,
36 assign_from_system,
37 assemble_python_lines,
37 assemble_python_lines,
38 )
38 )
39
39
40 # These are available in this module for backwards compatibility.
40 # These are available in this module for backwards compatibility.
41 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
41 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Utilities
46 # Utilities
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 # FIXME: These are general-purpose utilities that later can be moved to the
49 # FIXME: These are general-purpose utilities that later can be moved to the
50 # general ward. Kept here for now because we're being very strict about test
50 # general ward. Kept here for now because we're being very strict about test
51 # coverage with this code, and this lets us ensure that we keep 100% coverage
51 # coverage with this code, and this lets us ensure that we keep 100% coverage
52 # while developing.
52 # while developing.
53
53
54 # compiled regexps for autoindent management
54 # compiled regexps for autoindent management
55 dedent_re = re.compile('|'.join([
55 dedent_re = re.compile('|'.join([
56 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
56 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
57 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
57 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
58 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
58 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
59 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
59 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
60 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
60 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
61 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
61 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
62 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
62 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
63 ]))
63 ]))
64 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
64 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
65
65
66 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
66 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
67 # before pure comments
67 # before pure comments
68 comment_line_re = re.compile('^\s*\#')
68 comment_line_re = re.compile('^\s*\#')
69
69
70
70
71 def num_ini_spaces(s):
71 def num_ini_spaces(s):
72 """Return the number of initial spaces in a string.
72 """Return the number of initial spaces in a string.
73
73
74 Note that tabs are counted as a single space. For now, we do *not* support
74 Note that tabs are counted as a single space. For now, we do *not* support
75 mixing of tabs and spaces in the user's input.
75 mixing of tabs and spaces in the user's input.
76
76
77 Parameters
77 Parameters
78 ----------
78 ----------
79 s : string
79 s : string
80
80
81 Returns
81 Returns
82 -------
82 -------
83 n : int
83 n : int
84 """
84 """
85
85
86 ini_spaces = ini_spaces_re.match(s)
86 ini_spaces = ini_spaces_re.match(s)
87 if ini_spaces:
87 if ini_spaces:
88 return ini_spaces.end()
88 return ini_spaces.end()
89 else:
89 else:
90 return 0
90 return 0
91
91
92 # Fake token types for partial_tokenize:
92 # Fake token types for partial_tokenize:
93 INCOMPLETE_STRING = tokenize.N_TOKENS
93 INCOMPLETE_STRING = tokenize.N_TOKENS
94 IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1
94 IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1
95
95
96 # The 2 classes below have the same API as TokenInfo, but don't try to look up
96 # The 2 classes below have the same API as TokenInfo, but don't try to look up
97 # a token type name that they won't find.
97 # a token type name that they won't find.
98 class IncompleteString:
98 class IncompleteString:
99 type = exact_type = INCOMPLETE_STRING
99 type = exact_type = INCOMPLETE_STRING
100 def __init__(self, s, start, end, line):
100 def __init__(self, s, start, end, line):
101 self.s = s
101 self.s = s
102 self.start = start
102 self.start = start
103 self.end = end
103 self.end = end
104 self.line = line
104 self.line = line
105
105
106 class InMultilineStatement:
106 class InMultilineStatement:
107 type = exact_type = IN_MULTILINE_STATEMENT
107 type = exact_type = IN_MULTILINE_STATEMENT
108 def __init__(self, pos, line):
108 def __init__(self, pos, line):
109 self.s = ''
109 self.s = ''
110 self.start = self.end = pos
110 self.start = self.end = pos
111 self.line = line
111 self.line = line
112
112
113 def partial_tokens(s):
113 def partial_tokens(s):
114 """Iterate over tokens from a possibly-incomplete string of code.
114 """Iterate over tokens from a possibly-incomplete string of code.
115
115
116 This adds two special token types: INCOMPLETE_STRING and
116 This adds two special token types: INCOMPLETE_STRING and
117 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and
117 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and
118 represent the two main ways for code to be incomplete.
118 represent the two main ways for code to be incomplete.
119 """
119 """
120 readline = io.StringIO(s).readline
120 readline = io.StringIO(s).readline
121 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
121 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
122 try:
122 try:
123 for token in tokenize.generate_tokens(readline):
123 for token in tokenize.generate_tokens(readline):
124 yield token
124 yield token
125 except tokenize.TokenError as e:
125 except tokenize.TokenError as e:
126 # catch EOF error
126 # catch EOF error
127 lines = s.splitlines(keepends=True)
127 lines = s.splitlines(keepends=True)
128 end = len(lines), len(lines[-1])
128 end = len(lines), len(lines[-1])
129 if 'multi-line string' in e.args[0]:
129 if 'multi-line string' in e.args[0]:
130 l, c = start = token.end
130 l, c = start = token.end
131 s = lines[l-1][c:] + ''.join(lines[l:])
131 s = lines[l-1][c:] + ''.join(lines[l:])
132 yield IncompleteString(s, start, end, lines[-1])
132 yield IncompleteString(s, start, end, lines[-1])
133 elif 'multi-line statement' in e.args[0]:
133 elif 'multi-line statement' in e.args[0]:
134 yield InMultilineStatement(end, lines[-1])
134 yield InMultilineStatement(end, lines[-1])
135 else:
135 else:
136 raise
136 raise
137
137
138 def find_next_indent(code):
138 def find_next_indent(code):
139 """Find the number of spaces for the next line of indentation"""
139 """Find the number of spaces for the next line of indentation"""
140 tokens = list(partial_tokens(code))
140 tokens = list(partial_tokens(code))
141 if tokens[-1].type == tokenize.ENDMARKER:
141 if tokens[-1].type == tokenize.ENDMARKER:
142 tokens.pop()
142 tokens.pop()
143 if not tokens:
143 if not tokens:
144 return 0
144 return 0
145 while (tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}):
145 while (tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}):
146 tokens.pop()
146 tokens.pop()
147
147
148 if tokens[-1].type == INCOMPLETE_STRING:
148 if tokens[-1].type == INCOMPLETE_STRING:
149 # Inside a multiline string
149 # Inside a multiline string
150 return 0
150 return 0
151
151
152 # Find the indents used before
152 # Find the indents used before
153 prev_indents = [0]
153 prev_indents = [0]
154 def _add_indent(n):
154 def _add_indent(n):
155 if n != prev_indents[-1]:
155 if n != prev_indents[-1]:
156 prev_indents.append(n)
156 prev_indents.append(n)
157
157
158 tokiter = iter(tokens)
158 tokiter = iter(tokens)
159 for tok in tokiter:
159 for tok in tokiter:
160 if tok.type in {tokenize.INDENT, tokenize.DEDENT}:
160 if tok.type in {tokenize.INDENT, tokenize.DEDENT}:
161 _add_indent(tok.end[1])
161 _add_indent(tok.end[1])
162 elif (tok.type == tokenize.NL):
162 elif (tok.type == tokenize.NL):
163 try:
163 try:
164 _add_indent(next(tokiter).start[1])
164 _add_indent(next(tokiter).start[1])
165 except StopIteration:
165 except StopIteration:
166 break
166 break
167
167
168 last_indent = prev_indents.pop()
168 last_indent = prev_indents.pop()
169
169
170 # If we've just opened a multiline statement (e.g. 'a = ['), indent more
170 # If we've just opened a multiline statement (e.g. 'a = ['), indent more
171 if tokens[-1].type == IN_MULTILINE_STATEMENT:
171 if tokens[-1].type == IN_MULTILINE_STATEMENT:
172 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}:
172 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}:
173 return last_indent + 4
173 return last_indent + 4
174 return last_indent
174 return last_indent
175
175
176 if tokens[-1].exact_type == tokenize.COLON:
176 if tokens[-1].exact_type == tokenize.COLON:
177 # Line ends with colon - indent
177 # Line ends with colon - indent
178 return last_indent + 4
178 return last_indent + 4
179
179
180 if last_indent:
180 if last_indent:
181 # Examine the last line for dedent cues - statements like return or
181 # Examine the last line for dedent cues - statements like return or
182 # raise which normally end a block of code.
182 # raise which normally end a block of code.
183 last_line_starts = 0
183 last_line_starts = 0
184 for i, tok in enumerate(tokens):
184 for i, tok in enumerate(tokens):
185 if tok.type == tokenize.NEWLINE:
185 if tok.type == tokenize.NEWLINE:
186 last_line_starts = i + 1
186 last_line_starts = i + 1
187
187
188 last_line_tokens = tokens[last_line_starts:]
188 last_line_tokens = tokens[last_line_starts:]
189 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME]
189 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME]
190 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}:
190 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}:
191 # Find the most recent indentation less than the current level
191 # Find the most recent indentation less than the current level
192 for indent in reversed(prev_indents):
192 for indent in reversed(prev_indents):
193 if indent < last_indent:
193 if indent < last_indent:
194 return indent
194 return indent
195
195
196 return last_indent
196 return last_indent
197
197
198
198
199 def last_blank(src):
199 def last_blank(src):
200 """Determine if the input source ends in a blank.
200 """Determine if the input source ends in a blank.
201
201
202 A blank is either a newline or a line consisting of whitespace.
202 A blank is either a newline or a line consisting of whitespace.
203
203
204 Parameters
204 Parameters
205 ----------
205 ----------
206 src : string
206 src : string
207 A single or multiline string.
207 A single or multiline string.
208 """
208 """
209 if not src: return False
209 if not src: return False
210 ll = src.splitlines()[-1]
210 ll = src.splitlines()[-1]
211 return (ll == '') or ll.isspace()
211 return (ll == '') or ll.isspace()
212
212
213
213
214 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
214 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
215 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
215 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
216
216
217 def last_two_blanks(src):
217 def last_two_blanks(src):
218 """Determine if the input source ends in two blanks.
218 """Determine if the input source ends in two blanks.
219
219
220 A blank is either a newline or a line consisting of whitespace.
220 A blank is either a newline or a line consisting of whitespace.
221
221
222 Parameters
222 Parameters
223 ----------
223 ----------
224 src : string
224 src : string
225 A single or multiline string.
225 A single or multiline string.
226 """
226 """
227 if not src: return False
227 if not src: return False
228 # The logic here is tricky: I couldn't get a regexp to work and pass all
228 # The logic here is tricky: I couldn't get a regexp to work and pass all
229 # the tests, so I took a different approach: split the source by lines,
229 # the tests, so I took a different approach: split the source by lines,
230 # grab the last two and prepend '###\n' as a stand-in for whatever was in
230 # grab the last two and prepend '###\n' as a stand-in for whatever was in
231 # the body before the last two lines. Then, with that structure, it's
231 # the body before the last two lines. Then, with that structure, it's
232 # possible to analyze with two regexps. Not the most elegant solution, but
232 # possible to analyze with two regexps. Not the most elegant solution, but
233 # it works. If anyone tries to change this logic, make sure to validate
233 # it works. If anyone tries to change this logic, make sure to validate
234 # the whole test suite first!
234 # the whole test suite first!
235 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
235 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
236 return (bool(last_two_blanks_re.match(new_src)) or
236 return (bool(last_two_blanks_re.match(new_src)) or
237 bool(last_two_blanks_re2.match(new_src)) )
237 bool(last_two_blanks_re2.match(new_src)) )
238
238
239
239
240 def remove_comments(src):
240 def remove_comments(src):
241 """Remove all comments from input source.
241 """Remove all comments from input source.
242
242
243 Note: comments are NOT recognized inside of strings!
243 Note: comments are NOT recognized inside of strings!
244
244
245 Parameters
245 Parameters
246 ----------
246 ----------
247 src : string
247 src : string
248 A single or multiline input string.
248 A single or multiline input string.
249
249
250 Returns
250 Returns
251 -------
251 -------
252 String with all Python comments removed.
252 String with all Python comments removed.
253 """
253 """
254
254
255 return re.sub('#.*', '', src)
255 return re.sub('#.*', '', src)
256
256
257
257
258 def get_input_encoding():
258 def get_input_encoding():
259 """Return the default standard input encoding.
259 """Return the default standard input encoding.
260
260
261 If sys.stdin has no encoding, 'ascii' is returned."""
261 If sys.stdin has no encoding, 'ascii' is returned."""
262 # There are strange environments for which sys.stdin.encoding is None. We
262 # There are strange environments for which sys.stdin.encoding is None. We
263 # ensure that a valid encoding is returned.
263 # ensure that a valid encoding is returned.
264 encoding = getattr(sys.stdin, 'encoding', None)
264 encoding = getattr(sys.stdin, 'encoding', None)
265 if encoding is None:
265 if encoding is None:
266 encoding = 'ascii'
266 encoding = 'ascii'
267 return encoding
267 return encoding
268
268
269 #-----------------------------------------------------------------------------
269 #-----------------------------------------------------------------------------
270 # Classes and functions for normal Python syntax handling
270 # Classes and functions for normal Python syntax handling
271 #-----------------------------------------------------------------------------
271 #-----------------------------------------------------------------------------
272
272
273 class InputSplitter(object):
273 class InputSplitter(object):
274 r"""An object that can accumulate lines of Python source before execution.
274 r"""An object that can accumulate lines of Python source before execution.
275
275
276 This object is designed to be fed python source line-by-line, using
276 This object is designed to be fed python source line-by-line, using
277 :meth:`push`. It will return on each push whether the currently pushed
277 :meth:`push`. It will return on each push whether the currently pushed
278 code could be executed already. In addition, it provides a method called
278 code could be executed already. In addition, it provides a method called
279 :meth:`push_accepts_more` that can be used to query whether more input
279 :meth:`push_accepts_more` that can be used to query whether more input
280 can be pushed into a single interactive block.
280 can be pushed into a single interactive block.
281
281
282 This is a simple example of how an interactive terminal-based client can use
282 This is a simple example of how an interactive terminal-based client can use
283 this tool::
283 this tool::
284
284
285 isp = InputSplitter()
285 isp = InputSplitter()
286 while isp.push_accepts_more():
286 while isp.push_accepts_more():
287 indent = ' '*isp.indent_spaces
287 indent = ' '*isp.indent_spaces
288 prompt = '>>> ' + indent
288 prompt = '>>> ' + indent
289 line = indent + raw_input(prompt)
289 line = indent + raw_input(prompt)
290 isp.push(line)
290 isp.push(line)
291 print 'Input source was:\n', isp.source_reset(),
291 print 'Input source was:\n', isp.source_reset(),
292 """
292 """
293 # A cache for calculating the current indentation.
293 # A cache for storing the current indentation
294 # If the first value matches self.source, the second value is an integer
294 # The first value stores the most recently processed source input
295 # number of spaces for the current indentation. If the first value does not
295 # The second value is the number of spaces for the current indentation
296 # match, self.source has changed, and the indentation must be recalculated.
296 # If self.source matches the first value, the second value is a valid
297 # current indentation. Otherwise, the cache is invalid and the indentation
298 # must be recalculated.
297 _indent_spaces_cache = None, None
299 _indent_spaces_cache = None, None
298 # String, indicating the default input encoding. It is computed by default
300 # String, indicating the default input encoding. It is computed by default
299 # at initialization time via get_input_encoding(), but it can be reset by a
301 # at initialization time via get_input_encoding(), but it can be reset by a
300 # client with specific knowledge of the encoding.
302 # client with specific knowledge of the encoding.
301 encoding = ''
303 encoding = ''
302 # String where the current full source input is stored, properly encoded.
304 # String where the current full source input is stored, properly encoded.
303 # Reading this attribute is the normal way of querying the currently pushed
305 # Reading this attribute is the normal way of querying the currently pushed
304 # source code, that has been properly encoded.
306 # source code, that has been properly encoded.
305 source = ''
307 source = ''
306 # Code object corresponding to the current source. It is automatically
308 # Code object corresponding to the current source. It is automatically
307 # synced to the source, so it can be queried at any time to obtain the code
309 # synced to the source, so it can be queried at any time to obtain the code
308 # object; it will be None if the source doesn't compile to valid Python.
310 # object; it will be None if the source doesn't compile to valid Python.
309 code = None
311 code = None
310
312
311 # Private attributes
313 # Private attributes
312
314
313 # List with lines of input accumulated so far
315 # List with lines of input accumulated so far
314 _buffer = None
316 _buffer = None
315 # Command compiler
317 # Command compiler
316 _compile = None
318 _compile = None
317 # Boolean indicating whether the current block is complete
319 # Boolean indicating whether the current block is complete
318 _is_complete = None
320 _is_complete = None
319 # Boolean indicating whether the current block has an unrecoverable syntax error
321 # Boolean indicating whether the current block has an unrecoverable syntax error
320 _is_invalid = False
322 _is_invalid = False
321
323
322 def __init__(self):
324 def __init__(self):
323 """Create a new InputSplitter instance.
325 """Create a new InputSplitter instance.
324 """
326 """
325 self._buffer = []
327 self._buffer = []
326 self._compile = codeop.CommandCompiler()
328 self._compile = codeop.CommandCompiler()
327 self.encoding = get_input_encoding()
329 self.encoding = get_input_encoding()
328
330
329 def reset(self):
331 def reset(self):
330 """Reset the input buffer and associated state."""
332 """Reset the input buffer and associated state."""
331 self._buffer[:] = []
333 self._buffer[:] = []
332 self.source = ''
334 self.source = ''
333 self.code = None
335 self.code = None
334 self._is_complete = False
336 self._is_complete = False
335 self._is_invalid = False
337 self._is_invalid = False
336
338
337 def source_reset(self):
339 def source_reset(self):
338 """Return the input source and perform a full reset.
340 """Return the input source and perform a full reset.
339 """
341 """
340 out = self.source
342 out = self.source
341 self.reset()
343 self.reset()
342 return out
344 return out
343
345
344 def check_complete(self, source):
346 def check_complete(self, source):
345 """Return whether a block of code is ready to execute, or should be continued
347 """Return whether a block of code is ready to execute, or should be continued
346
348
347 This is a non-stateful API, and will reset the state of this InputSplitter.
349 This is a non-stateful API, and will reset the state of this InputSplitter.
348
350
349 Parameters
351 Parameters
350 ----------
352 ----------
351 source : string
353 source : string
352 Python input code, which can be multiline.
354 Python input code, which can be multiline.
353
355
354 Returns
356 Returns
355 -------
357 -------
356 status : str
358 status : str
357 One of 'complete', 'incomplete', or 'invalid' if source is not a
359 One of 'complete', 'incomplete', or 'invalid' if source is not a
358 prefix of valid code.
360 prefix of valid code.
359 indent_spaces : int or None
361 indent_spaces : int or None
360 The number of spaces by which to indent the next line of code. If
362 The number of spaces by which to indent the next line of code. If
361 status is not 'incomplete', this is None.
363 status is not 'incomplete', this is None.
362 """
364 """
363 self.reset()
365 self.reset()
364 try:
366 try:
365 self.push(source)
367 self.push(source)
366 except SyntaxError:
368 except SyntaxError:
367 # Transformers in IPythonInputSplitter can raise SyntaxError,
369 # Transformers in IPythonInputSplitter can raise SyntaxError,
368 # which push() will not catch.
370 # which push() will not catch.
369 return 'invalid', None
371 return 'invalid', None
370 else:
372 else:
371 if self._is_invalid:
373 if self._is_invalid:
372 return 'invalid', None
374 return 'invalid', None
373 elif self.push_accepts_more():
375 elif self.push_accepts_more():
374 return 'incomplete', self.get_indent_spaces()
376 return 'incomplete', self.get_indent_spaces()
375 else:
377 else:
376 return 'complete', None
378 return 'complete', None
377 finally:
379 finally:
378 self.reset()
380 self.reset()
379
381
380 def push(self, lines):
382 def push(self, lines):
381 """Push one or more lines of input.
383 """Push one or more lines of input.
382
384
383 This stores the given lines and returns a status code indicating
385 This stores the given lines and returns a status code indicating
384 whether the code forms a complete Python block or not.
386 whether the code forms a complete Python block or not.
385
387
386 Any exceptions generated in compilation are swallowed, but if an
388 Any exceptions generated in compilation are swallowed, but if an
387 exception was produced, the method returns True.
389 exception was produced, the method returns True.
388
390
389 Parameters
391 Parameters
390 ----------
392 ----------
391 lines : string
393 lines : string
392 One or more lines of Python input.
394 One or more lines of Python input.
393
395
394 Returns
396 Returns
395 -------
397 -------
396 is_complete : boolean
398 is_complete : boolean
397 True if the current input source (the result of the current input
399 True if the current input source (the result of the current input
398 plus prior inputs) forms a complete Python execution block. Note that
400 plus prior inputs) forms a complete Python execution block. Note that
399 this value is also stored as a private attribute (``_is_complete``), so it
401 this value is also stored as a private attribute (``_is_complete``), so it
400 can be queried at any time.
402 can be queried at any time.
401 """
403 """
402 self._store(lines)
404 self._store(lines)
403 source = self.source
405 source = self.source
404
406
405 # Before calling _compile(), reset the code object to None so that if an
407 # Before calling _compile(), reset the code object to None so that if an
406 # exception is raised in compilation, we don't mislead by having
408 # exception is raised in compilation, we don't mislead by having
407 # inconsistent code/source attributes.
409 # inconsistent code/source attributes.
408 self.code, self._is_complete = None, None
410 self.code, self._is_complete = None, None
409 self._is_invalid = False
411 self._is_invalid = False
410
412
411 # Honor termination lines properly
413 # Honor termination lines properly
412 if source.endswith('\\\n'):
414 if source.endswith('\\\n'):
413 return False
415 return False
414
416
415 try:
417 try:
416 with warnings.catch_warnings():
418 with warnings.catch_warnings():
417 warnings.simplefilter('error', SyntaxWarning)
419 warnings.simplefilter('error', SyntaxWarning)
418 self.code = self._compile(source, symbol="exec")
420 self.code = self._compile(source, symbol="exec")
419 # Invalid syntax can produce any of a number of different errors from
421 # Invalid syntax can produce any of a number of different errors from
420 # inside the compiler, so we have to catch them all. Syntax errors
422 # inside the compiler, so we have to catch them all. Syntax errors
421 # immediately produce a 'ready' block, so the invalid Python can be
423 # immediately produce a 'ready' block, so the invalid Python can be
422 # sent to the kernel for evaluation with possible ipython
424 # sent to the kernel for evaluation with possible ipython
423 # special-syntax conversion.
425 # special-syntax conversion.
424 except (SyntaxError, OverflowError, ValueError, TypeError,
426 except (SyntaxError, OverflowError, ValueError, TypeError,
425 MemoryError, SyntaxWarning):
427 MemoryError, SyntaxWarning):
426 self._is_complete = True
428 self._is_complete = True
427 self._is_invalid = True
429 self._is_invalid = True
428 else:
430 else:
429 # Compilation didn't produce any exceptions (though it may not have
431 # Compilation didn't produce any exceptions (though it may not have
430 # given a complete code object)
432 # given a complete code object)
431 self._is_complete = self.code is not None
433 self._is_complete = self.code is not None
432
434
433 return self._is_complete
435 return self._is_complete
434
436
435 def push_accepts_more(self):
437 def push_accepts_more(self):
436 """Return whether a block of interactive input can accept more input.
438 """Return whether a block of interactive input can accept more input.
437
439
438 This method is meant to be used by line-oriented frontends, who need to
440 This method is meant to be used by line-oriented frontends, who need to
439 guess whether a block is complete or not based solely on prior and
441 guess whether a block is complete or not based solely on prior and
440 current input lines. The InputSplitter considers it has a complete
442 current input lines. The InputSplitter considers it has a complete
441 interactive block and will not accept more input when either:
443 interactive block and will not accept more input when either:
442
444
443 * A SyntaxError is raised
445 * A SyntaxError is raised
444
446
445 * The code is complete and consists of a single line or a single
447 * The code is complete and consists of a single line or a single
446 non-compound statement
448 non-compound statement
447
449
448 * The code is complete and has a blank line at the end
450 * The code is complete and has a blank line at the end
449
451
450 If the current input produces a syntax error, this method immediately
452 If the current input produces a syntax error, this method immediately
451 returns False but does *not* raise the syntax error exception, as
453 returns False but does *not* raise the syntax error exception, as
452 typically clients will want to send invalid syntax to an execution
454 typically clients will want to send invalid syntax to an execution
453 backend which might convert the invalid syntax into valid Python via
455 backend which might convert the invalid syntax into valid Python via
454 one of the dynamic IPython mechanisms.
456 one of the dynamic IPython mechanisms.
455 """
457 """
456
458
457 # With incomplete input, unconditionally accept more
459 # With incomplete input, unconditionally accept more
458 # A syntax error also sets _is_complete to True - see push()
460 # A syntax error also sets _is_complete to True - see push()
459 if not self._is_complete:
461 if not self._is_complete:
460 #print("Not complete") # debug
462 #print("Not complete") # debug
461 return True
463 return True
462
464
463 # The user can make any (complete) input execute by leaving a blank line
465 # The user can make any (complete) input execute by leaving a blank line
464 last_line = self.source.splitlines()[-1]
466 last_line = self.source.splitlines()[-1]
465 if (not last_line) or last_line.isspace():
467 if (not last_line) or last_line.isspace():
466 #print("Blank line") # debug
468 #print("Blank line") # debug
467 return False
469 return False
468
470
469 # If there's just a single line or AST node, and we're flush left, as is
471 # If there's just a single line or AST node, and we're flush left, as is
470 # the case after a simple statement such as 'a=1', we want to execute it
472 # the case after a simple statement such as 'a=1', we want to execute it
471 # straight away.
473 # straight away.
472 if self.get_indent_spaces() == 0:
474 if self.get_indent_spaces() == 0:
473 if len(self.source.splitlines()) <= 1:
475 if len(self.source.splitlines()) <= 1:
474 return False
476 return False
475
477
476 try:
478 try:
477 code_ast = ast.parse(u''.join(self._buffer))
479 code_ast = ast.parse(u''.join(self._buffer))
478 except Exception:
480 except Exception:
479 #print("Can't parse AST") # debug
481 #print("Can't parse AST") # debug
480 return False
482 return False
481 else:
483 else:
482 if len(code_ast.body) == 1 and \
484 if len(code_ast.body) == 1 and \
483 not hasattr(code_ast.body[0], 'body'):
485 not hasattr(code_ast.body[0], 'body'):
484 #print("Simple statement") # debug
486 #print("Simple statement") # debug
485 return False
487 return False
486
488
487 # General fallback - accept more code
489 # General fallback - accept more code
488 return True
490 return True
489
491
490 def get_indent_spaces(self):
492 def get_indent_spaces(self):
491 sourcefor, n = self._indent_spaces_cache
493 sourcefor, n = self._indent_spaces_cache
492 if sourcefor == self.source:
494 if sourcefor == self.source:
493 return n
495 return n
494
496
495 # self.source always has a trailing newline
497 # self.source always has a trailing newline
496 n = find_next_indent(self.source[:-1])
498 n = find_next_indent(self.source[:-1])
497 self._indent_spaces_cache = (self.source, n)
499 self._indent_spaces_cache = (self.source, n)
498 return n
500 return n
499
501
500 def _store(self, lines, buffer=None, store='source'):
502 def _store(self, lines, buffer=None, store='source'):
501 """Store one or more lines of input.
503 """Store one or more lines of input.
502
504
503 If input lines are not newline-terminated, a newline is automatically
505 If input lines are not newline-terminated, a newline is automatically
504 appended."""
506 appended."""
505
507
506 if buffer is None:
508 if buffer is None:
507 buffer = self._buffer
509 buffer = self._buffer
508
510
509 if lines.endswith('\n'):
511 if lines.endswith('\n'):
510 buffer.append(lines)
512 buffer.append(lines)
511 else:
513 else:
512 buffer.append(lines+'\n')
514 buffer.append(lines+'\n')
513 setattr(self, store, self._set_source(buffer))
515 setattr(self, store, self._set_source(buffer))
514
516
515 def _set_source(self, buffer):
517 def _set_source(self, buffer):
516 return u''.join(buffer)
518 return u''.join(buffer)
517
519
518
520
519 class IPythonInputSplitter(InputSplitter):
521 class IPythonInputSplitter(InputSplitter):
520 """An input splitter that recognizes all of IPython's special syntax."""
522 """An input splitter that recognizes all of IPython's special syntax."""
521
523
522 # String with raw, untransformed input.
524 # String with raw, untransformed input.
523 source_raw = ''
525 source_raw = ''
524
526
525 # Flag to track when a transformer has stored input that it hasn't given
527 # Flag to track when a transformer has stored input that it hasn't given
526 # back yet.
528 # back yet.
527 transformer_accumulating = False
529 transformer_accumulating = False
528
530
529 # Flag to track when assemble_python_lines has stored input that it hasn't
531 # Flag to track when assemble_python_lines has stored input that it hasn't
530 # given back yet.
532 # given back yet.
531 within_python_line = False
533 within_python_line = False
532
534
533 # Private attributes
535 # Private attributes
534
536
535 # List with lines of raw input accumulated so far.
537 # List with lines of raw input accumulated so far.
536 _buffer_raw = None
538 _buffer_raw = None
537
539
538 def __init__(self, line_input_checker=True, physical_line_transforms=None,
540 def __init__(self, line_input_checker=True, physical_line_transforms=None,
539 logical_line_transforms=None, python_line_transforms=None):
541 logical_line_transforms=None, python_line_transforms=None):
540 super(IPythonInputSplitter, self).__init__()
542 super(IPythonInputSplitter, self).__init__()
541 self._buffer_raw = []
543 self._buffer_raw = []
542 self._validate = True
544 self._validate = True
543
545
544 if physical_line_transforms is not None:
546 if physical_line_transforms is not None:
545 self.physical_line_transforms = physical_line_transforms
547 self.physical_line_transforms = physical_line_transforms
546 else:
548 else:
547 self.physical_line_transforms = [
549 self.physical_line_transforms = [
548 leading_indent(),
550 leading_indent(),
549 classic_prompt(),
551 classic_prompt(),
550 ipy_prompt(),
552 ipy_prompt(),
551 cellmagic(end_on_blank_line=line_input_checker),
553 cellmagic(end_on_blank_line=line_input_checker),
552 ]
554 ]
553
555
554 self.assemble_logical_lines = assemble_logical_lines()
556 self.assemble_logical_lines = assemble_logical_lines()
555 if logical_line_transforms is not None:
557 if logical_line_transforms is not None:
556 self.logical_line_transforms = logical_line_transforms
558 self.logical_line_transforms = logical_line_transforms
557 else:
559 else:
558 self.logical_line_transforms = [
560 self.logical_line_transforms = [
559 help_end(),
561 help_end(),
560 escaped_commands(),
562 escaped_commands(),
561 assign_from_magic(),
563 assign_from_magic(),
562 assign_from_system(),
564 assign_from_system(),
563 ]
565 ]
564
566
565 self.assemble_python_lines = assemble_python_lines()
567 self.assemble_python_lines = assemble_python_lines()
566 if python_line_transforms is not None:
568 if python_line_transforms is not None:
567 self.python_line_transforms = python_line_transforms
569 self.python_line_transforms = python_line_transforms
568 else:
570 else:
569 # We don't use any of these at present
571 # We don't use any of these at present
570 self.python_line_transforms = []
572 self.python_line_transforms = []
571
573
572 @property
574 @property
573 def transforms(self):
575 def transforms(self):
574 "Quick access to all transformers."
576 "Quick access to all transformers."
575 return self.physical_line_transforms + \
577 return self.physical_line_transforms + \
576 [self.assemble_logical_lines] + self.logical_line_transforms + \
578 [self.assemble_logical_lines] + self.logical_line_transforms + \
577 [self.assemble_python_lines] + self.python_line_transforms
579 [self.assemble_python_lines] + self.python_line_transforms
578
580
579 @property
581 @property
580 def transforms_in_use(self):
582 def transforms_in_use(self):
581 """Transformers, excluding logical line transformers if we're in a
583 """Transformers, excluding logical line transformers if we're in a
582 Python line."""
584 Python line."""
583 t = self.physical_line_transforms[:]
585 t = self.physical_line_transforms[:]
584 if not self.within_python_line:
586 if not self.within_python_line:
585 t += [self.assemble_logical_lines] + self.logical_line_transforms
587 t += [self.assemble_logical_lines] + self.logical_line_transforms
586 return t + [self.assemble_python_lines] + self.python_line_transforms
588 return t + [self.assemble_python_lines] + self.python_line_transforms
587
589
588 def reset(self):
590 def reset(self):
589 """Reset the input buffer and associated state."""
591 """Reset the input buffer and associated state."""
590 super(IPythonInputSplitter, self).reset()
592 super(IPythonInputSplitter, self).reset()
591 self._buffer_raw[:] = []
593 self._buffer_raw[:] = []
592 self.source_raw = ''
594 self.source_raw = ''
593 self.transformer_accumulating = False
595 self.transformer_accumulating = False
594 self.within_python_line = False
596 self.within_python_line = False
595
597
596 for t in self.transforms:
598 for t in self.transforms:
597 try:
599 try:
598 t.reset()
600 t.reset()
599 except SyntaxError:
601 except SyntaxError:
600 # Nothing that calls reset() expects to handle transformer
602 # Nothing that calls reset() expects to handle transformer
601 # errors
603 # errors
602 pass
604 pass
603
605
604 def flush_transformers(self):
606 def flush_transformers(self):
605 def _flush(transform, outs):
607 def _flush(transform, outs):
606 """yield transformed lines
608 """yield transformed lines
607
609
608 always strings, never None
610 always strings, never None
609
611
610 transform: the current transform
612 transform: the current transform
611 outs: an iterable of previously transformed inputs.
613 outs: an iterable of previously transformed inputs.
612 Each may be multiline, which will be passed
614 Each may be multiline, which will be passed
613 one line at a time to transform.
615 one line at a time to transform.
614 """
616 """
615 for out in outs:
617 for out in outs:
616 for line in out.splitlines():
618 for line in out.splitlines():
617 # push one line at a time
619 # push one line at a time
618 tmp = transform.push(line)
620 tmp = transform.push(line)
619 if tmp is not None:
621 if tmp is not None:
620 yield tmp
622 yield tmp
621
623
622 # reset the transform
624 # reset the transform
623 tmp = transform.reset()
625 tmp = transform.reset()
624 if tmp is not None:
626 if tmp is not None:
625 yield tmp
627 yield tmp
626
628
627 out = []
629 out = []
628 for t in self.transforms_in_use:
630 for t in self.transforms_in_use:
629 out = _flush(t, out)
631 out = _flush(t, out)
630
632
631 out = list(out)
633 out = list(out)
632 if out:
634 if out:
633 self._store('\n'.join(out))
635 self._store('\n'.join(out))
634
636
635 def raw_reset(self):
637 def raw_reset(self):
636 """Return raw input only and perform a full reset.
638 """Return raw input only and perform a full reset.
637 """
639 """
638 out = self.source_raw
640 out = self.source_raw
639 self.reset()
641 self.reset()
640 return out
642 return out
641
643
642 def source_reset(self):
644 def source_reset(self):
643 try:
645 try:
644 self.flush_transformers()
646 self.flush_transformers()
645 return self.source
647 return self.source
646 finally:
648 finally:
647 self.reset()
649 self.reset()
648
650
649 def push_accepts_more(self):
651 def push_accepts_more(self):
650 if self.transformer_accumulating:
652 if self.transformer_accumulating:
651 return True
653 return True
652 else:
654 else:
653 return super(IPythonInputSplitter, self).push_accepts_more()
655 return super(IPythonInputSplitter, self).push_accepts_more()
654
656
655 def transform_cell(self, cell):
657 def transform_cell(self, cell):
656 """Process and translate a cell of input.
658 """Process and translate a cell of input.
657 """
659 """
658 self.reset()
660 self.reset()
659 try:
661 try:
660 self.push(cell)
662 self.push(cell)
661 self.flush_transformers()
663 self.flush_transformers()
662 return self.source
664 return self.source
663 finally:
665 finally:
664 self.reset()
666 self.reset()
665
667
666 def push(self, lines):
668 def push(self, lines):
667 """Push one or more lines of IPython input.
669 """Push one or more lines of IPython input.
668
670
669 This stores the given lines and returns a status code indicating
671 This stores the given lines and returns a status code indicating
670 whether the code forms a complete Python block or not, after processing
672 whether the code forms a complete Python block or not, after processing
671 all input lines for special IPython syntax.
673 all input lines for special IPython syntax.
672
674
673 Any exceptions generated in compilation are swallowed, but if an
675 Any exceptions generated in compilation are swallowed, but if an
674 exception was produced, the method returns True.
676 exception was produced, the method returns True.
675
677
676 Parameters
678 Parameters
677 ----------
679 ----------
678 lines : string
680 lines : string
679 One or more lines of Python input.
681 One or more lines of Python input.
680
682
681 Returns
683 Returns
682 -------
684 -------
683 is_complete : boolean
685 is_complete : boolean
684 True if the current input source (the result of the current input
686 True if the current input source (the result of the current input
685 plus prior inputs) forms a complete Python execution block. Note that
687 plus prior inputs) forms a complete Python execution block. Note that
686 this value is also stored as a private attribute (_is_complete), so it
688 this value is also stored as a private attribute (_is_complete), so it
687 can be queried at any time.
689 can be queried at any time.
688 """
690 """
689
691
690 # We must ensure all input is pure unicode
692 # We must ensure all input is pure unicode
691 lines = cast_unicode(lines, self.encoding)
693 lines = cast_unicode(lines, self.encoding)
692 # ''.splitlines() --> [], but we need to push the empty line to transformers
694 # ''.splitlines() --> [], but we need to push the empty line to transformers
693 lines_list = lines.splitlines()
695 lines_list = lines.splitlines()
694 if not lines_list:
696 if not lines_list:
695 lines_list = ['']
697 lines_list = ['']
696
698
697 # Store raw source before applying any transformations to it. Note
699 # Store raw source before applying any transformations to it. Note
698 # that this must be done *after* the reset() call that would otherwise
700 # that this must be done *after* the reset() call that would otherwise
699 # flush the buffer.
701 # flush the buffer.
700 self._store(lines, self._buffer_raw, 'source_raw')
702 self._store(lines, self._buffer_raw, 'source_raw')
701
703
702 transformed_lines_list = []
704 transformed_lines_list = []
703 for line in lines_list:
705 for line in lines_list:
704 transformed = self._transform_line(line)
706 transformed = self._transform_line(line)
705 if transformed is not None:
707 if transformed is not None:
706 transformed_lines_list.append(transformed)
708 transformed_lines_list.append(transformed)
707
709
708 if transformed_lines_list:
710 if transformed_lines_list:
709 transformed_lines = '\n'.join(transformed_lines_list)
711 transformed_lines = '\n'.join(transformed_lines_list)
710 return super(IPythonInputSplitter, self).push(transformed_lines)
712 return super(IPythonInputSplitter, self).push(transformed_lines)
711 else:
713 else:
712 # Got nothing back from transformers - they must be waiting for
714 # Got nothing back from transformers - they must be waiting for
713 # more input.
715 # more input.
714 return False
716 return False
715
717
716 def _transform_line(self, line):
718 def _transform_line(self, line):
717 """Push a line of input code through the various transformers.
719 """Push a line of input code through the various transformers.
718
720
719 Returns any output from the transformers, or None if a transformer
721 Returns any output from the transformers, or None if a transformer
720 is accumulating lines.
722 is accumulating lines.
721
723
722 Sets self.transformer_accumulating as a side effect.
724 Sets self.transformer_accumulating as a side effect.
723 """
725 """
724 def _accumulating(dbg):
726 def _accumulating(dbg):
725 #print(dbg)
727 #print(dbg)
726 self.transformer_accumulating = True
728 self.transformer_accumulating = True
727 return None
729 return None
728
730
729 for transformer in self.physical_line_transforms:
731 for transformer in self.physical_line_transforms:
730 line = transformer.push(line)
732 line = transformer.push(line)
731 if line is None:
733 if line is None:
732 return _accumulating(transformer)
734 return _accumulating(transformer)
733
735
734 if not self.within_python_line:
736 if not self.within_python_line:
735 line = self.assemble_logical_lines.push(line)
737 line = self.assemble_logical_lines.push(line)
736 if line is None:
738 if line is None:
737 return _accumulating('acc logical line')
739 return _accumulating('acc logical line')
738
740
739 for transformer in self.logical_line_transforms:
741 for transformer in self.logical_line_transforms:
740 line = transformer.push(line)
742 line = transformer.push(line)
741 if line is None:
743 if line is None:
742 return _accumulating(transformer)
744 return _accumulating(transformer)
743
745
744 line = self.assemble_python_lines.push(line)
746 line = self.assemble_python_lines.push(line)
745 if line is None:
747 if line is None:
746 self.within_python_line = True
748 self.within_python_line = True
747 return _accumulating('acc python line')
749 return _accumulating('acc python line')
748 else:
750 else:
749 self.within_python_line = False
751 self.within_python_line = False
750
752
751 for transformer in self.python_line_transforms:
753 for transformer in self.python_line_transforms:
752 line = transformer.push(line)
754 line = transformer.push(line)
753 if line is None:
755 if line is None:
754 return _accumulating(transformer)
756 return _accumulating(transformer)
755
757
756 #print("transformers clear") #debug
758 #print("transformers clear") #debug
757 self.transformer_accumulating = False
759 self.transformer_accumulating = False
758 return line
760 return line
759
761
General Comments 0
You need to be logged in to leave comments. Login now