##// END OF EJS Templates
Leave inputsplitter.indent_spaces for backwards compatibility
Thomas Kluyver -
Show More
@@ -1,761 +1,766 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 storing the current indentation
293 # A cache for storing the current indentation
294 # The first value stores the most recently processed source input
294 # The first value stores the most recently processed source input
295 # The second value is the number of spaces for the current indentation
295 # The second value is the number of spaces for the current indentation
296 # If self.source matches the first value, the second value is a valid
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
297 # current indentation. Otherwise, the cache is invalid and the indentation
298 # must be recalculated.
298 # must be recalculated.
299 _indent_spaces_cache = None, None
299 _indent_spaces_cache = None, None
300 # String, indicating the default input encoding. It is computed by default
300 # String, indicating the default input encoding. It is computed by default
301 # 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
302 # client with specific knowledge of the encoding.
302 # client with specific knowledge of the encoding.
303 encoding = ''
303 encoding = ''
304 # String where the current full source input is stored, properly encoded.
304 # String where the current full source input is stored, properly encoded.
305 # 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
306 # source code, that has been properly encoded.
306 # source code, that has been properly encoded.
307 source = ''
307 source = ''
308 # Code object corresponding to the current source. It is automatically
308 # Code object corresponding to the current source. It is automatically
309 # 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
310 # 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.
311 code = None
311 code = None
312
312
313 # Private attributes
313 # Private attributes
314
314
315 # List with lines of input accumulated so far
315 # List with lines of input accumulated so far
316 _buffer = None
316 _buffer = None
317 # Command compiler
317 # Command compiler
318 _compile = None
318 _compile = None
319 # Boolean indicating whether the current block is complete
319 # Boolean indicating whether the current block is complete
320 _is_complete = None
320 _is_complete = None
321 # Boolean indicating whether the current block has an unrecoverable syntax error
321 # Boolean indicating whether the current block has an unrecoverable syntax error
322 _is_invalid = False
322 _is_invalid = False
323
323
324 def __init__(self):
324 def __init__(self):
325 """Create a new InputSplitter instance.
325 """Create a new InputSplitter instance.
326 """
326 """
327 self._buffer = []
327 self._buffer = []
328 self._compile = codeop.CommandCompiler()
328 self._compile = codeop.CommandCompiler()
329 self.encoding = get_input_encoding()
329 self.encoding = get_input_encoding()
330
330
331 def reset(self):
331 def reset(self):
332 """Reset the input buffer and associated state."""
332 """Reset the input buffer and associated state."""
333 self._buffer[:] = []
333 self._buffer[:] = []
334 self.source = ''
334 self.source = ''
335 self.code = None
335 self.code = None
336 self._is_complete = False
336 self._is_complete = False
337 self._is_invalid = False
337 self._is_invalid = False
338
338
339 def source_reset(self):
339 def source_reset(self):
340 """Return the input source and perform a full reset.
340 """Return the input source and perform a full reset.
341 """
341 """
342 out = self.source
342 out = self.source
343 self.reset()
343 self.reset()
344 return out
344 return out
345
345
346 def check_complete(self, source):
346 def check_complete(self, source):
347 """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
348
348
349 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.
350
350
351 Parameters
351 Parameters
352 ----------
352 ----------
353 source : string
353 source : string
354 Python input code, which can be multiline.
354 Python input code, which can be multiline.
355
355
356 Returns
356 Returns
357 -------
357 -------
358 status : str
358 status : str
359 One of 'complete', 'incomplete', or 'invalid' if source is not a
359 One of 'complete', 'incomplete', or 'invalid' if source is not a
360 prefix of valid code.
360 prefix of valid code.
361 indent_spaces : int or None
361 indent_spaces : int or None
362 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
363 status is not 'incomplete', this is None.
363 status is not 'incomplete', this is None.
364 """
364 """
365 self.reset()
365 self.reset()
366 try:
366 try:
367 self.push(source)
367 self.push(source)
368 except SyntaxError:
368 except SyntaxError:
369 # Transformers in IPythonInputSplitter can raise SyntaxError,
369 # Transformers in IPythonInputSplitter can raise SyntaxError,
370 # which push() will not catch.
370 # which push() will not catch.
371 return 'invalid', None
371 return 'invalid', None
372 else:
372 else:
373 if self._is_invalid:
373 if self._is_invalid:
374 return 'invalid', None
374 return 'invalid', None
375 elif self.push_accepts_more():
375 elif self.push_accepts_more():
376 return 'incomplete', self.get_indent_spaces()
376 return 'incomplete', self.get_indent_spaces()
377 else:
377 else:
378 return 'complete', None
378 return 'complete', None
379 finally:
379 finally:
380 self.reset()
380 self.reset()
381
381
382 def push(self, lines):
382 def push(self, lines):
383 """Push one or more lines of input.
383 """Push one or more lines of input.
384
384
385 This stores the given lines and returns a status code indicating
385 This stores the given lines and returns a status code indicating
386 whether the code forms a complete Python block or not.
386 whether the code forms a complete Python block or not.
387
387
388 Any exceptions generated in compilation are swallowed, but if an
388 Any exceptions generated in compilation are swallowed, but if an
389 exception was produced, the method returns True.
389 exception was produced, the method returns True.
390
390
391 Parameters
391 Parameters
392 ----------
392 ----------
393 lines : string
393 lines : string
394 One or more lines of Python input.
394 One or more lines of Python input.
395
395
396 Returns
396 Returns
397 -------
397 -------
398 is_complete : boolean
398 is_complete : boolean
399 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
400 plus prior inputs) forms a complete Python execution block. Note that
400 plus prior inputs) forms a complete Python execution block. Note that
401 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
402 can be queried at any time.
402 can be queried at any time.
403 """
403 """
404 self._store(lines)
404 self._store(lines)
405 source = self.source
405 source = self.source
406
406
407 # 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
408 # exception is raised in compilation, we don't mislead by having
408 # exception is raised in compilation, we don't mislead by having
409 # inconsistent code/source attributes.
409 # inconsistent code/source attributes.
410 self.code, self._is_complete = None, None
410 self.code, self._is_complete = None, None
411 self._is_invalid = False
411 self._is_invalid = False
412
412
413 # Honor termination lines properly
413 # Honor termination lines properly
414 if source.endswith('\\\n'):
414 if source.endswith('\\\n'):
415 return False
415 return False
416
416
417 try:
417 try:
418 with warnings.catch_warnings():
418 with warnings.catch_warnings():
419 warnings.simplefilter('error', SyntaxWarning)
419 warnings.simplefilter('error', SyntaxWarning)
420 self.code = self._compile(source, symbol="exec")
420 self.code = self._compile(source, symbol="exec")
421 # 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
422 # 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
423 # immediately produce a 'ready' block, so the invalid Python can be
423 # immediately produce a 'ready' block, so the invalid Python can be
424 # sent to the kernel for evaluation with possible ipython
424 # sent to the kernel for evaluation with possible ipython
425 # special-syntax conversion.
425 # special-syntax conversion.
426 except (SyntaxError, OverflowError, ValueError, TypeError,
426 except (SyntaxError, OverflowError, ValueError, TypeError,
427 MemoryError, SyntaxWarning):
427 MemoryError, SyntaxWarning):
428 self._is_complete = True
428 self._is_complete = True
429 self._is_invalid = True
429 self._is_invalid = True
430 else:
430 else:
431 # Compilation didn't produce any exceptions (though it may not have
431 # Compilation didn't produce any exceptions (though it may not have
432 # given a complete code object)
432 # given a complete code object)
433 self._is_complete = self.code is not None
433 self._is_complete = self.code is not None
434
434
435 return self._is_complete
435 return self._is_complete
436
436
437 def push_accepts_more(self):
437 def push_accepts_more(self):
438 """Return whether a block of interactive input can accept more input.
438 """Return whether a block of interactive input can accept more input.
439
439
440 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
441 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
442 current input lines. The InputSplitter considers it has a complete
442 current input lines. The InputSplitter considers it has a complete
443 interactive block and will not accept more input when either:
443 interactive block and will not accept more input when either:
444
444
445 * A SyntaxError is raised
445 * A SyntaxError is raised
446
446
447 * 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
448 non-compound statement
448 non-compound statement
449
449
450 * 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
451
451
452 If the current input produces a syntax error, this method immediately
452 If the current input produces a syntax error, this method immediately
453 returns False but does *not* raise the syntax error exception, as
453 returns False but does *not* raise the syntax error exception, as
454 typically clients will want to send invalid syntax to an execution
454 typically clients will want to send invalid syntax to an execution
455 backend which might convert the invalid syntax into valid Python via
455 backend which might convert the invalid syntax into valid Python via
456 one of the dynamic IPython mechanisms.
456 one of the dynamic IPython mechanisms.
457 """
457 """
458
458
459 # With incomplete input, unconditionally accept more
459 # With incomplete input, unconditionally accept more
460 # A syntax error also sets _is_complete to True - see push()
460 # A syntax error also sets _is_complete to True - see push()
461 if not self._is_complete:
461 if not self._is_complete:
462 #print("Not complete") # debug
462 #print("Not complete") # debug
463 return True
463 return True
464
464
465 # 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
466 last_line = self.source.splitlines()[-1]
466 last_line = self.source.splitlines()[-1]
467 if (not last_line) or last_line.isspace():
467 if (not last_line) or last_line.isspace():
468 #print("Blank line") # debug
468 #print("Blank line") # debug
469 return False
469 return False
470
470
471 # 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
472 # 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
473 # straight away.
473 # straight away.
474 if self.get_indent_spaces() == 0:
474 if self.get_indent_spaces() == 0:
475 if len(self.source.splitlines()) <= 1:
475 if len(self.source.splitlines()) <= 1:
476 return False
476 return False
477
477
478 try:
478 try:
479 code_ast = ast.parse(u''.join(self._buffer))
479 code_ast = ast.parse(u''.join(self._buffer))
480 except Exception:
480 except Exception:
481 #print("Can't parse AST") # debug
481 #print("Can't parse AST") # debug
482 return False
482 return False
483 else:
483 else:
484 if len(code_ast.body) == 1 and \
484 if len(code_ast.body) == 1 and \
485 not hasattr(code_ast.body[0], 'body'):
485 not hasattr(code_ast.body[0], 'body'):
486 #print("Simple statement") # debug
486 #print("Simple statement") # debug
487 return False
487 return False
488
488
489 # General fallback - accept more code
489 # General fallback - accept more code
490 return True
490 return True
491
491
492 def get_indent_spaces(self):
492 def get_indent_spaces(self):
493 sourcefor, n = self._indent_spaces_cache
493 sourcefor, n = self._indent_spaces_cache
494 if sourcefor == self.source:
494 if sourcefor == self.source:
495 return n
495 return n
496
496
497 # self.source always has a trailing newline
497 # self.source always has a trailing newline
498 n = find_next_indent(self.source[:-1])
498 n = find_next_indent(self.source[:-1])
499 self._indent_spaces_cache = (self.source, n)
499 self._indent_spaces_cache = (self.source, n)
500 return n
500 return n
501
501
502 # Backwards compatibility. I think all code that used .indent_spaces was
503 # inside IPython, but we can leave this here until IPython 7 in case any
504 # other modules are using it. -TK, November 2017
505 indent_spaces = property(get_indent_spaces)
506
502 def _store(self, lines, buffer=None, store='source'):
507 def _store(self, lines, buffer=None, store='source'):
503 """Store one or more lines of input.
508 """Store one or more lines of input.
504
509
505 If input lines are not newline-terminated, a newline is automatically
510 If input lines are not newline-terminated, a newline is automatically
506 appended."""
511 appended."""
507
512
508 if buffer is None:
513 if buffer is None:
509 buffer = self._buffer
514 buffer = self._buffer
510
515
511 if lines.endswith('\n'):
516 if lines.endswith('\n'):
512 buffer.append(lines)
517 buffer.append(lines)
513 else:
518 else:
514 buffer.append(lines+'\n')
519 buffer.append(lines+'\n')
515 setattr(self, store, self._set_source(buffer))
520 setattr(self, store, self._set_source(buffer))
516
521
517 def _set_source(self, buffer):
522 def _set_source(self, buffer):
518 return u''.join(buffer)
523 return u''.join(buffer)
519
524
520
525
521 class IPythonInputSplitter(InputSplitter):
526 class IPythonInputSplitter(InputSplitter):
522 """An input splitter that recognizes all of IPython's special syntax."""
527 """An input splitter that recognizes all of IPython's special syntax."""
523
528
524 # String with raw, untransformed input.
529 # String with raw, untransformed input.
525 source_raw = ''
530 source_raw = ''
526
531
527 # Flag to track when a transformer has stored input that it hasn't given
532 # Flag to track when a transformer has stored input that it hasn't given
528 # back yet.
533 # back yet.
529 transformer_accumulating = False
534 transformer_accumulating = False
530
535
531 # Flag to track when assemble_python_lines has stored input that it hasn't
536 # Flag to track when assemble_python_lines has stored input that it hasn't
532 # given back yet.
537 # given back yet.
533 within_python_line = False
538 within_python_line = False
534
539
535 # Private attributes
540 # Private attributes
536
541
537 # List with lines of raw input accumulated so far.
542 # List with lines of raw input accumulated so far.
538 _buffer_raw = None
543 _buffer_raw = None
539
544
540 def __init__(self, line_input_checker=True, physical_line_transforms=None,
545 def __init__(self, line_input_checker=True, physical_line_transforms=None,
541 logical_line_transforms=None, python_line_transforms=None):
546 logical_line_transforms=None, python_line_transforms=None):
542 super(IPythonInputSplitter, self).__init__()
547 super(IPythonInputSplitter, self).__init__()
543 self._buffer_raw = []
548 self._buffer_raw = []
544 self._validate = True
549 self._validate = True
545
550
546 if physical_line_transforms is not None:
551 if physical_line_transforms is not None:
547 self.physical_line_transforms = physical_line_transforms
552 self.physical_line_transforms = physical_line_transforms
548 else:
553 else:
549 self.physical_line_transforms = [
554 self.physical_line_transforms = [
550 leading_indent(),
555 leading_indent(),
551 classic_prompt(),
556 classic_prompt(),
552 ipy_prompt(),
557 ipy_prompt(),
553 cellmagic(end_on_blank_line=line_input_checker),
558 cellmagic(end_on_blank_line=line_input_checker),
554 ]
559 ]
555
560
556 self.assemble_logical_lines = assemble_logical_lines()
561 self.assemble_logical_lines = assemble_logical_lines()
557 if logical_line_transforms is not None:
562 if logical_line_transforms is not None:
558 self.logical_line_transforms = logical_line_transforms
563 self.logical_line_transforms = logical_line_transforms
559 else:
564 else:
560 self.logical_line_transforms = [
565 self.logical_line_transforms = [
561 help_end(),
566 help_end(),
562 escaped_commands(),
567 escaped_commands(),
563 assign_from_magic(),
568 assign_from_magic(),
564 assign_from_system(),
569 assign_from_system(),
565 ]
570 ]
566
571
567 self.assemble_python_lines = assemble_python_lines()
572 self.assemble_python_lines = assemble_python_lines()
568 if python_line_transforms is not None:
573 if python_line_transforms is not None:
569 self.python_line_transforms = python_line_transforms
574 self.python_line_transforms = python_line_transforms
570 else:
575 else:
571 # We don't use any of these at present
576 # We don't use any of these at present
572 self.python_line_transforms = []
577 self.python_line_transforms = []
573
578
574 @property
579 @property
575 def transforms(self):
580 def transforms(self):
576 "Quick access to all transformers."
581 "Quick access to all transformers."
577 return self.physical_line_transforms + \
582 return self.physical_line_transforms + \
578 [self.assemble_logical_lines] + self.logical_line_transforms + \
583 [self.assemble_logical_lines] + self.logical_line_transforms + \
579 [self.assemble_python_lines] + self.python_line_transforms
584 [self.assemble_python_lines] + self.python_line_transforms
580
585
581 @property
586 @property
582 def transforms_in_use(self):
587 def transforms_in_use(self):
583 """Transformers, excluding logical line transformers if we're in a
588 """Transformers, excluding logical line transformers if we're in a
584 Python line."""
589 Python line."""
585 t = self.physical_line_transforms[:]
590 t = self.physical_line_transforms[:]
586 if not self.within_python_line:
591 if not self.within_python_line:
587 t += [self.assemble_logical_lines] + self.logical_line_transforms
592 t += [self.assemble_logical_lines] + self.logical_line_transforms
588 return t + [self.assemble_python_lines] + self.python_line_transforms
593 return t + [self.assemble_python_lines] + self.python_line_transforms
589
594
590 def reset(self):
595 def reset(self):
591 """Reset the input buffer and associated state."""
596 """Reset the input buffer and associated state."""
592 super(IPythonInputSplitter, self).reset()
597 super(IPythonInputSplitter, self).reset()
593 self._buffer_raw[:] = []
598 self._buffer_raw[:] = []
594 self.source_raw = ''
599 self.source_raw = ''
595 self.transformer_accumulating = False
600 self.transformer_accumulating = False
596 self.within_python_line = False
601 self.within_python_line = False
597
602
598 for t in self.transforms:
603 for t in self.transforms:
599 try:
604 try:
600 t.reset()
605 t.reset()
601 except SyntaxError:
606 except SyntaxError:
602 # Nothing that calls reset() expects to handle transformer
607 # Nothing that calls reset() expects to handle transformer
603 # errors
608 # errors
604 pass
609 pass
605
610
606 def flush_transformers(self):
611 def flush_transformers(self):
607 def _flush(transform, outs):
612 def _flush(transform, outs):
608 """yield transformed lines
613 """yield transformed lines
609
614
610 always strings, never None
615 always strings, never None
611
616
612 transform: the current transform
617 transform: the current transform
613 outs: an iterable of previously transformed inputs.
618 outs: an iterable of previously transformed inputs.
614 Each may be multiline, which will be passed
619 Each may be multiline, which will be passed
615 one line at a time to transform.
620 one line at a time to transform.
616 """
621 """
617 for out in outs:
622 for out in outs:
618 for line in out.splitlines():
623 for line in out.splitlines():
619 # push one line at a time
624 # push one line at a time
620 tmp = transform.push(line)
625 tmp = transform.push(line)
621 if tmp is not None:
626 if tmp is not None:
622 yield tmp
627 yield tmp
623
628
624 # reset the transform
629 # reset the transform
625 tmp = transform.reset()
630 tmp = transform.reset()
626 if tmp is not None:
631 if tmp is not None:
627 yield tmp
632 yield tmp
628
633
629 out = []
634 out = []
630 for t in self.transforms_in_use:
635 for t in self.transforms_in_use:
631 out = _flush(t, out)
636 out = _flush(t, out)
632
637
633 out = list(out)
638 out = list(out)
634 if out:
639 if out:
635 self._store('\n'.join(out))
640 self._store('\n'.join(out))
636
641
637 def raw_reset(self):
642 def raw_reset(self):
638 """Return raw input only and perform a full reset.
643 """Return raw input only and perform a full reset.
639 """
644 """
640 out = self.source_raw
645 out = self.source_raw
641 self.reset()
646 self.reset()
642 return out
647 return out
643
648
644 def source_reset(self):
649 def source_reset(self):
645 try:
650 try:
646 self.flush_transformers()
651 self.flush_transformers()
647 return self.source
652 return self.source
648 finally:
653 finally:
649 self.reset()
654 self.reset()
650
655
651 def push_accepts_more(self):
656 def push_accepts_more(self):
652 if self.transformer_accumulating:
657 if self.transformer_accumulating:
653 return True
658 return True
654 else:
659 else:
655 return super(IPythonInputSplitter, self).push_accepts_more()
660 return super(IPythonInputSplitter, self).push_accepts_more()
656
661
657 def transform_cell(self, cell):
662 def transform_cell(self, cell):
658 """Process and translate a cell of input.
663 """Process and translate a cell of input.
659 """
664 """
660 self.reset()
665 self.reset()
661 try:
666 try:
662 self.push(cell)
667 self.push(cell)
663 self.flush_transformers()
668 self.flush_transformers()
664 return self.source
669 return self.source
665 finally:
670 finally:
666 self.reset()
671 self.reset()
667
672
668 def push(self, lines):
673 def push(self, lines):
669 """Push one or more lines of IPython input.
674 """Push one or more lines of IPython input.
670
675
671 This stores the given lines and returns a status code indicating
676 This stores the given lines and returns a status code indicating
672 whether the code forms a complete Python block or not, after processing
677 whether the code forms a complete Python block or not, after processing
673 all input lines for special IPython syntax.
678 all input lines for special IPython syntax.
674
679
675 Any exceptions generated in compilation are swallowed, but if an
680 Any exceptions generated in compilation are swallowed, but if an
676 exception was produced, the method returns True.
681 exception was produced, the method returns True.
677
682
678 Parameters
683 Parameters
679 ----------
684 ----------
680 lines : string
685 lines : string
681 One or more lines of Python input.
686 One or more lines of Python input.
682
687
683 Returns
688 Returns
684 -------
689 -------
685 is_complete : boolean
690 is_complete : boolean
686 True if the current input source (the result of the current input
691 True if the current input source (the result of the current input
687 plus prior inputs) forms a complete Python execution block. Note that
692 plus prior inputs) forms a complete Python execution block. Note that
688 this value is also stored as a private attribute (_is_complete), so it
693 this value is also stored as a private attribute (_is_complete), so it
689 can be queried at any time.
694 can be queried at any time.
690 """
695 """
691
696
692 # We must ensure all input is pure unicode
697 # We must ensure all input is pure unicode
693 lines = cast_unicode(lines, self.encoding)
698 lines = cast_unicode(lines, self.encoding)
694 # ''.splitlines() --> [], but we need to push the empty line to transformers
699 # ''.splitlines() --> [], but we need to push the empty line to transformers
695 lines_list = lines.splitlines()
700 lines_list = lines.splitlines()
696 if not lines_list:
701 if not lines_list:
697 lines_list = ['']
702 lines_list = ['']
698
703
699 # Store raw source before applying any transformations to it. Note
704 # Store raw source before applying any transformations to it. Note
700 # that this must be done *after* the reset() call that would otherwise
705 # that this must be done *after* the reset() call that would otherwise
701 # flush the buffer.
706 # flush the buffer.
702 self._store(lines, self._buffer_raw, 'source_raw')
707 self._store(lines, self._buffer_raw, 'source_raw')
703
708
704 transformed_lines_list = []
709 transformed_lines_list = []
705 for line in lines_list:
710 for line in lines_list:
706 transformed = self._transform_line(line)
711 transformed = self._transform_line(line)
707 if transformed is not None:
712 if transformed is not None:
708 transformed_lines_list.append(transformed)
713 transformed_lines_list.append(transformed)
709
714
710 if transformed_lines_list:
715 if transformed_lines_list:
711 transformed_lines = '\n'.join(transformed_lines_list)
716 transformed_lines = '\n'.join(transformed_lines_list)
712 return super(IPythonInputSplitter, self).push(transformed_lines)
717 return super(IPythonInputSplitter, self).push(transformed_lines)
713 else:
718 else:
714 # Got nothing back from transformers - they must be waiting for
719 # Got nothing back from transformers - they must be waiting for
715 # more input.
720 # more input.
716 return False
721 return False
717
722
718 def _transform_line(self, line):
723 def _transform_line(self, line):
719 """Push a line of input code through the various transformers.
724 """Push a line of input code through the various transformers.
720
725
721 Returns any output from the transformers, or None if a transformer
726 Returns any output from the transformers, or None if a transformer
722 is accumulating lines.
727 is accumulating lines.
723
728
724 Sets self.transformer_accumulating as a side effect.
729 Sets self.transformer_accumulating as a side effect.
725 """
730 """
726 def _accumulating(dbg):
731 def _accumulating(dbg):
727 #print(dbg)
732 #print(dbg)
728 self.transformer_accumulating = True
733 self.transformer_accumulating = True
729 return None
734 return None
730
735
731 for transformer in self.physical_line_transforms:
736 for transformer in self.physical_line_transforms:
732 line = transformer.push(line)
737 line = transformer.push(line)
733 if line is None:
738 if line is None:
734 return _accumulating(transformer)
739 return _accumulating(transformer)
735
740
736 if not self.within_python_line:
741 if not self.within_python_line:
737 line = self.assemble_logical_lines.push(line)
742 line = self.assemble_logical_lines.push(line)
738 if line is None:
743 if line is None:
739 return _accumulating('acc logical line')
744 return _accumulating('acc logical line')
740
745
741 for transformer in self.logical_line_transforms:
746 for transformer in self.logical_line_transforms:
742 line = transformer.push(line)
747 line = transformer.push(line)
743 if line is None:
748 if line is None:
744 return _accumulating(transformer)
749 return _accumulating(transformer)
745
750
746 line = self.assemble_python_lines.push(line)
751 line = self.assemble_python_lines.push(line)
747 if line is None:
752 if line is None:
748 self.within_python_line = True
753 self.within_python_line = True
749 return _accumulating('acc python line')
754 return _accumulating('acc python line')
750 else:
755 else:
751 self.within_python_line = False
756 self.within_python_line = False
752
757
753 for transformer in self.python_line_transforms:
758 for transformer in self.python_line_transforms:
754 line = transformer.push(line)
759 line = transformer.push(line)
755 if line is None:
760 if line is None:
756 return _accumulating(transformer)
761 return _accumulating(transformer)
757
762
758 #print("transformers clear") #debug
763 #print("transformers clear") #debug
759 self.transformer_accumulating = False
764 self.transformer_accumulating = False
760 return line
765 return line
761
766
General Comments 0
You need to be logged in to leave comments. Login now