##// END OF EJS Templates
add strip_encoding_cookie transformer...
MinRK -
Show More
@@ -1,657 +1,659 b''
1 """Analysis of text input into executable blocks.
1 """Analysis of text input into executable blocks.
2
2
3 The main class in this module, :class:`InputSplitter`, is designed to break
3 The main class in this module, :class:`InputSplitter`, is designed to break
4 input from either interactive, line-by-line environments or block-based ones,
4 input from either interactive, line-by-line environments or block-based ones,
5 into standalone blocks that can be executed by Python as 'single' statements
5 into standalone blocks that can be executed by Python as 'single' statements
6 (thus triggering sys.displayhook).
6 (thus triggering sys.displayhook).
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
10
11 For more details, see the class docstring below.
11 For more details, see the class docstring below.
12
12
13 Syntax Transformations
13 Syntax Transformations
14 ----------------------
14 ----------------------
15
15
16 One of the main jobs of the code in this file is to apply all syntax
16 One of the main jobs of the code in this file is to apply all syntax
17 transformations that make up 'the IPython language', i.e. magics, shell
17 transformations that make up 'the IPython language', i.e. magics, shell
18 escapes, etc. All transformations should be implemented as *fully stateless*
18 escapes, etc. All transformations should be implemented as *fully stateless*
19 entities, that simply take one line as their input and return a line.
19 entities, that simply take one line as their input and return a line.
20 Internally for implementation purposes they may be a normal function or a
20 Internally for implementation purposes they may be a normal function or a
21 callable object, but the only input they receive will be a single line and they
21 callable object, but the only input they receive will be a single line and they
22 should only return a line, without holding any data-dependent state between
22 should only return a line, without holding any data-dependent state between
23 calls.
23 calls.
24
24
25 As an example, the EscapedTransformer is a class so we can more clearly group
25 As an example, the EscapedTransformer is a class so we can more clearly group
26 together the functionality of dispatching to individual functions based on the
26 together the functionality of dispatching to individual functions based on the
27 starting escape character, but the only method for public use is its call
27 starting escape character, but the only method for public use is its call
28 method.
28 method.
29
29
30
30
31 ToDo
31 ToDo
32 ----
32 ----
33
33
34 - Should we make push() actually raise an exception once push_accepts_more()
34 - Should we make push() actually raise an exception once push_accepts_more()
35 returns False?
35 returns False?
36
36
37 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
37 - Naming cleanups. The tr_* names aren't the most elegant, though now they are
38 at least just attributes of a class so not really very exposed.
38 at least just attributes of a class so not really very exposed.
39
39
40 - Think about the best way to support dynamic things: automagic, autocall,
40 - Think about the best way to support dynamic things: automagic, autocall,
41 macros, etc.
41 macros, etc.
42
42
43 - Think of a better heuristic for the application of the transforms in
43 - Think of a better heuristic for the application of the transforms in
44 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
44 IPythonInputSplitter.push() than looking at the buffer ending in ':'. Idea:
45 track indentation change events (indent, dedent, nothing) and apply them only
45 track indentation change events (indent, dedent, nothing) and apply them only
46 if the indentation went up, but not otherwise.
46 if the indentation went up, but not otherwise.
47
47
48 - Think of the cleanest way for supporting user-specified transformations (the
48 - Think of the cleanest way for supporting user-specified transformations (the
49 user prefilters we had before).
49 user prefilters we had before).
50
50
51 Authors
51 Authors
52 -------
52 -------
53
53
54 * Fernando Perez
54 * Fernando Perez
55 * Brian Granger
55 * Brian Granger
56 """
56 """
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58 # Copyright (C) 2010 The IPython Development Team
58 # Copyright (C) 2010 The IPython Development Team
59 #
59 #
60 # Distributed under the terms of the BSD License. The full license is in
60 # Distributed under the terms of the BSD License. The full license is in
61 # the file COPYING, distributed as part of this software.
61 # the file COPYING, distributed as part of this software.
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Imports
65 # Imports
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67 # stdlib
67 # stdlib
68 import ast
68 import ast
69 import codeop
69 import codeop
70 import re
70 import re
71 import sys
71 import sys
72
72
73 # IPython modules
73 # IPython modules
74 from IPython.utils.py3compat import cast_unicode
74 from IPython.utils.py3compat import cast_unicode
75 from IPython.core.inputtransformer import (leading_indent,
75 from IPython.core.inputtransformer import (leading_indent,
76 classic_prompt,
76 classic_prompt,
77 ipy_prompt,
77 ipy_prompt,
78 strip_encoding_cookie,
78 cellmagic,
79 cellmagic,
79 assemble_logical_lines,
80 assemble_logical_lines,
80 help_end,
81 help_end,
81 escaped_commands,
82 escaped_commands,
82 assign_from_magic,
83 assign_from_magic,
83 assign_from_system,
84 assign_from_system,
84 assemble_python_lines,
85 assemble_python_lines,
85 )
86 )
86
87
87 # These are available in this module for backwards compatibility.
88 # These are available in this module for backwards compatibility.
88 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
89 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
89 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
90 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
90 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
91 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
91
92
92 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
93 # Utilities
94 # Utilities
94 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
95
96
96 # FIXME: These are general-purpose utilities that later can be moved to the
97 # FIXME: These are general-purpose utilities that later can be moved to the
97 # general ward. Kept here for now because we're being very strict about test
98 # general ward. Kept here for now because we're being very strict about test
98 # coverage with this code, and this lets us ensure that we keep 100% coverage
99 # coverage with this code, and this lets us ensure that we keep 100% coverage
99 # while developing.
100 # while developing.
100
101
101 # compiled regexps for autoindent management
102 # compiled regexps for autoindent management
102 dedent_re = re.compile('|'.join([
103 dedent_re = re.compile('|'.join([
103 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
104 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
104 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
105 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
105 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
106 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
106 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
107 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
107 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
108 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
108 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
109 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
109 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
110 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
110 ]))
111 ]))
111 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
112 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
112
113
113 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
114 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
114 # before pure comments
115 # before pure comments
115 comment_line_re = re.compile('^\s*\#')
116 comment_line_re = re.compile('^\s*\#')
116
117
117
118
118 def num_ini_spaces(s):
119 def num_ini_spaces(s):
119 """Return the number of initial spaces in a string.
120 """Return the number of initial spaces in a string.
120
121
121 Note that tabs are counted as a single space. For now, we do *not* support
122 Note that tabs are counted as a single space. For now, we do *not* support
122 mixing of tabs and spaces in the user's input.
123 mixing of tabs and spaces in the user's input.
123
124
124 Parameters
125 Parameters
125 ----------
126 ----------
126 s : string
127 s : string
127
128
128 Returns
129 Returns
129 -------
130 -------
130 n : int
131 n : int
131 """
132 """
132
133
133 ini_spaces = ini_spaces_re.match(s)
134 ini_spaces = ini_spaces_re.match(s)
134 if ini_spaces:
135 if ini_spaces:
135 return ini_spaces.end()
136 return ini_spaces.end()
136 else:
137 else:
137 return 0
138 return 0
138
139
139 def last_blank(src):
140 def last_blank(src):
140 """Determine if the input source ends in a blank.
141 """Determine if the input source ends in a blank.
141
142
142 A blank is either a newline or a line consisting of whitespace.
143 A blank is either a newline or a line consisting of whitespace.
143
144
144 Parameters
145 Parameters
145 ----------
146 ----------
146 src : string
147 src : string
147 A single or multiline string.
148 A single or multiline string.
148 """
149 """
149 if not src: return False
150 if not src: return False
150 ll = src.splitlines()[-1]
151 ll = src.splitlines()[-1]
151 return (ll == '') or ll.isspace()
152 return (ll == '') or ll.isspace()
152
153
153
154
154 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
155 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
155 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
156 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
156
157
157 def last_two_blanks(src):
158 def last_two_blanks(src):
158 """Determine if the input source ends in two blanks.
159 """Determine if the input source ends in two blanks.
159
160
160 A blank is either a newline or a line consisting of whitespace.
161 A blank is either a newline or a line consisting of whitespace.
161
162
162 Parameters
163 Parameters
163 ----------
164 ----------
164 src : string
165 src : string
165 A single or multiline string.
166 A single or multiline string.
166 """
167 """
167 if not src: return False
168 if not src: return False
168 # The logic here is tricky: I couldn't get a regexp to work and pass all
169 # The logic here is tricky: I couldn't get a regexp to work and pass all
169 # the tests, so I took a different approach: split the source by lines,
170 # the tests, so I took a different approach: split the source by lines,
170 # grab the last two and prepend '###\n' as a stand-in for whatever was in
171 # grab the last two and prepend '###\n' as a stand-in for whatever was in
171 # the body before the last two lines. Then, with that structure, it's
172 # the body before the last two lines. Then, with that structure, it's
172 # possible to analyze with two regexps. Not the most elegant solution, but
173 # possible to analyze with two regexps. Not the most elegant solution, but
173 # it works. If anyone tries to change this logic, make sure to validate
174 # it works. If anyone tries to change this logic, make sure to validate
174 # the whole test suite first!
175 # the whole test suite first!
175 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
176 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
176 return (bool(last_two_blanks_re.match(new_src)) or
177 return (bool(last_two_blanks_re.match(new_src)) or
177 bool(last_two_blanks_re2.match(new_src)) )
178 bool(last_two_blanks_re2.match(new_src)) )
178
179
179
180
180 def remove_comments(src):
181 def remove_comments(src):
181 """Remove all comments from input source.
182 """Remove all comments from input source.
182
183
183 Note: comments are NOT recognized inside of strings!
184 Note: comments are NOT recognized inside of strings!
184
185
185 Parameters
186 Parameters
186 ----------
187 ----------
187 src : string
188 src : string
188 A single or multiline input string.
189 A single or multiline input string.
189
190
190 Returns
191 Returns
191 -------
192 -------
192 String with all Python comments removed.
193 String with all Python comments removed.
193 """
194 """
194
195
195 return re.sub('#.*', '', src)
196 return re.sub('#.*', '', src)
196
197
197
198
198 def get_input_encoding():
199 def get_input_encoding():
199 """Return the default standard input encoding.
200 """Return the default standard input encoding.
200
201
201 If sys.stdin has no encoding, 'ascii' is returned."""
202 If sys.stdin has no encoding, 'ascii' is returned."""
202 # There are strange environments for which sys.stdin.encoding is None. We
203 # There are strange environments for which sys.stdin.encoding is None. We
203 # ensure that a valid encoding is returned.
204 # ensure that a valid encoding is returned.
204 encoding = getattr(sys.stdin, 'encoding', None)
205 encoding = getattr(sys.stdin, 'encoding', None)
205 if encoding is None:
206 if encoding is None:
206 encoding = 'ascii'
207 encoding = 'ascii'
207 return encoding
208 return encoding
208
209
209 #-----------------------------------------------------------------------------
210 #-----------------------------------------------------------------------------
210 # Classes and functions for normal Python syntax handling
211 # Classes and functions for normal Python syntax handling
211 #-----------------------------------------------------------------------------
212 #-----------------------------------------------------------------------------
212
213
213 class InputSplitter(object):
214 class InputSplitter(object):
214 """An object that can accumulate lines of Python source before execution.
215 """An object that can accumulate lines of Python source before execution.
215
216
216 This object is designed to be fed python source line-by-line, using
217 This object is designed to be fed python source line-by-line, using
217 :meth:`push`. It will return on each push whether the currently pushed
218 :meth:`push`. It will return on each push whether the currently pushed
218 code could be executed already. In addition, it provides a method called
219 code could be executed already. In addition, it provides a method called
219 :meth:`push_accepts_more` that can be used to query whether more input
220 :meth:`push_accepts_more` that can be used to query whether more input
220 can be pushed into a single interactive block.
221 can be pushed into a single interactive block.
221
222
222 This is a simple example of how an interactive terminal-based client can use
223 This is a simple example of how an interactive terminal-based client can use
223 this tool::
224 this tool::
224
225
225 isp = InputSplitter()
226 isp = InputSplitter()
226 while isp.push_accepts_more():
227 while isp.push_accepts_more():
227 indent = ' '*isp.indent_spaces
228 indent = ' '*isp.indent_spaces
228 prompt = '>>> ' + indent
229 prompt = '>>> ' + indent
229 line = indent + raw_input(prompt)
230 line = indent + raw_input(prompt)
230 isp.push(line)
231 isp.push(line)
231 print 'Input source was:\n', isp.source_reset(),
232 print 'Input source was:\n', isp.source_reset(),
232 """
233 """
233 # Number of spaces of indentation computed from input that has been pushed
234 # Number of spaces of indentation computed from input that has been pushed
234 # so far. This is the attributes callers should query to get the current
235 # so far. This is the attributes callers should query to get the current
235 # indentation level, in order to provide auto-indent facilities.
236 # indentation level, in order to provide auto-indent facilities.
236 indent_spaces = 0
237 indent_spaces = 0
237 # String, indicating the default input encoding. It is computed by default
238 # String, indicating the default input encoding. It is computed by default
238 # at initialization time via get_input_encoding(), but it can be reset by a
239 # at initialization time via get_input_encoding(), but it can be reset by a
239 # client with specific knowledge of the encoding.
240 # client with specific knowledge of the encoding.
240 encoding = ''
241 encoding = ''
241 # String where the current full source input is stored, properly encoded.
242 # String where the current full source input is stored, properly encoded.
242 # Reading this attribute is the normal way of querying the currently pushed
243 # Reading this attribute is the normal way of querying the currently pushed
243 # source code, that has been properly encoded.
244 # source code, that has been properly encoded.
244 source = ''
245 source = ''
245 # Code object corresponding to the current source. It is automatically
246 # Code object corresponding to the current source. It is automatically
246 # synced to the source, so it can be queried at any time to obtain the code
247 # synced to the source, so it can be queried at any time to obtain the code
247 # object; it will be None if the source doesn't compile to valid Python.
248 # object; it will be None if the source doesn't compile to valid Python.
248 code = None
249 code = None
249
250
250 # Private attributes
251 # Private attributes
251
252
252 # List with lines of input accumulated so far
253 # List with lines of input accumulated so far
253 _buffer = None
254 _buffer = None
254 # Command compiler
255 # Command compiler
255 _compile = None
256 _compile = None
256 # Mark when input has changed indentation all the way back to flush-left
257 # Mark when input has changed indentation all the way back to flush-left
257 _full_dedent = False
258 _full_dedent = False
258 # Boolean indicating whether the current block is complete
259 # Boolean indicating whether the current block is complete
259 _is_complete = None
260 _is_complete = None
260
261
261 def __init__(self):
262 def __init__(self):
262 """Create a new InputSplitter instance.
263 """Create a new InputSplitter instance.
263 """
264 """
264 self._buffer = []
265 self._buffer = []
265 self._compile = codeop.CommandCompiler()
266 self._compile = codeop.CommandCompiler()
266 self.encoding = get_input_encoding()
267 self.encoding = get_input_encoding()
267
268
268 def reset(self):
269 def reset(self):
269 """Reset the input buffer and associated state."""
270 """Reset the input buffer and associated state."""
270 self.indent_spaces = 0
271 self.indent_spaces = 0
271 self._buffer[:] = []
272 self._buffer[:] = []
272 self.source = ''
273 self.source = ''
273 self.code = None
274 self.code = None
274 self._is_complete = False
275 self._is_complete = False
275 self._full_dedent = False
276 self._full_dedent = False
276
277
277 def source_reset(self):
278 def source_reset(self):
278 """Return the input source and perform a full reset.
279 """Return the input source and perform a full reset.
279 """
280 """
280 out = self.source
281 out = self.source
281 self.reset()
282 self.reset()
282 return out
283 return out
283
284
284 def push(self, lines):
285 def push(self, lines):
285 """Push one or more lines of input.
286 """Push one or more lines of input.
286
287
287 This stores the given lines and returns a status code indicating
288 This stores the given lines and returns a status code indicating
288 whether the code forms a complete Python block or not.
289 whether the code forms a complete Python block or not.
289
290
290 Any exceptions generated in compilation are swallowed, but if an
291 Any exceptions generated in compilation are swallowed, but if an
291 exception was produced, the method returns True.
292 exception was produced, the method returns True.
292
293
293 Parameters
294 Parameters
294 ----------
295 ----------
295 lines : string
296 lines : string
296 One or more lines of Python input.
297 One or more lines of Python input.
297
298
298 Returns
299 Returns
299 -------
300 -------
300 is_complete : boolean
301 is_complete : boolean
301 True if the current input source (the result of the current input
302 True if the current input source (the result of the current input
302 plus prior inputs) forms a complete Python execution block. Note that
303 plus prior inputs) forms a complete Python execution block. Note that
303 this value is also stored as a private attribute (``_is_complete``), so it
304 this value is also stored as a private attribute (``_is_complete``), so it
304 can be queried at any time.
305 can be queried at any time.
305 """
306 """
306 self._store(lines)
307 self._store(lines)
307 source = self.source
308 source = self.source
308
309
309 # Before calling _compile(), reset the code object to None so that if an
310 # Before calling _compile(), reset the code object to None so that if an
310 # exception is raised in compilation, we don't mislead by having
311 # exception is raised in compilation, we don't mislead by having
311 # inconsistent code/source attributes.
312 # inconsistent code/source attributes.
312 self.code, self._is_complete = None, None
313 self.code, self._is_complete = None, None
313
314
314 # Honor termination lines properly
315 # Honor termination lines properly
315 if source.endswith('\\\n'):
316 if source.endswith('\\\n'):
316 return False
317 return False
317
318
318 self._update_indent(lines)
319 self._update_indent(lines)
319 try:
320 try:
320 self.code = self._compile(source, symbol="exec")
321 self.code = self._compile(source, symbol="exec")
321 # Invalid syntax can produce any of a number of different errors from
322 # Invalid syntax can produce any of a number of different errors from
322 # inside the compiler, so we have to catch them all. Syntax errors
323 # inside the compiler, so we have to catch them all. Syntax errors
323 # immediately produce a 'ready' block, so the invalid Python can be
324 # immediately produce a 'ready' block, so the invalid Python can be
324 # sent to the kernel for evaluation with possible ipython
325 # sent to the kernel for evaluation with possible ipython
325 # special-syntax conversion.
326 # special-syntax conversion.
326 except (SyntaxError, OverflowError, ValueError, TypeError,
327 except (SyntaxError, OverflowError, ValueError, TypeError,
327 MemoryError):
328 MemoryError):
328 self._is_complete = True
329 self._is_complete = True
329 else:
330 else:
330 # Compilation didn't produce any exceptions (though it may not have
331 # Compilation didn't produce any exceptions (though it may not have
331 # given a complete code object)
332 # given a complete code object)
332 self._is_complete = self.code is not None
333 self._is_complete = self.code is not None
333
334
334 return self._is_complete
335 return self._is_complete
335
336
336 def push_accepts_more(self):
337 def push_accepts_more(self):
337 """Return whether a block of interactive input can accept more input.
338 """Return whether a block of interactive input can accept more input.
338
339
339 This method is meant to be used by line-oriented frontends, who need to
340 This method is meant to be used by line-oriented frontends, who need to
340 guess whether a block is complete or not based solely on prior and
341 guess whether a block is complete or not based solely on prior and
341 current input lines. The InputSplitter considers it has a complete
342 current input lines. The InputSplitter considers it has a complete
342 interactive block and will not accept more input when either:
343 interactive block and will not accept more input when either:
343
344
344 * A SyntaxError is raised
345 * A SyntaxError is raised
345
346
346 * The code is complete and consists of a single line or a single
347 * The code is complete and consists of a single line or a single
347 non-compound statement
348 non-compound statement
348
349
349 * The code is complete and has a blank line at the end
350 * The code is complete and has a blank line at the end
350
351
351 If the current input produces a syntax error, this method immediately
352 If the current input produces a syntax error, this method immediately
352 returns False but does *not* raise the syntax error exception, as
353 returns False but does *not* raise the syntax error exception, as
353 typically clients will want to send invalid syntax to an execution
354 typically clients will want to send invalid syntax to an execution
354 backend which might convert the invalid syntax into valid Python via
355 backend which might convert the invalid syntax into valid Python via
355 one of the dynamic IPython mechanisms.
356 one of the dynamic IPython mechanisms.
356 """
357 """
357
358
358 # With incomplete input, unconditionally accept more
359 # With incomplete input, unconditionally accept more
359 # A syntax error also sets _is_complete to True - see push()
360 # A syntax error also sets _is_complete to True - see push()
360 if not self._is_complete:
361 if not self._is_complete:
361 #print("Not complete") # debug
362 #print("Not complete") # debug
362 return True
363 return True
363
364
364 # The user can make any (complete) input execute by leaving a blank line
365 # The user can make any (complete) input execute by leaving a blank line
365 last_line = self.source.splitlines()[-1]
366 last_line = self.source.splitlines()[-1]
366 if (not last_line) or last_line.isspace():
367 if (not last_line) or last_line.isspace():
367 #print("Blank line") # debug
368 #print("Blank line") # debug
368 return False
369 return False
369
370
370 # If there's just a single line or AST node, and we're flush left, as is
371 # If there's just a single line or AST node, and we're flush left, as is
371 # the case after a simple statement such as 'a=1', we want to execute it
372 # the case after a simple statement such as 'a=1', we want to execute it
372 # straight away.
373 # straight away.
373 if self.indent_spaces==0:
374 if self.indent_spaces==0:
374 if len(self.source.splitlines()) <= 1:
375 if len(self.source.splitlines()) <= 1:
375 return False
376 return False
376
377
377 try:
378 try:
378 code_ast = ast.parse(u''.join(self._buffer))
379 code_ast = ast.parse(u''.join(self._buffer))
379 except Exception:
380 except Exception:
380 #print("Can't parse AST") # debug
381 #print("Can't parse AST") # debug
381 return False
382 return False
382 else:
383 else:
383 if len(code_ast.body) == 1 and \
384 if len(code_ast.body) == 1 and \
384 not hasattr(code_ast.body[0], 'body'):
385 not hasattr(code_ast.body[0], 'body'):
385 #print("Simple statement") # debug
386 #print("Simple statement") # debug
386 return False
387 return False
387
388
388 # General fallback - accept more code
389 # General fallback - accept more code
389 return True
390 return True
390
391
391 #------------------------------------------------------------------------
392 #------------------------------------------------------------------------
392 # Private interface
393 # Private interface
393 #------------------------------------------------------------------------
394 #------------------------------------------------------------------------
394
395
395 def _find_indent(self, line):
396 def _find_indent(self, line):
396 """Compute the new indentation level for a single line.
397 """Compute the new indentation level for a single line.
397
398
398 Parameters
399 Parameters
399 ----------
400 ----------
400 line : str
401 line : str
401 A single new line of non-whitespace, non-comment Python input.
402 A single new line of non-whitespace, non-comment Python input.
402
403
403 Returns
404 Returns
404 -------
405 -------
405 indent_spaces : int
406 indent_spaces : int
406 New value for the indent level (it may be equal to self.indent_spaces
407 New value for the indent level (it may be equal to self.indent_spaces
407 if indentation doesn't change.
408 if indentation doesn't change.
408
409
409 full_dedent : boolean
410 full_dedent : boolean
410 Whether the new line causes a full flush-left dedent.
411 Whether the new line causes a full flush-left dedent.
411 """
412 """
412 indent_spaces = self.indent_spaces
413 indent_spaces = self.indent_spaces
413 full_dedent = self._full_dedent
414 full_dedent = self._full_dedent
414
415
415 inisp = num_ini_spaces(line)
416 inisp = num_ini_spaces(line)
416 if inisp < indent_spaces:
417 if inisp < indent_spaces:
417 indent_spaces = inisp
418 indent_spaces = inisp
418 if indent_spaces <= 0:
419 if indent_spaces <= 0:
419 #print 'Full dedent in text',self.source # dbg
420 #print 'Full dedent in text',self.source # dbg
420 full_dedent = True
421 full_dedent = True
421
422
422 if line.rstrip()[-1] == ':':
423 if line.rstrip()[-1] == ':':
423 indent_spaces += 4
424 indent_spaces += 4
424 elif dedent_re.match(line):
425 elif dedent_re.match(line):
425 indent_spaces -= 4
426 indent_spaces -= 4
426 if indent_spaces <= 0:
427 if indent_spaces <= 0:
427 full_dedent = True
428 full_dedent = True
428
429
429 # Safety
430 # Safety
430 if indent_spaces < 0:
431 if indent_spaces < 0:
431 indent_spaces = 0
432 indent_spaces = 0
432 #print 'safety' # dbg
433 #print 'safety' # dbg
433
434
434 return indent_spaces, full_dedent
435 return indent_spaces, full_dedent
435
436
436 def _update_indent(self, lines):
437 def _update_indent(self, lines):
437 for line in remove_comments(lines).splitlines():
438 for line in remove_comments(lines).splitlines():
438 if line and not line.isspace():
439 if line and not line.isspace():
439 self.indent_spaces, self._full_dedent = self._find_indent(line)
440 self.indent_spaces, self._full_dedent = self._find_indent(line)
440
441
441 def _store(self, lines, buffer=None, store='source'):
442 def _store(self, lines, buffer=None, store='source'):
442 """Store one or more lines of input.
443 """Store one or more lines of input.
443
444
444 If input lines are not newline-terminated, a newline is automatically
445 If input lines are not newline-terminated, a newline is automatically
445 appended."""
446 appended."""
446
447
447 if buffer is None:
448 if buffer is None:
448 buffer = self._buffer
449 buffer = self._buffer
449
450
450 if lines.endswith('\n'):
451 if lines.endswith('\n'):
451 buffer.append(lines)
452 buffer.append(lines)
452 else:
453 else:
453 buffer.append(lines+'\n')
454 buffer.append(lines+'\n')
454 setattr(self, store, self._set_source(buffer))
455 setattr(self, store, self._set_source(buffer))
455
456
456 def _set_source(self, buffer):
457 def _set_source(self, buffer):
457 return u''.join(buffer)
458 return u''.join(buffer)
458
459
459
460
460 class IPythonInputSplitter(InputSplitter):
461 class IPythonInputSplitter(InputSplitter):
461 """An input splitter that recognizes all of IPython's special syntax."""
462 """An input splitter that recognizes all of IPython's special syntax."""
462
463
463 # String with raw, untransformed input.
464 # String with raw, untransformed input.
464 source_raw = ''
465 source_raw = ''
465
466
466 # Flag to track when a transformer has stored input that it hasn't given
467 # Flag to track when a transformer has stored input that it hasn't given
467 # back yet.
468 # back yet.
468 transformer_accumulating = False
469 transformer_accumulating = False
469
470
470 # Flag to track when assemble_python_lines has stored input that it hasn't
471 # Flag to track when assemble_python_lines has stored input that it hasn't
471 # given back yet.
472 # given back yet.
472 within_python_line = False
473 within_python_line = False
473
474
474 # Private attributes
475 # Private attributes
475
476
476 # List with lines of raw input accumulated so far.
477 # List with lines of raw input accumulated so far.
477 _buffer_raw = None
478 _buffer_raw = None
478
479
479 def __init__(self, line_input_checker=True, physical_line_transforms=None,
480 def __init__(self, line_input_checker=True, physical_line_transforms=None,
480 logical_line_transforms=None, python_line_transforms=None):
481 logical_line_transforms=None, python_line_transforms=None):
481 super(IPythonInputSplitter, self).__init__()
482 super(IPythonInputSplitter, self).__init__()
482 self._buffer_raw = []
483 self._buffer_raw = []
483 self._validate = True
484 self._validate = True
484
485
485 if physical_line_transforms is not None:
486 if physical_line_transforms is not None:
486 self.physical_line_transforms = physical_line_transforms
487 self.physical_line_transforms = physical_line_transforms
487 else:
488 else:
488 self.physical_line_transforms = [leading_indent(),
489 self.physical_line_transforms = [leading_indent(),
489 classic_prompt(),
490 classic_prompt(),
490 ipy_prompt(),
491 ipy_prompt(),
492 strip_encoding_cookie(),
491 ]
493 ]
492
494
493 self.assemble_logical_lines = assemble_logical_lines()
495 self.assemble_logical_lines = assemble_logical_lines()
494 if logical_line_transforms is not None:
496 if logical_line_transforms is not None:
495 self.logical_line_transforms = logical_line_transforms
497 self.logical_line_transforms = logical_line_transforms
496 else:
498 else:
497 self.logical_line_transforms = [cellmagic(end_on_blank_line=line_input_checker),
499 self.logical_line_transforms = [cellmagic(end_on_blank_line=line_input_checker),
498 help_end(),
500 help_end(),
499 escaped_commands(),
501 escaped_commands(),
500 assign_from_magic(),
502 assign_from_magic(),
501 assign_from_system(),
503 assign_from_system(),
502 ]
504 ]
503
505
504 self.assemble_python_lines = assemble_python_lines()
506 self.assemble_python_lines = assemble_python_lines()
505 if python_line_transforms is not None:
507 if python_line_transforms is not None:
506 self.python_line_transforms = python_line_transforms
508 self.python_line_transforms = python_line_transforms
507 else:
509 else:
508 # We don't use any of these at present
510 # We don't use any of these at present
509 self.python_line_transforms = []
511 self.python_line_transforms = []
510
512
511 @property
513 @property
512 def transforms(self):
514 def transforms(self):
513 "Quick access to all transformers."
515 "Quick access to all transformers."
514 return self.physical_line_transforms + \
516 return self.physical_line_transforms + \
515 [self.assemble_logical_lines] + self.logical_line_transforms + \
517 [self.assemble_logical_lines] + self.logical_line_transforms + \
516 [self.assemble_python_lines] + self.python_line_transforms
518 [self.assemble_python_lines] + self.python_line_transforms
517
519
518 @property
520 @property
519 def transforms_in_use(self):
521 def transforms_in_use(self):
520 """Transformers, excluding logical line transformers if we're in a
522 """Transformers, excluding logical line transformers if we're in a
521 Python line."""
523 Python line."""
522 t = self.physical_line_transforms[:]
524 t = self.physical_line_transforms[:]
523 if not self.within_python_line:
525 if not self.within_python_line:
524 t += [self.assemble_logical_lines] + self.logical_line_transforms
526 t += [self.assemble_logical_lines] + self.logical_line_transforms
525 return t + [self.assemble_python_lines] + self.python_line_transforms
527 return t + [self.assemble_python_lines] + self.python_line_transforms
526
528
527 def reset(self):
529 def reset(self):
528 """Reset the input buffer and associated state."""
530 """Reset the input buffer and associated state."""
529 super(IPythonInputSplitter, self).reset()
531 super(IPythonInputSplitter, self).reset()
530 self._buffer_raw[:] = []
532 self._buffer_raw[:] = []
531 self.source_raw = ''
533 self.source_raw = ''
532 self.transformer_accumulating = False
534 self.transformer_accumulating = False
533 self.within_python_line = False
535 self.within_python_line = False
534 for t in self.transforms:
536 for t in self.transforms:
535 t.reset()
537 t.reset()
536
538
537 def flush_transformers(self):
539 def flush_transformers(self):
538 def _flush(transform, out):
540 def _flush(transform, out):
539 if out is not None:
541 if out is not None:
540 tmp = transform.push(out)
542 tmp = transform.push(out)
541 return tmp or transform.reset() or None
543 return tmp or transform.reset() or None
542 else:
544 else:
543 return transform.reset() or None
545 return transform.reset() or None
544
546
545 out = None
547 out = None
546 for t in self.transforms_in_use:
548 for t in self.transforms_in_use:
547 out = _flush(t, out)
549 out = _flush(t, out)
548
550
549 if out is not None:
551 if out is not None:
550 self._store(out)
552 self._store(out)
551
553
552 def source_raw_reset(self):
554 def source_raw_reset(self):
553 """Return input and raw source and perform a full reset.
555 """Return input and raw source and perform a full reset.
554 """
556 """
555 self.flush_transformers()
557 self.flush_transformers()
556 out = self.source
558 out = self.source
557 out_r = self.source_raw
559 out_r = self.source_raw
558 self.reset()
560 self.reset()
559 return out, out_r
561 return out, out_r
560
562
561 def source_reset(self):
563 def source_reset(self):
562 self.flush_transformers()
564 self.flush_transformers()
563 return super(IPythonInputSplitter, self).source_reset()
565 return super(IPythonInputSplitter, self).source_reset()
564
566
565 def push_accepts_more(self):
567 def push_accepts_more(self):
566 if self.transformer_accumulating:
568 if self.transformer_accumulating:
567 return True
569 return True
568 else:
570 else:
569 return super(IPythonInputSplitter, self).push_accepts_more()
571 return super(IPythonInputSplitter, self).push_accepts_more()
570
572
571 def transform_cell(self, cell):
573 def transform_cell(self, cell):
572 """Process and translate a cell of input.
574 """Process and translate a cell of input.
573 """
575 """
574 self.reset()
576 self.reset()
575 self.push(cell)
577 self.push(cell)
576 return self.source_reset()
578 return self.source_reset()
577
579
578 def push(self, lines):
580 def push(self, lines):
579 """Push one or more lines of IPython input.
581 """Push one or more lines of IPython input.
580
582
581 This stores the given lines and returns a status code indicating
583 This stores the given lines and returns a status code indicating
582 whether the code forms a complete Python block or not, after processing
584 whether the code forms a complete Python block or not, after processing
583 all input lines for special IPython syntax.
585 all input lines for special IPython syntax.
584
586
585 Any exceptions generated in compilation are swallowed, but if an
587 Any exceptions generated in compilation are swallowed, but if an
586 exception was produced, the method returns True.
588 exception was produced, the method returns True.
587
589
588 Parameters
590 Parameters
589 ----------
591 ----------
590 lines : string
592 lines : string
591 One or more lines of Python input.
593 One or more lines of Python input.
592
594
593 Returns
595 Returns
594 -------
596 -------
595 is_complete : boolean
597 is_complete : boolean
596 True if the current input source (the result of the current input
598 True if the current input source (the result of the current input
597 plus prior inputs) forms a complete Python execution block. Note that
599 plus prior inputs) forms a complete Python execution block. Note that
598 this value is also stored as a private attribute (_is_complete), so it
600 this value is also stored as a private attribute (_is_complete), so it
599 can be queried at any time.
601 can be queried at any time.
600 """
602 """
601
603
602 # We must ensure all input is pure unicode
604 # We must ensure all input is pure unicode
603 lines = cast_unicode(lines, self.encoding)
605 lines = cast_unicode(lines, self.encoding)
604
606
605 # ''.splitlines() --> [], but we need to push the empty line to transformers
607 # ''.splitlines() --> [], but we need to push the empty line to transformers
606 lines_list = lines.splitlines()
608 lines_list = lines.splitlines()
607 if not lines_list:
609 if not lines_list:
608 lines_list = ['']
610 lines_list = ['']
609
611
610 # Store raw source before applying any transformations to it. Note
612 # Store raw source before applying any transformations to it. Note
611 # that this must be done *after* the reset() call that would otherwise
613 # that this must be done *after* the reset() call that would otherwise
612 # flush the buffer.
614 # flush the buffer.
613 self._store(lines, self._buffer_raw, 'source_raw')
615 self._store(lines, self._buffer_raw, 'source_raw')
614
616
615 for line in lines_list:
617 for line in lines_list:
616 out = self.push_line(line)
618 out = self.push_line(line)
617
619
618 return out
620 return out
619
621
620 def push_line(self, line):
622 def push_line(self, line):
621 buf = self._buffer
623 buf = self._buffer
622
624
623 def _accumulating(dbg):
625 def _accumulating(dbg):
624 #print(dbg)
626 #print(dbg)
625 self.transformer_accumulating = True
627 self.transformer_accumulating = True
626 return False
628 return False
627
629
628 for transformer in self.physical_line_transforms:
630 for transformer in self.physical_line_transforms:
629 line = transformer.push(line)
631 line = transformer.push(line)
630 if line is None:
632 if line is None:
631 return _accumulating(transformer)
633 return _accumulating(transformer)
632
634
633 if not self.within_python_line:
635 if not self.within_python_line:
634 line = self.assemble_logical_lines.push(line)
636 line = self.assemble_logical_lines.push(line)
635 if line is None:
637 if line is None:
636 return _accumulating('acc logical line')
638 return _accumulating('acc logical line')
637
639
638 for transformer in self.logical_line_transforms:
640 for transformer in self.logical_line_transforms:
639 line = transformer.push(line)
641 line = transformer.push(line)
640 if line is None:
642 if line is None:
641 return _accumulating(transformer)
643 return _accumulating(transformer)
642
644
643 line = self.assemble_python_lines.push(line)
645 line = self.assemble_python_lines.push(line)
644 if line is None:
646 if line is None:
645 self.within_python_line = True
647 self.within_python_line = True
646 return _accumulating('acc python line')
648 return _accumulating('acc python line')
647 else:
649 else:
648 self.within_python_line = False
650 self.within_python_line = False
649
651
650 for transformer in self.python_line_transforms:
652 for transformer in self.python_line_transforms:
651 line = transformer.push(line)
653 line = transformer.push(line)
652 if line is None:
654 if line is None:
653 return _accumulating(transformer)
655 return _accumulating(transformer)
654
656
655 #print("transformers clear") #debug
657 #print("transformers clear") #debug
656 self.transformer_accumulating = False
658 self.transformer_accumulating = False
657 return super(IPythonInputSplitter, self).push(line)
659 return super(IPythonInputSplitter, self).push(line)
@@ -1,447 +1,472 b''
1 import abc
1 import abc
2 import functools
2 import functools
3 import re
3 import re
4 from StringIO import StringIO
4 from StringIO import StringIO
5
5
6 from IPython.core.splitinput import LineInfo
6 from IPython.core.splitinput import LineInfo
7 from IPython.utils import tokenize2
7 from IPython.utils import tokenize2
8 from IPython.utils.openpy import cookie_comment_re
8 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
9 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
9
10
10 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
11 # Globals
12 # Globals
12 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
13
14
14 # The escape sequences that define the syntax transformations IPython will
15 # The escape sequences that define the syntax transformations IPython will
15 # apply to user input. These can NOT be just changed here: many regular
16 # apply to user input. These can NOT be just changed here: many regular
16 # expressions and other parts of the code may use their hardcoded values, and
17 # expressions and other parts of the code may use their hardcoded values, and
17 # for all intents and purposes they constitute the 'IPython syntax', so they
18 # for all intents and purposes they constitute the 'IPython syntax', so they
18 # should be considered fixed.
19 # should be considered fixed.
19
20
20 ESC_SHELL = '!' # Send line to underlying system shell
21 ESC_SHELL = '!' # Send line to underlying system shell
21 ESC_SH_CAP = '!!' # Send line to system shell and capture output
22 ESC_SH_CAP = '!!' # Send line to system shell and capture output
22 ESC_HELP = '?' # Find information about object
23 ESC_HELP = '?' # Find information about object
23 ESC_HELP2 = '??' # Find extra-detailed information about object
24 ESC_HELP2 = '??' # Find extra-detailed information about object
24 ESC_MAGIC = '%' # Call magic function
25 ESC_MAGIC = '%' # Call magic function
25 ESC_MAGIC2 = '%%' # Call cell-magic function
26 ESC_MAGIC2 = '%%' # Call cell-magic function
26 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
27 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
27 ESC_QUOTE2 = ';' # Quote all args as a single string, call
28 ESC_QUOTE2 = ';' # Quote all args as a single string, call
28 ESC_PAREN = '/' # Call first argument with rest of line as arguments
29 ESC_PAREN = '/' # Call first argument with rest of line as arguments
29
30
30 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
31 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
31 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
32 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
32 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
33 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
33
34
34
35
35 class InputTransformer(object):
36 class InputTransformer(object):
36 """Abstract base class for line-based input transformers."""
37 """Abstract base class for line-based input transformers."""
37 __metaclass__ = abc.ABCMeta
38 __metaclass__ = abc.ABCMeta
38
39
39 @abc.abstractmethod
40 @abc.abstractmethod
40 def push(self, line):
41 def push(self, line):
41 """Send a line of input to the transformer, returning the transformed
42 """Send a line of input to the transformer, returning the transformed
42 input or None if the transformer is waiting for more input.
43 input or None if the transformer is waiting for more input.
43
44
44 Must be overridden by subclasses.
45 Must be overridden by subclasses.
45 """
46 """
46 pass
47 pass
47
48
48 @abc.abstractmethod
49 @abc.abstractmethod
49 def reset(self):
50 def reset(self):
50 """Return, transformed any lines that the transformer has accumulated,
51 """Return, transformed any lines that the transformer has accumulated,
51 and reset its internal state.
52 and reset its internal state.
52
53
53 Must be overridden by subclasses.
54 Must be overridden by subclasses.
54 """
55 """
55 pass
56 pass
56
57
57 @classmethod
58 @classmethod
58 def wrap(cls, func):
59 def wrap(cls, func):
59 """Can be used by subclasses as a decorator, to return a factory that
60 """Can be used by subclasses as a decorator, to return a factory that
60 will allow instantiation with the decorated object.
61 will allow instantiation with the decorated object.
61 """
62 """
62 @functools.wraps(func)
63 @functools.wraps(func)
63 def transformer_factory(**kwargs):
64 def transformer_factory(**kwargs):
64 return cls(func, **kwargs)
65 return cls(func, **kwargs)
65
66
66 return transformer_factory
67 return transformer_factory
67
68
68 class StatelessInputTransformer(InputTransformer):
69 class StatelessInputTransformer(InputTransformer):
69 """Wrapper for a stateless input transformer implemented as a function."""
70 """Wrapper for a stateless input transformer implemented as a function."""
70 def __init__(self, func):
71 def __init__(self, func):
71 self.func = func
72 self.func = func
72
73
73 def __repr__(self):
74 def __repr__(self):
74 return "StatelessInputTransformer(func={0!r})".format(self.func)
75 return "StatelessInputTransformer(func={0!r})".format(self.func)
75
76
76 def push(self, line):
77 def push(self, line):
77 """Send a line of input to the transformer, returning the
78 """Send a line of input to the transformer, returning the
78 transformed input."""
79 transformed input."""
79 return self.func(line)
80 return self.func(line)
80
81
81 def reset(self):
82 def reset(self):
82 """No-op - exists for compatibility."""
83 """No-op - exists for compatibility."""
83 pass
84 pass
84
85
85 class CoroutineInputTransformer(InputTransformer):
86 class CoroutineInputTransformer(InputTransformer):
86 """Wrapper for an input transformer implemented as a coroutine."""
87 """Wrapper for an input transformer implemented as a coroutine."""
87 def __init__(self, coro, **kwargs):
88 def __init__(self, coro, **kwargs):
88 # Prime it
89 # Prime it
89 self.coro = coro(**kwargs)
90 self.coro = coro(**kwargs)
90 next(self.coro)
91 next(self.coro)
91
92
92 def __repr__(self):
93 def __repr__(self):
93 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
94 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
94
95
95 def push(self, line):
96 def push(self, line):
96 """Send a line of input to the transformer, returning the
97 """Send a line of input to the transformer, returning the
97 transformed input or None if the transformer is waiting for more
98 transformed input or None if the transformer is waiting for more
98 input.
99 input.
99 """
100 """
100 return self.coro.send(line)
101 return self.coro.send(line)
101
102
102 def reset(self):
103 def reset(self):
103 """Return, transformed any lines that the transformer has
104 """Return, transformed any lines that the transformer has
104 accumulated, and reset its internal state.
105 accumulated, and reset its internal state.
105 """
106 """
106 return self.coro.send(None)
107 return self.coro.send(None)
107
108
108 class TokenInputTransformer(InputTransformer):
109 class TokenInputTransformer(InputTransformer):
109 """Wrapper for a token-based input transformer.
110 """Wrapper for a token-based input transformer.
110
111
111 func should accept a list of tokens (5-tuples, see tokenize docs), and
112 func should accept a list of tokens (5-tuples, see tokenize docs), and
112 return an iterable which can be passed to tokenize.untokenize().
113 return an iterable which can be passed to tokenize.untokenize().
113 """
114 """
114 def __init__(self, func):
115 def __init__(self, func):
115 self.func = func
116 self.func = func
116 self.current_line = ""
117 self.current_line = ""
117 self.line_used = False
118 self.line_used = False
118 self.reset_tokenizer()
119 self.reset_tokenizer()
119
120
120 def reset_tokenizer(self):
121 def reset_tokenizer(self):
121 self.tokenizer = generate_tokens(self.get_line)
122 self.tokenizer = generate_tokens(self.get_line)
122
123
123 def get_line(self):
124 def get_line(self):
124 if self.line_used:
125 if self.line_used:
125 raise TokenError
126 raise TokenError
126 self.line_used = True
127 self.line_used = True
127 return self.current_line
128 return self.current_line
128
129
129 def push(self, line):
130 def push(self, line):
130 self.current_line += line + "\n"
131 self.current_line += line + "\n"
131 if self.current_line.isspace():
132 if self.current_line.isspace():
132 return self.reset()
133 return self.reset()
133
134
134 self.line_used = False
135 self.line_used = False
135 tokens = []
136 tokens = []
136 stop_at_NL = False
137 stop_at_NL = False
137 try:
138 try:
138 for intok in self.tokenizer:
139 for intok in self.tokenizer:
139 tokens.append(intok)
140 tokens.append(intok)
140 t = intok[0]
141 t = intok[0]
141 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
142 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
142 # Stop before we try to pull a line we don't have yet
143 # Stop before we try to pull a line we don't have yet
143 break
144 break
144 elif t == tokenize2.ERRORTOKEN:
145 elif t == tokenize2.ERRORTOKEN:
145 stop_at_NL = True
146 stop_at_NL = True
146 except TokenError:
147 except TokenError:
147 # Multi-line statement - stop and try again with the next line
148 # Multi-line statement - stop and try again with the next line
148 self.reset_tokenizer()
149 self.reset_tokenizer()
149 return None
150 return None
150
151
151 return self.output(tokens)
152 return self.output(tokens)
152
153
153 def output(self, tokens):
154 def output(self, tokens):
154 self.current_line = ""
155 self.current_line = ""
155 self.reset_tokenizer()
156 self.reset_tokenizer()
156 return untokenize(self.func(tokens)).rstrip('\n')
157 return untokenize(self.func(tokens)).rstrip('\n')
157
158
158 def reset(self):
159 def reset(self):
159 l = self.current_line
160 l = self.current_line
160 self.current_line = ""
161 self.current_line = ""
161 self.reset_tokenizer()
162 self.reset_tokenizer()
162 if l:
163 if l:
163 return l.rstrip('\n')
164 return l.rstrip('\n')
164
165
165 class assemble_python_lines(TokenInputTransformer):
166 class assemble_python_lines(TokenInputTransformer):
166 def __init__(self):
167 def __init__(self):
167 super(assemble_python_lines, self).__init__(None)
168 super(assemble_python_lines, self).__init__(None)
168
169
169 def output(self, tokens):
170 def output(self, tokens):
170 return self.reset()
171 return self.reset()
171
172
172 @CoroutineInputTransformer.wrap
173 @CoroutineInputTransformer.wrap
173 def assemble_logical_lines():
174 def assemble_logical_lines():
174 """Join lines following explicit line continuations (\)"""
175 """Join lines following explicit line continuations (\)"""
175 line = ''
176 line = ''
176 while True:
177 while True:
177 line = (yield line)
178 line = (yield line)
178 if not line or line.isspace():
179 if not line or line.isspace():
179 continue
180 continue
180
181
181 parts = []
182 parts = []
182 while line is not None:
183 while line is not None:
183 if line.endswith('\\') and (not has_comment(line)):
184 if line.endswith('\\') and (not has_comment(line)):
184 parts.append(line[:-1])
185 parts.append(line[:-1])
185 line = (yield None) # Get another line
186 line = (yield None) # Get another line
186 else:
187 else:
187 parts.append(line)
188 parts.append(line)
188 break
189 break
189
190
190 # Output
191 # Output
191 line = ''.join(parts)
192 line = ''.join(parts)
192
193
193 # Utilities
194 # Utilities
194 def _make_help_call(target, esc, lspace, next_input=None):
195 def _make_help_call(target, esc, lspace, next_input=None):
195 """Prepares a pinfo(2)/psearch call from a target name and the escape
196 """Prepares a pinfo(2)/psearch call from a target name and the escape
196 (i.e. ? or ??)"""
197 (i.e. ? or ??)"""
197 method = 'pinfo2' if esc == '??' \
198 method = 'pinfo2' if esc == '??' \
198 else 'psearch' if '*' in target \
199 else 'psearch' if '*' in target \
199 else 'pinfo'
200 else 'pinfo'
200 arg = " ".join([method, target])
201 arg = " ".join([method, target])
201 if next_input is None:
202 if next_input is None:
202 return '%sget_ipython().magic(%r)' % (lspace, arg)
203 return '%sget_ipython().magic(%r)' % (lspace, arg)
203 else:
204 else:
204 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
205 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
205 (lspace, next_input, arg)
206 (lspace, next_input, arg)
206
207
207 # These define the transformations for the different escape characters.
208 # These define the transformations for the different escape characters.
208 def _tr_system(line_info):
209 def _tr_system(line_info):
209 "Translate lines escaped with: !"
210 "Translate lines escaped with: !"
210 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
211 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
211 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
212 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
212
213
213 def _tr_system2(line_info):
214 def _tr_system2(line_info):
214 "Translate lines escaped with: !!"
215 "Translate lines escaped with: !!"
215 cmd = line_info.line.lstrip()[2:]
216 cmd = line_info.line.lstrip()[2:]
216 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
217 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
217
218
218 def _tr_help(line_info):
219 def _tr_help(line_info):
219 "Translate lines escaped with: ?/??"
220 "Translate lines escaped with: ?/??"
220 # A naked help line should just fire the intro help screen
221 # A naked help line should just fire the intro help screen
221 if not line_info.line[1:]:
222 if not line_info.line[1:]:
222 return 'get_ipython().show_usage()'
223 return 'get_ipython().show_usage()'
223
224
224 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
225 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
225
226
226 def _tr_magic(line_info):
227 def _tr_magic(line_info):
227 "Translate lines escaped with: %"
228 "Translate lines escaped with: %"
228 tpl = '%sget_ipython().magic(%r)'
229 tpl = '%sget_ipython().magic(%r)'
229 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
230 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
230 return tpl % (line_info.pre, cmd)
231 return tpl % (line_info.pre, cmd)
231
232
232 def _tr_quote(line_info):
233 def _tr_quote(line_info):
233 "Translate lines escaped with: ,"
234 "Translate lines escaped with: ,"
234 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
235 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
235 '", "'.join(line_info.the_rest.split()) )
236 '", "'.join(line_info.the_rest.split()) )
236
237
237 def _tr_quote2(line_info):
238 def _tr_quote2(line_info):
238 "Translate lines escaped with: ;"
239 "Translate lines escaped with: ;"
239 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
240 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
240 line_info.the_rest)
241 line_info.the_rest)
241
242
242 def _tr_paren(line_info):
243 def _tr_paren(line_info):
243 "Translate lines escaped with: /"
244 "Translate lines escaped with: /"
244 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
245 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
245 ", ".join(line_info.the_rest.split()))
246 ", ".join(line_info.the_rest.split()))
246
247
247 tr = { ESC_SHELL : _tr_system,
248 tr = { ESC_SHELL : _tr_system,
248 ESC_SH_CAP : _tr_system2,
249 ESC_SH_CAP : _tr_system2,
249 ESC_HELP : _tr_help,
250 ESC_HELP : _tr_help,
250 ESC_HELP2 : _tr_help,
251 ESC_HELP2 : _tr_help,
251 ESC_MAGIC : _tr_magic,
252 ESC_MAGIC : _tr_magic,
252 ESC_QUOTE : _tr_quote,
253 ESC_QUOTE : _tr_quote,
253 ESC_QUOTE2 : _tr_quote2,
254 ESC_QUOTE2 : _tr_quote2,
254 ESC_PAREN : _tr_paren }
255 ESC_PAREN : _tr_paren }
255
256
256 @StatelessInputTransformer.wrap
257 @StatelessInputTransformer.wrap
257 def escaped_commands(line):
258 def escaped_commands(line):
258 """Transform escaped commands - %magic, !system, ?help + various autocalls.
259 """Transform escaped commands - %magic, !system, ?help + various autocalls.
259 """
260 """
260 if not line or line.isspace():
261 if not line or line.isspace():
261 return line
262 return line
262 lineinf = LineInfo(line)
263 lineinf = LineInfo(line)
263 if lineinf.esc not in tr:
264 if lineinf.esc not in tr:
264 return line
265 return line
265
266
266 return tr[lineinf.esc](lineinf)
267 return tr[lineinf.esc](lineinf)
267
268
268 _initial_space_re = re.compile(r'\s*')
269 _initial_space_re = re.compile(r'\s*')
269
270
270 _help_end_re = re.compile(r"""(%{0,2}
271 _help_end_re = re.compile(r"""(%{0,2}
271 [a-zA-Z_*][\w*]* # Variable name
272 [a-zA-Z_*][\w*]* # Variable name
272 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
273 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
273 )
274 )
274 (\?\??)$ # ? or ??""",
275 (\?\??)$ # ? or ??""",
275 re.VERBOSE)
276 re.VERBOSE)
276
277
277 def has_comment(src):
278 def has_comment(src):
278 """Indicate whether an input line has (i.e. ends in, or is) a comment.
279 """Indicate whether an input line has (i.e. ends in, or is) a comment.
279
280
280 This uses tokenize, so it can distinguish comments from # inside strings.
281 This uses tokenize, so it can distinguish comments from # inside strings.
281
282
282 Parameters
283 Parameters
283 ----------
284 ----------
284 src : string
285 src : string
285 A single line input string.
286 A single line input string.
286
287
287 Returns
288 Returns
288 -------
289 -------
289 comment : bool
290 comment : bool
290 True if source has a comment.
291 True if source has a comment.
291 """
292 """
292 readline = StringIO(src).readline
293 readline = StringIO(src).readline
293 toktypes = set()
294 toktypes = set()
294 try:
295 try:
295 for t in generate_tokens(readline):
296 for t in generate_tokens(readline):
296 toktypes.add(t[0])
297 toktypes.add(t[0])
297 except TokenError:
298 except TokenError:
298 pass
299 pass
299 return(tokenize2.COMMENT in toktypes)
300 return(tokenize2.COMMENT in toktypes)
300
301
301
302
302 @StatelessInputTransformer.wrap
303 @StatelessInputTransformer.wrap
303 def help_end(line):
304 def help_end(line):
304 """Translate lines with ?/?? at the end"""
305 """Translate lines with ?/?? at the end"""
305 m = _help_end_re.search(line)
306 m = _help_end_re.search(line)
306 if m is None or has_comment(line):
307 if m is None or has_comment(line):
307 return line
308 return line
308 target = m.group(1)
309 target = m.group(1)
309 esc = m.group(3)
310 esc = m.group(3)
310 lspace = _initial_space_re.match(line).group(0)
311 lspace = _initial_space_re.match(line).group(0)
311
312
312 # If we're mid-command, put it back on the next prompt for the user.
313 # If we're mid-command, put it back on the next prompt for the user.
313 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
314 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
314
315
315 return _make_help_call(target, esc, lspace, next_input)
316 return _make_help_call(target, esc, lspace, next_input)
316
317
317
318
318 @CoroutineInputTransformer.wrap
319 @CoroutineInputTransformer.wrap
319 def cellmagic(end_on_blank_line=False):
320 def cellmagic(end_on_blank_line=False):
320 """Captures & transforms cell magics.
321 """Captures & transforms cell magics.
321
322
322 After a cell magic is started, this stores up any lines it gets until it is
323 After a cell magic is started, this stores up any lines it gets until it is
323 reset (sent None).
324 reset (sent None).
324 """
325 """
325 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
326 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
326 cellmagic_help_re = re.compile('%%\w+\?')
327 cellmagic_help_re = re.compile('%%\w+\?')
327 line = ''
328 line = ''
328 while True:
329 while True:
329 line = (yield line)
330 line = (yield line)
330 if (not line) or (not line.startswith(ESC_MAGIC2)):
331 if (not line) or (not line.startswith(ESC_MAGIC2)):
331 continue
332 continue
332
333
333 if cellmagic_help_re.match(line):
334 if cellmagic_help_re.match(line):
334 # This case will be handled by help_end
335 # This case will be handled by help_end
335 continue
336 continue
336
337
337 first = line
338 first = line
338 body = []
339 body = []
339 line = (yield None)
340 line = (yield None)
340 while (line is not None) and \
341 while (line is not None) and \
341 ((line.strip() != '') or not end_on_blank_line):
342 ((line.strip() != '') or not end_on_blank_line):
342 body.append(line)
343 body.append(line)
343 line = (yield None)
344 line = (yield None)
344
345
345 # Output
346 # Output
346 magic_name, _, first = first.partition(' ')
347 magic_name, _, first = first.partition(' ')
347 magic_name = magic_name.lstrip(ESC_MAGIC2)
348 magic_name = magic_name.lstrip(ESC_MAGIC2)
348 line = tpl % (magic_name, first, u'\n'.join(body))
349 line = tpl % (magic_name, first, u'\n'.join(body))
349
350
350
351
351 def _strip_prompts(prompt_re):
352 def _strip_prompts(prompt_re):
352 """Remove matching input prompts from a block of input."""
353 """Remove matching input prompts from a block of input."""
353 line = ''
354 line = ''
354 while True:
355 while True:
355 line = (yield line)
356 line = (yield line)
356
357
357 # First line of cell
358 # First line of cell
358 if line is None:
359 if line is None:
359 continue
360 continue
360 out, n1 = prompt_re.subn('', line, count=1)
361 out, n1 = prompt_re.subn('', line, count=1)
361 line = (yield out)
362 line = (yield out)
362
363
363 # Second line of cell, because people often copy from just after the
364 # Second line of cell, because people often copy from just after the
364 # first prompt, so we might not see it in the first line.
365 # first prompt, so we might not see it in the first line.
365 if line is None:
366 if line is None:
366 continue
367 continue
367 out, n2 = prompt_re.subn('', line, count=1)
368 out, n2 = prompt_re.subn('', line, count=1)
368 line = (yield out)
369 line = (yield out)
369
370
370 if n1 or n2:
371 if n1 or n2:
371 # Found the input prompt in the first two lines - check for it in
372 # Found the input prompt in the first two lines - check for it in
372 # the rest of the cell as well.
373 # the rest of the cell as well.
373 while line is not None:
374 while line is not None:
374 line = (yield prompt_re.sub('', line, count=1))
375 line = (yield prompt_re.sub('', line, count=1))
375
376
376 else:
377 else:
377 # Prompts not in input - wait for reset
378 # Prompts not in input - wait for reset
378 while line is not None:
379 while line is not None:
379 line = (yield line)
380 line = (yield line)
380
381
381 @CoroutineInputTransformer.wrap
382 @CoroutineInputTransformer.wrap
382 def classic_prompt():
383 def classic_prompt():
383 """Strip the >>>/... prompts of the Python interactive shell."""
384 """Strip the >>>/... prompts of the Python interactive shell."""
384 # FIXME: non-capturing version (?:...) usable?
385 # FIXME: non-capturing version (?:...) usable?
385 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
386 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
386 return _strip_prompts(prompt_re)
387 return _strip_prompts(prompt_re)
387
388
388 @CoroutineInputTransformer.wrap
389 @CoroutineInputTransformer.wrap
389 def ipy_prompt():
390 def ipy_prompt():
390 """Strip IPython's In [1]:/...: prompts."""
391 """Strip IPython's In [1]:/...: prompts."""
391 # FIXME: non-capturing version (?:...) usable?
392 # FIXME: non-capturing version (?:...) usable?
392 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
393 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
393 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
394 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
394 return _strip_prompts(prompt_re)
395 return _strip_prompts(prompt_re)
395
396
396
397
397 @CoroutineInputTransformer.wrap
398 @CoroutineInputTransformer.wrap
398 def leading_indent():
399 def leading_indent():
399 """Remove leading indentation.
400 """Remove leading indentation.
400
401
401 If the first line starts with a spaces or tabs, the same whitespace will be
402 If the first line starts with a spaces or tabs, the same whitespace will be
402 removed from each following line until it is reset.
403 removed from each following line until it is reset.
403 """
404 """
404 space_re = re.compile(r'^[ \t]+')
405 space_re = re.compile(r'^[ \t]+')
405 line = ''
406 line = ''
406 while True:
407 while True:
407 line = (yield line)
408 line = (yield line)
408
409
409 if line is None:
410 if line is None:
410 continue
411 continue
411
412
412 m = space_re.match(line)
413 m = space_re.match(line)
413 if m:
414 if m:
414 space = m.group(0)
415 space = m.group(0)
415 while line is not None:
416 while line is not None:
416 if line.startswith(space):
417 if line.startswith(space):
417 line = line[len(space):]
418 line = line[len(space):]
418 line = (yield line)
419 line = (yield line)
419 else:
420 else:
420 # No leading spaces - wait for reset
421 # No leading spaces - wait for reset
421 while line is not None:
422 while line is not None:
422 line = (yield line)
423 line = (yield line)
423
424
424
425
426 @CoroutineInputTransformer.wrap
427 def strip_encoding_cookie():
428 """Remove encoding comment if found in first two lines
429
430 If the first or second line has the `# coding: utf-8` comment,
431 it will be removed.
432 """
433 line = ''
434 while True:
435 line = (yield line)
436 # check comment on first two lines
437 for i in range(2):
438 if line is None:
439 break
440 if cookie_comment_re.match(line):
441 line = (yield "")
442 else:
443 line = (yield line)
444
445 # no-op on the rest of the cell
446 while line is not None:
447 line = (yield line)
448
449
425 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
450 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
426 r'\s*=\s*!\s*(?P<cmd>.*)')
451 r'\s*=\s*!\s*(?P<cmd>.*)')
427 assign_system_template = '%s = get_ipython().getoutput(%r)'
452 assign_system_template = '%s = get_ipython().getoutput(%r)'
428 @StatelessInputTransformer.wrap
453 @StatelessInputTransformer.wrap
429 def assign_from_system(line):
454 def assign_from_system(line):
430 """Transform assignment from system commands (e.g. files = !ls)"""
455 """Transform assignment from system commands (e.g. files = !ls)"""
431 m = assign_system_re.match(line)
456 m = assign_system_re.match(line)
432 if m is None:
457 if m is None:
433 return line
458 return line
434
459
435 return assign_system_template % m.group('lhs', 'cmd')
460 return assign_system_template % m.group('lhs', 'cmd')
436
461
437 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
462 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
438 r'\s*=\s*%\s*(?P<cmd>.*)')
463 r'\s*=\s*%\s*(?P<cmd>.*)')
439 assign_magic_template = '%s = get_ipython().magic(%r)'
464 assign_magic_template = '%s = get_ipython().magic(%r)'
440 @StatelessInputTransformer.wrap
465 @StatelessInputTransformer.wrap
441 def assign_from_magic(line):
466 def assign_from_magic(line):
442 """Transform assignment from magic commands (e.g. a = %who_ls)"""
467 """Transform assignment from magic commands (e.g. a = %who_ls)"""
443 m = assign_magic_re.match(line)
468 m = assign_magic_re.match(line)
444 if m is None:
469 if m is None:
445 return line
470 return line
446
471
447 return assign_magic_template % m.group('lhs', 'cmd')
472 return assign_magic_template % m.group('lhs', 'cmd')
General Comments 0
You need to be logged in to leave comments. Login now