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