##// END OF EJS Templates
Revised input transformation framework.
Thomas Kluyver -
Show More
@@ -1,651 +1,712 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.core.splitinput import split_user_input, LineInfo
74 from IPython.core.splitinput import split_user_input, LineInfo
75 from IPython.utils.py3compat import cast_unicode
75 from IPython.utils.py3compat import cast_unicode
76 from IPython.core.inputtransformer import (leading_indent,
76 from IPython.core.inputtransformer import (leading_indent,
77 classic_prompt,
77 classic_prompt,
78 ipy_prompt,
78 ipy_prompt,
79 cellmagic,
79 cellmagic,
80 assemble_logical_lines,
80 assemble_logical_lines,
81 help_end,
81 help_end,
82 escaped_transformer,
82 escaped_transformer,
83 assign_from_magic,
83 assign_from_magic,
84 assign_from_system,
84 assign_from_system,
85 assemble_python_lines,
85 )
86 )
86
87
87 # Temporary!
88 # Temporary!
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 # Input mode
250 # Input mode
250 input_mode = 'line'
251 input_mode = 'line'
251
252
252 # Private attributes
253 # Private attributes
253
254
254 # List with lines of input accumulated so far
255 # List with lines of input accumulated so far
255 _buffer = None
256 _buffer = None
256 # Command compiler
257 # Command compiler
257 _compile = None
258 _compile = None
258 # Mark when input has changed indentation all the way back to flush-left
259 # Mark when input has changed indentation all the way back to flush-left
259 _full_dedent = False
260 _full_dedent = False
260 # Boolean indicating whether the current block is complete
261 # Boolean indicating whether the current block is complete
261 _is_complete = None
262 _is_complete = None
262
263
263 def __init__(self, input_mode=None):
264 def __init__(self, input_mode=None):
264 """Create a new InputSplitter instance.
265 """Create a new InputSplitter instance.
265
266
266 Parameters
267 Parameters
267 ----------
268 ----------
268 input_mode : str
269 input_mode : str
269
270
270 One of ['line', 'cell']; default is 'line'.
271 One of ['line', 'cell']; default is 'line'.
271
272
272 The input_mode parameter controls how new inputs are used when fed via
273 The input_mode parameter controls how new inputs are used when fed via
273 the :meth:`push` method:
274 the :meth:`push` method:
274
275
275 - 'line': meant for line-oriented clients, inputs are appended one at a
276 - 'line': meant for line-oriented clients, inputs are appended one at a
276 time to the internal buffer and the whole buffer is compiled.
277 time to the internal buffer and the whole buffer is compiled.
277
278
278 - 'cell': meant for clients that can edit multi-line 'cells' of text at
279 - 'cell': meant for clients that can edit multi-line 'cells' of text at
279 a time. A cell can contain one or more blocks that can be compile in
280 a time. A cell can contain one or more blocks that can be compile in
280 'single' mode by Python. In this mode, each new input new input
281 'single' mode by Python. In this mode, each new input new input
281 completely replaces all prior inputs. Cell mode is thus equivalent
282 completely replaces all prior inputs. Cell mode is thus equivalent
282 to prepending a full reset() to every push() call.
283 to prepending a full reset() to every push() call.
283 """
284 """
284 self._buffer = []
285 self._buffer = []
285 self._compile = codeop.CommandCompiler()
286 self._compile = codeop.CommandCompiler()
286 self.encoding = get_input_encoding()
287 self.encoding = get_input_encoding()
287 self.input_mode = InputSplitter.input_mode if input_mode is None \
288 self.input_mode = InputSplitter.input_mode if input_mode is None \
288 else input_mode
289 else input_mode
289
290
290 def reset(self):
291 def reset(self):
291 """Reset the input buffer and associated state."""
292 """Reset the input buffer and associated state."""
292 self.indent_spaces = 0
293 self.indent_spaces = 0
293 self._buffer[:] = []
294 self._buffer[:] = []
294 self.source = ''
295 self.source = ''
295 self.code = None
296 self.code = None
296 self._is_complete = False
297 self._is_complete = False
297 self._full_dedent = False
298 self._full_dedent = False
298
299
299 def source_reset(self):
300 def source_reset(self):
300 """Return the input source and perform a full reset.
301 """Return the input source and perform a full reset.
301 """
302 """
302 out = self.source
303 out = self.source
303 self.reset()
304 self.reset()
304 return out
305 return out
305
306
306 def push(self, lines):
307 def push(self, lines):
307 """Push one or more lines of input.
308 """Push one or more lines of input.
308
309
309 This stores the given lines and returns a status code indicating
310 This stores the given lines and returns a status code indicating
310 whether the code forms a complete Python block or not.
311 whether the code forms a complete Python block or not.
311
312
312 Any exceptions generated in compilation are swallowed, but if an
313 Any exceptions generated in compilation are swallowed, but if an
313 exception was produced, the method returns True.
314 exception was produced, the method returns True.
314
315
315 Parameters
316 Parameters
316 ----------
317 ----------
317 lines : string
318 lines : string
318 One or more lines of Python input.
319 One or more lines of Python input.
319
320
320 Returns
321 Returns
321 -------
322 -------
322 is_complete : boolean
323 is_complete : boolean
323 True if the current input source (the result of the current input
324 True if the current input source (the result of the current input
324 plus prior inputs) forms a complete Python execution block. Note that
325 plus prior inputs) forms a complete Python execution block. Note that
325 this value is also stored as a private attribute (``_is_complete``), so it
326 this value is also stored as a private attribute (``_is_complete``), so it
326 can be queried at any time.
327 can be queried at any time.
327 """
328 """
328 if self.input_mode == 'cell':
329 if self.input_mode == 'cell':
329 self.reset()
330 self.reset()
330
331
331 self._store(lines)
332 self._store(lines)
332 source = self.source
333 source = self.source
333
334
334 # Before calling _compile(), reset the code object to None so that if an
335 # Before calling _compile(), reset the code object to None so that if an
335 # exception is raised in compilation, we don't mislead by having
336 # exception is raised in compilation, we don't mislead by having
336 # inconsistent code/source attributes.
337 # inconsistent code/source attributes.
337 self.code, self._is_complete = None, None
338 self.code, self._is_complete = None, None
338
339
339 # Honor termination lines properly
340 # Honor termination lines properly
340 if source.endswith('\\\n'):
341 if source.endswith('\\\n'):
341 return False
342 return False
342
343
343 self._update_indent(lines)
344 self._update_indent(lines)
344 try:
345 try:
345 self.code = self._compile(source, symbol="exec")
346 self.code = self._compile(source, symbol="exec")
346 # Invalid syntax can produce any of a number of different errors from
347 # Invalid syntax can produce any of a number of different errors from
347 # inside the compiler, so we have to catch them all. Syntax errors
348 # inside the compiler, so we have to catch them all. Syntax errors
348 # immediately produce a 'ready' block, so the invalid Python can be
349 # immediately produce a 'ready' block, so the invalid Python can be
349 # sent to the kernel for evaluation with possible ipython
350 # sent to the kernel for evaluation with possible ipython
350 # special-syntax conversion.
351 # special-syntax conversion.
351 except (SyntaxError, OverflowError, ValueError, TypeError,
352 except (SyntaxError, OverflowError, ValueError, TypeError,
352 MemoryError):
353 MemoryError):
353 self._is_complete = True
354 self._is_complete = True
354 else:
355 else:
355 # Compilation didn't produce any exceptions (though it may not have
356 # Compilation didn't produce any exceptions (though it may not have
356 # given a complete code object)
357 # given a complete code object)
357 self._is_complete = self.code is not None
358 self._is_complete = self.code is not None
358
359
359 return self._is_complete
360 return self._is_complete
360
361
361 def push_accepts_more(self):
362 def push_accepts_more(self):
362 """Return whether a block of interactive input can accept more input.
363 """Return whether a block of interactive input can accept more input.
363
364
364 This method is meant to be used by line-oriented frontends, who need to
365 This method is meant to be used by line-oriented frontends, who need to
365 guess whether a block is complete or not based solely on prior and
366 guess whether a block is complete or not based solely on prior and
366 current input lines. The InputSplitter considers it has a complete
367 current input lines. The InputSplitter considers it has a complete
367 interactive block and will not accept more input only when either a
368 interactive block and will not accept more input only when either a
368 SyntaxError is raised, or *all* of the following are true:
369 SyntaxError is raised, or *all* of the following are true:
369
370
370 1. The input compiles to a complete statement.
371 1. The input compiles to a complete statement.
371
372
372 2. The indentation level is flush-left (because if we are indented,
373 2. The indentation level is flush-left (because if we are indented,
373 like inside a function definition or for loop, we need to keep
374 like inside a function definition or for loop, we need to keep
374 reading new input).
375 reading new input).
375
376
376 3. There is one extra line consisting only of whitespace.
377 3. There is one extra line consisting only of whitespace.
377
378
378 Because of condition #3, this method should be used only by
379 Because of condition #3, this method should be used only by
379 *line-oriented* frontends, since it means that intermediate blank lines
380 *line-oriented* frontends, since it means that intermediate blank lines
380 are not allowed in function definitions (or any other indented block).
381 are not allowed in function definitions (or any other indented block).
381
382
382 If the current input produces a syntax error, this method immediately
383 If the current input produces a syntax error, this method immediately
383 returns False but does *not* raise the syntax error exception, as
384 returns False but does *not* raise the syntax error exception, as
384 typically clients will want to send invalid syntax to an execution
385 typically clients will want to send invalid syntax to an execution
385 backend which might convert the invalid syntax into valid Python via
386 backend which might convert the invalid syntax into valid Python via
386 one of the dynamic IPython mechanisms.
387 one of the dynamic IPython mechanisms.
387 """
388 """
388
389
389 # With incomplete input, unconditionally accept more
390 # With incomplete input, unconditionally accept more
390 if not self._is_complete:
391 if not self._is_complete:
391 return True
392 return True
392
393
393 # If we already have complete input and we're flush left, the answer
394 # If we already have complete input and we're flush left, the answer
394 # depends. In line mode, if there hasn't been any indentation,
395 # depends. In line mode, if there hasn't been any indentation,
395 # that's it. If we've come back from some indentation, we need
396 # that's it. If we've come back from some indentation, we need
396 # the blank final line to finish.
397 # the blank final line to finish.
397 # In cell mode, we need to check how many blocks the input so far
398 # In cell mode, we need to check how many blocks the input so far
398 # compiles into, because if there's already more than one full
399 # compiles into, because if there's already more than one full
399 # independent block of input, then the client has entered full
400 # independent block of input, then the client has entered full
400 # 'cell' mode and is feeding lines that each is complete. In this
401 # 'cell' mode and is feeding lines that each is complete. In this
401 # case we should then keep accepting. The Qt terminal-like console
402 # case we should then keep accepting. The Qt terminal-like console
402 # does precisely this, to provide the convenience of terminal-like
403 # does precisely this, to provide the convenience of terminal-like
403 # input of single expressions, but allowing the user (with a
404 # input of single expressions, but allowing the user (with a
404 # separate keystroke) to switch to 'cell' mode and type multiple
405 # separate keystroke) to switch to 'cell' mode and type multiple
405 # expressions in one shot.
406 # expressions in one shot.
406 if self.indent_spaces==0:
407 if self.indent_spaces==0:
407 if self.input_mode=='line':
408 if self.input_mode=='line':
408 if not self._full_dedent:
409 if not self._full_dedent:
409 return False
410 return False
410 else:
411 else:
411 try:
412 try:
412 code_ast = ast.parse(u''.join(self._buffer))
413 code_ast = ast.parse(u''.join(self._buffer))
413 except Exception:
414 except Exception:
414 return False
415 return False
415 else:
416 else:
416 if len(code_ast.body) == 1:
417 if len(code_ast.body) == 1:
417 return False
418 return False
418
419
419 # When input is complete, then termination is marked by an extra blank
420 # When input is complete, then termination is marked by an extra blank
420 # line at the end.
421 # line at the end.
421 last_line = self.source.splitlines()[-1]
422 last_line = self.source.splitlines()[-1]
422 return bool(last_line and not last_line.isspace())
423 return bool(last_line and not last_line.isspace())
423
424
424 #------------------------------------------------------------------------
425 #------------------------------------------------------------------------
425 # Private interface
426 # Private interface
426 #------------------------------------------------------------------------
427 #------------------------------------------------------------------------
427
428
428 def _find_indent(self, line):
429 def _find_indent(self, line):
429 """Compute the new indentation level for a single line.
430 """Compute the new indentation level for a single line.
430
431
431 Parameters
432 Parameters
432 ----------
433 ----------
433 line : str
434 line : str
434 A single new line of non-whitespace, non-comment Python input.
435 A single new line of non-whitespace, non-comment Python input.
435
436
436 Returns
437 Returns
437 -------
438 -------
438 indent_spaces : int
439 indent_spaces : int
439 New value for the indent level (it may be equal to self.indent_spaces
440 New value for the indent level (it may be equal to self.indent_spaces
440 if indentation doesn't change.
441 if indentation doesn't change.
441
442
442 full_dedent : boolean
443 full_dedent : boolean
443 Whether the new line causes a full flush-left dedent.
444 Whether the new line causes a full flush-left dedent.
444 """
445 """
445 indent_spaces = self.indent_spaces
446 indent_spaces = self.indent_spaces
446 full_dedent = self._full_dedent
447 full_dedent = self._full_dedent
447
448
448 inisp = num_ini_spaces(line)
449 inisp = num_ini_spaces(line)
449 if inisp < indent_spaces:
450 if inisp < indent_spaces:
450 indent_spaces = inisp
451 indent_spaces = inisp
451 if indent_spaces <= 0:
452 if indent_spaces <= 0:
452 #print 'Full dedent in text',self.source # dbg
453 #print 'Full dedent in text',self.source # dbg
453 full_dedent = True
454 full_dedent = True
454
455
455 if line.rstrip()[-1] == ':':
456 if line.rstrip()[-1] == ':':
456 indent_spaces += 4
457 indent_spaces += 4
457 elif dedent_re.match(line):
458 elif dedent_re.match(line):
458 indent_spaces -= 4
459 indent_spaces -= 4
459 if indent_spaces <= 0:
460 if indent_spaces <= 0:
460 full_dedent = True
461 full_dedent = True
461
462
462 # Safety
463 # Safety
463 if indent_spaces < 0:
464 if indent_spaces < 0:
464 indent_spaces = 0
465 indent_spaces = 0
465 #print 'safety' # dbg
466 #print 'safety' # dbg
466
467
467 return indent_spaces, full_dedent
468 return indent_spaces, full_dedent
468
469
469 def _update_indent(self, lines):
470 def _update_indent(self, lines):
470 for line in remove_comments(lines).splitlines():
471 for line in remove_comments(lines).splitlines():
471 if line and not line.isspace():
472 if line and not line.isspace():
472 self.indent_spaces, self._full_dedent = self._find_indent(line)
473 self.indent_spaces, self._full_dedent = self._find_indent(line)
473
474
474 def _store(self, lines, buffer=None, store='source'):
475 def _store(self, lines, buffer=None, store='source'):
475 """Store one or more lines of input.
476 """Store one or more lines of input.
476
477
477 If input lines are not newline-terminated, a newline is automatically
478 If input lines are not newline-terminated, a newline is automatically
478 appended."""
479 appended."""
479
480
480 if buffer is None:
481 if buffer is None:
481 buffer = self._buffer
482 buffer = self._buffer
482
483
483 if lines.endswith('\n'):
484 if lines.endswith('\n'):
484 buffer.append(lines)
485 buffer.append(lines)
485 else:
486 else:
486 buffer.append(lines+'\n')
487 buffer.append(lines+'\n')
487 setattr(self, store, self._set_source(buffer))
488 setattr(self, store, self._set_source(buffer))
488
489
489 def _set_source(self, buffer):
490 def _set_source(self, buffer):
490 return u''.join(buffer)
491 return u''.join(buffer)
491
492
492
493
493 class IPythonInputSplitter(InputSplitter):
494 class IPythonInputSplitter(InputSplitter):
494 """An input splitter that recognizes all of IPython's special syntax."""
495 """An input splitter that recognizes all of IPython's special syntax."""
495
496
496 # String with raw, untransformed input.
497 # String with raw, untransformed input.
497 source_raw = ''
498 source_raw = ''
498
499
499 # Flag to track when a transformer has stored input that it hasn't given
500 # Flag to track when a transformer has stored input that it hasn't given
500 # back yet.
501 # back yet.
501 transformer_accumulating = False
502 transformer_accumulating = False
503
504 # Flag to track when assemble_python_lines has stored input that it hasn't
505 # given back yet.
506 within_python_line = False
502
507
503 # Private attributes
508 # Private attributes
504
509
505 # List with lines of raw input accumulated so far.
510 # List with lines of raw input accumulated so far.
506 _buffer_raw = None
511 _buffer_raw = None
507
512
508 def __init__(self, input_mode=None, transforms=None):
513 def __init__(self, input_mode=None, physical_line_transforms=None,
514 logical_line_transforms=None, python_line_transforms=None):
509 super(IPythonInputSplitter, self).__init__(input_mode)
515 super(IPythonInputSplitter, self).__init__(input_mode)
510 self._buffer_raw = []
516 self._buffer_raw = []
511 self._validate = True
517 self._validate = True
512 if transforms is not None:
518
513 self.transforms = transforms
519 self.physical_line_transforms = physical_line_transforms or \
514 else:
520 [leading_indent(),
515 self.transforms = [leading_indent(),
521 classic_prompt(),
516 classic_prompt(),
522 ipy_prompt(),
517 ipy_prompt(),
523 ]
518 cellmagic(),
524
519 assemble_logical_lines(),
525 self.assemble_logical_lines = assemble_logical_lines()
520 help_end(),
526 self.logical_line_transforms = logical_line_transforms or \
521 escaped_transformer(),
527 [cellmagic(),
522 assign_from_magic(),
528 help_end(),
523 assign_from_system(),
529 escaped_transformer(),
524 ]
530 assign_from_magic(),
531 assign_from_system(),
532 ]
533
534 self.assemble_python_lines = assemble_python_lines()
535 self.python_line_transforms = python_line_transforms or []
536
537 @property
538 def transforms(self):
539 "Quick access to all transformers."
540 return self.physical_line_transforms + \
541 [self.assemble_logical_lines] + self.logical_line_transforms + \
542 [self.assemble_python_lines] + self.python_line_transforms
543
544 @property
545 def transforms_in_use(self):
546 """Transformers, excluding logical line transformers if we're in a
547 Python line."""
548 t = self.physical_line_transforms + [self.assemble_logical_lines]
549 if not self.within_python_line:
550 t += self.logical_line_transforms
551 return t + [self.assemble_python_lines] + self.python_line_transforms
525
552
526 def reset(self):
553 def reset(self):
527 """Reset the input buffer and associated state."""
554 """Reset the input buffer and associated state."""
528 super(IPythonInputSplitter, self).reset()
555 super(IPythonInputSplitter, self).reset()
529 self._buffer_raw[:] = []
556 self._buffer_raw[:] = []
530 self.source_raw = ''
557 self.source_raw = ''
531 self.transformer_accumulating = False
558 self.transformer_accumulating = False
532 for t in self.transforms:
559 for t in self.transforms:
533 t.reset()
560 t.reset()
534
561
535 def flush_transformers(self):
562 def flush_transformers(self):
563 def _flush(transform, out):
564 if out is not None:
565 tmp = transform.push(out)
566 return tmp or transform.reset() or None
567 else:
568 return transform.reset() or None
569
536 out = None
570 out = None
537 for t in self.transforms:
571 for t in self.transforms_in_use:
538 tmp = t.reset()
572 out = _flush(t, out)
539 if tmp:
573
540 out = tmp
574 if out is not None:
541 if out:
542 self._store(out)
575 self._store(out)
543
576
544 def source_raw_reset(self):
577 def source_raw_reset(self):
545 """Return input and raw source and perform a full reset.
578 """Return input and raw source and perform a full reset.
546 """
579 """
547 self.flush_transformers()
580 self.flush_transformers()
548 out = self.source
581 out = self.source
549 out_r = self.source_raw
582 out_r = self.source_raw
550 self.reset()
583 self.reset()
551 return out, out_r
584 return out, out_r
552
585
553 def source_reset(self):
586 def source_reset(self):
554 self.flush_transformers()
587 self.flush_transformers()
555 return super(IPythonInputSplitter, self).source_reset()
588 return super(IPythonInputSplitter, self).source_reset()
556
589
557 def push_accepts_more(self):
590 def push_accepts_more(self):
558 if self.transformer_accumulating:
591 if self.transformer_accumulating:
559 return True
592 return True
560 else:
593 else:
561 return super(IPythonInputSplitter, self).push_accepts_more()
594 return super(IPythonInputSplitter, self).push_accepts_more()
562
595
563 def transform_cell(self, cell):
596 def transform_cell(self, cell):
564 """Process and translate a cell of input.
597 """Process and translate a cell of input.
565 """
598 """
566 self.reset()
599 self.reset()
567 self.push(cell)
600 self.push(cell)
568 return self.source_reset()
601 return self.source_reset()
569
602
570 def push(self, lines):
603 def push(self, lines):
571 """Push one or more lines of IPython input.
604 """Push one or more lines of IPython input.
572
605
573 This stores the given lines and returns a status code indicating
606 This stores the given lines and returns a status code indicating
574 whether the code forms a complete Python block or not, after processing
607 whether the code forms a complete Python block or not, after processing
575 all input lines for special IPython syntax.
608 all input lines for special IPython syntax.
576
609
577 Any exceptions generated in compilation are swallowed, but if an
610 Any exceptions generated in compilation are swallowed, but if an
578 exception was produced, the method returns True.
611 exception was produced, the method returns True.
579
612
580 Parameters
613 Parameters
581 ----------
614 ----------
582 lines : string
615 lines : string
583 One or more lines of Python input.
616 One or more lines of Python input.
584
617
585 Returns
618 Returns
586 -------
619 -------
587 is_complete : boolean
620 is_complete : boolean
588 True if the current input source (the result of the current input
621 True if the current input source (the result of the current input
589 plus prior inputs) forms a complete Python execution block. Note that
622 plus prior inputs) forms a complete Python execution block. Note that
590 this value is also stored as a private attribute (_is_complete), so it
623 this value is also stored as a private attribute (_is_complete), so it
591 can be queried at any time.
624 can be queried at any time.
592 """
625 """
593
626
594 # We must ensure all input is pure unicode
627 # We must ensure all input is pure unicode
595 lines = cast_unicode(lines, self.encoding)
628 lines = cast_unicode(lines, self.encoding)
596
629
597 # ''.splitlines() --> [], but we need to push the empty line to transformers
630 # ''.splitlines() --> [], but we need to push the empty line to transformers
598 lines_list = lines.splitlines()
631 lines_list = lines.splitlines()
599 if not lines_list:
632 if not lines_list:
600 lines_list = ['']
633 lines_list = ['']
601
634
602 # Transform logic
635 # Transform logic
603 #
636 #
604 # We only apply the line transformers to the input if we have either no
637 # We only apply the line transformers to the input if we have either no
605 # input yet, or complete input, or if the last line of the buffer ends
638 # input yet, or complete input, or if the last line of the buffer ends
606 # with ':' (opening an indented block). This prevents the accidental
639 # with ':' (opening an indented block). This prevents the accidental
607 # transformation of escapes inside multiline expressions like
640 # transformation of escapes inside multiline expressions like
608 # triple-quoted strings or parenthesized expressions.
641 # triple-quoted strings or parenthesized expressions.
609 #
642 #
610 # The last heuristic, while ugly, ensures that the first line of an
643 # The last heuristic, while ugly, ensures that the first line of an
611 # indented block is correctly transformed.
644 # indented block is correctly transformed.
612 #
645 #
613 # FIXME: try to find a cleaner approach for this last bit.
646 # FIXME: try to find a cleaner approach for this last bit.
614
647
615 # If we were in 'block' mode, since we're going to pump the parent
648 # If we were in 'block' mode, since we're going to pump the parent
616 # class by hand line by line, we need to temporarily switch out to
649 # class by hand line by line, we need to temporarily switch out to
617 # 'line' mode, do a single manual reset and then feed the lines one
650 # 'line' mode, do a single manual reset and then feed the lines one
618 # by one. Note that this only matters if the input has more than one
651 # by one. Note that this only matters if the input has more than one
619 # line.
652 # line.
620 changed_input_mode = False
653 changed_input_mode = False
621
654
622 if self.input_mode == 'cell':
655 if self.input_mode == 'cell':
623 self.reset()
656 self.reset()
624 changed_input_mode = True
657 changed_input_mode = True
625 saved_input_mode = 'cell'
658 saved_input_mode = 'cell'
626 self.input_mode = 'line'
659 self.input_mode = 'line'
627
660
628 # Store raw source before applying any transformations to it. Note
661 # Store raw source before applying any transformations to it. Note
629 # that this must be done *after* the reset() call that would otherwise
662 # that this must be done *after* the reset() call that would otherwise
630 # flush the buffer.
663 # flush the buffer.
631 self._store(lines, self._buffer_raw, 'source_raw')
664 self._store(lines, self._buffer_raw, 'source_raw')
632
665
633 try:
666 try:
634 for line in lines_list:
667 for line in lines_list:
635 out = self.push_line(line)
668 out = self.push_line(line)
636 finally:
669 finally:
637 if changed_input_mode:
670 if changed_input_mode:
638 self.input_mode = saved_input_mode
671 self.input_mode = saved_input_mode
639
672
640 return out
673 return out
641
674
642 def push_line(self, line):
675 def push_line(self, line):
643 buf = self._buffer
676 buf = self._buffer
644 for transformer in self.transforms:
677
678 def _accumulating(dbg):
679 #print(dbg)
680 self.transformer_accumulating = True
681 return False
682
683 for transformer in self.physical_line_transforms:
684 line = transformer.push(line)
685 if line is None:
686 return _accumulating(transformer)
687
688 line = self.assemble_logical_lines.push(line)
689 if line is None:
690 return _accumulating('acc logical line')
691
692 if not self.within_python_line:
693 for transformer in self.logical_line_transforms:
694 line = transformer.push(line)
695 if line is None:
696 return _accumulating(transformer)
697
698 line = self.assemble_python_lines.push(line)
699 if line is None:
700 self.within_python_line = True
701 return _accumulating('acc python line')
702 else:
703 self.within_python_line = False
704
705 for transformer in self.python_line_transforms:
645 line = transformer.push(line)
706 line = transformer.push(line)
646 if line is None:
707 if line is None:
647 self.transformer_accumulating = True
708 return _accumulating(transformer)
648 return False
649
709
710 #print("transformers clear") #debug
650 self.transformer_accumulating = False
711 self.transformer_accumulating = False
651 return super(IPythonInputSplitter, self).push(line)
712 return super(IPythonInputSplitter, self).push(line)
@@ -1,454 +1,487 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 import tokenize
5 import tokenize
6
6
7 try:
7 try:
8 generate_tokens = tokenize.generate_tokens
8 generate_tokens = tokenize.generate_tokens
9 except AttributeError:
9 except AttributeError:
10 # Python 3. Note that we use the undocumented _tokenize because it expects
10 # Python 3. Note that we use the undocumented _tokenize because it expects
11 # strings, not bytes. See also Python issue #9969.
11 # strings, not bytes. See also Python issue #9969.
12 generate_tokens = tokenize._tokenize
12 generate_tokens = tokenize._tokenize
13
13
14 from IPython.core.splitinput import split_user_input, LineInfo
14 from IPython.core.splitinput import split_user_input, LineInfo
15 from IPython.utils.untokenize import untokenize
15 from IPython.utils.untokenize import untokenize
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Globals
18 # Globals
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 # The escape sequences that define the syntax transformations IPython will
21 # The escape sequences that define the syntax transformations IPython will
22 # apply to user input. These can NOT be just changed here: many regular
22 # apply to user input. These can NOT be just changed here: many regular
23 # expressions and other parts of the code may use their hardcoded values, and
23 # expressions and other parts of the code may use their hardcoded values, and
24 # for all intents and purposes they constitute the 'IPython syntax', so they
24 # for all intents and purposes they constitute the 'IPython syntax', so they
25 # should be considered fixed.
25 # should be considered fixed.
26
26
27 ESC_SHELL = '!' # Send line to underlying system shell
27 ESC_SHELL = '!' # Send line to underlying system shell
28 ESC_SH_CAP = '!!' # Send line to system shell and capture output
28 ESC_SH_CAP = '!!' # Send line to system shell and capture output
29 ESC_HELP = '?' # Find information about object
29 ESC_HELP = '?' # Find information about object
30 ESC_HELP2 = '??' # Find extra-detailed information about object
30 ESC_HELP2 = '??' # Find extra-detailed information about object
31 ESC_MAGIC = '%' # Call magic function
31 ESC_MAGIC = '%' # Call magic function
32 ESC_MAGIC2 = '%%' # Call cell-magic function
32 ESC_MAGIC2 = '%%' # Call cell-magic function
33 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
33 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
34 ESC_QUOTE2 = ';' # Quote all args as a single string, call
34 ESC_QUOTE2 = ';' # Quote all args as a single string, call
35 ESC_PAREN = '/' # Call first argument with rest of line as arguments
35 ESC_PAREN = '/' # Call first argument with rest of line as arguments
36
36
37 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
37 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
38 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
38 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
39 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
39 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
40
40
41
41
42 class InputTransformer(object):
42 class InputTransformer(object):
43 """Abstract base class for line-based input transformers."""
43 """Abstract base class for line-based input transformers."""
44 __metaclass__ = abc.ABCMeta
44 __metaclass__ = abc.ABCMeta
45
45
46 @abc.abstractmethod
46 @abc.abstractmethod
47 def push(self, line):
47 def push(self, line):
48 """Send a line of input to the transformer, returning the transformed
48 """Send a line of input to the transformer, returning the transformed
49 input or None if the transformer is waiting for more input.
49 input or None if the transformer is waiting for more input.
50
50
51 Must be overridden by subclasses.
51 Must be overridden by subclasses.
52 """
52 """
53 pass
53 pass
54
54
55 @abc.abstractmethod
55 @abc.abstractmethod
56 def reset(self):
56 def reset(self):
57 """Return, transformed any lines that the transformer has accumulated,
57 """Return, transformed any lines that the transformer has accumulated,
58 and reset its internal state.
58 and reset its internal state.
59
59
60 Must be overridden by subclasses.
60 Must be overridden by subclasses.
61 """
61 """
62 pass
62 pass
63
63
64 # Set this to True to allow the transformer to act on lines inside strings.
64 # Set this to True to allow the transformer to act on lines inside strings.
65 look_in_string = False
65 look_in_string = False
66
66
67 @classmethod
67 @classmethod
68 def wrap(cls, func):
68 def wrap(cls, func):
69 """Can be used by subclasses as a decorator, to return a factory that
69 """Can be used by subclasses as a decorator, to return a factory that
70 will allow instantiation with the decorated object.
70 will allow instantiation with the decorated object.
71 """
71 """
72 @functools.wraps(func)
72 @functools.wraps(func)
73 def transformer_factory():
73 def transformer_factory():
74 transformer = cls(func)
74 transformer = cls(func)
75 if getattr(transformer_factory, 'look_in_string', False):
75 if getattr(transformer_factory, 'look_in_string', False):
76 transformer.look_in_string = True
76 transformer.look_in_string = True
77 return transformer
77 return transformer
78
78
79 return transformer_factory
79 return transformer_factory
80
80
81 class StatelessInputTransformer(InputTransformer):
81 class StatelessInputTransformer(InputTransformer):
82 """Wrapper for a stateless input transformer implemented as a function."""
82 """Wrapper for a stateless input transformer implemented as a function."""
83 def __init__(self, func):
83 def __init__(self, func):
84 self.func = func
84 self.func = func
85
85
86 def __repr__(self):
86 def __repr__(self):
87 return "StatelessInputTransformer(func={!r})".format(self.func)
87 return "StatelessInputTransformer(func={!r})".format(self.func)
88
88
89 def push(self, line):
89 def push(self, line):
90 """Send a line of input to the transformer, returning the
90 """Send a line of input to the transformer, returning the
91 transformed input."""
91 transformed input."""
92 return self.func(line)
92 return self.func(line)
93
93
94 def reset(self):
94 def reset(self):
95 """No-op - exists for compatibility."""
95 """No-op - exists for compatibility."""
96 pass
96 pass
97
97
98 class CoroutineInputTransformer(InputTransformer):
98 class CoroutineInputTransformer(InputTransformer):
99 """Wrapper for an input transformer implemented as a coroutine."""
99 """Wrapper for an input transformer implemented as a coroutine."""
100 def __init__(self, coro):
100 def __init__(self, coro):
101 # Prime it
101 # Prime it
102 self.coro = coro()
102 self.coro = coro()
103 next(self.coro)
103 next(self.coro)
104
104
105 def __repr__(self):
105 def __repr__(self):
106 return "CoroutineInputTransformer(coro={!r})".format(self.coro)
106 return "CoroutineInputTransformer(coro={!r})".format(self.coro)
107
107
108 def push(self, line):
108 def push(self, line):
109 """Send a line of input to the transformer, returning the
109 """Send a line of input to the transformer, returning the
110 transformed input or None if the transformer is waiting for more
110 transformed input or None if the transformer is waiting for more
111 input.
111 input.
112 """
112 """
113 return self.coro.send(line)
113 return self.coro.send(line)
114
114
115 def reset(self):
115 def reset(self):
116 """Return, transformed any lines that the transformer has
116 """Return, transformed any lines that the transformer has
117 accumulated, and reset its internal state.
117 accumulated, and reset its internal state.
118 """
118 """
119 return self.coro.send(None)
119 return self.coro.send(None)
120
120
121 class TokenInputTransformer(InputTransformer):
121 class TokenInputTransformer(InputTransformer):
122 """Wrapper for a token-based input transformer.
122 """Wrapper for a token-based input transformer.
123
123
124 func should accept a list of tokens (5-tuples, see tokenize docs), and
124 func should accept a list of tokens (5-tuples, see tokenize docs), and
125 return an iterable which can be passed to tokenize.untokenize().
125 return an iterable which can be passed to tokenize.untokenize().
126 """
126 """
127 def __init__(self, func):
127 def __init__(self, func):
128 self.func = func
128 self.func = func
129 self.current_line = ""
129 self.current_line = ""
130 self.line_used= False
130 self.line_used = False
131 self.reset_tokenizer()
131 self.reset_tokenizer()
132
132
133 def reset_tokenizer(self):
133 def reset_tokenizer(self):
134 self.tokenizer = generate_tokens(self.get_line)
134 self.tokenizer = generate_tokens(self.get_line)
135
135
136 def get_line(self):
136 def get_line(self):
137 if self.line_used:
137 if self.line_used:
138 raise tokenize.TokenError
138 raise tokenize.TokenError
139 self.line_used = True
139 self.line_used = True
140 return self.current_line
140 return self.current_line
141
141
142 def push(self, line):
142 def push(self, line):
143 self.current_line += line + "\n"
143 self.current_line += line + "\n"
144 if self.current_line.isspace():
145 return self.reset()
146
144 self.line_used = False
147 self.line_used = False
145 tokens = []
148 tokens = []
149 stop_at_NL = False
146 try:
150 try:
147 for intok in self.tokenizer:
151 for intok in self.tokenizer:
148 tokens.append(intok)
152 tokens.append(intok)
149 if intok[0] in (tokenize.NEWLINE, tokenize.NL):
153 t = intok[0]
154 if t == tokenize.NEWLINE or (stop_at_NL and t == tokenize.NL):
150 # Stop before we try to pull a line we don't have yet
155 # Stop before we try to pull a line we don't have yet
151 break
156 break
157 elif t in (tokenize.COMMENT, tokenize.ERRORTOKEN):
158 stop_at_NL = True
152 except tokenize.TokenError:
159 except tokenize.TokenError:
153 # Multi-line statement - stop and try again with the next line
160 # Multi-line statement - stop and try again with the next line
154 self.reset_tokenizer()
161 self.reset_tokenizer()
155 return None
162 return None
156
163
164 return self.output(tokens)
165
166 def output(self, tokens):
157 self.current_line = ""
167 self.current_line = ""
158 self.reset_tokenizer()
168 self.reset_tokenizer()
159 return untokenize(self.func(tokens)).rstrip('\n')
169 return untokenize(self.func(tokens)).rstrip('\n')
160
170
161 def reset(self):
171 def reset(self):
162 l = self.current_line
172 l = self.current_line
163 self.current_line = ""
173 self.current_line = ""
174 self.reset_tokenizer()
164 if l:
175 if l:
165 return l.rstrip('\n')
176 return l.rstrip('\n')
166
177
167 @TokenInputTransformer.wrap
178 class assemble_python_lines(TokenInputTransformer):
168 def assemble_logical_lines(tokens):
179 def __init__(self):
169 return tokens
180 super(assemble_python_lines, self).__init__(None)
181
182 def output(self, tokens):
183 return self.reset()
184
185 @CoroutineInputTransformer.wrap
186 def assemble_logical_lines():
187 """Join lines following explicit line continuations (\)"""
188 line = ''
189 while True:
190 line = (yield line)
191 if not line or line.isspace():
192 continue
193
194 parts = []
195 while line is not None:
196 parts.append(line.rstrip('\\'))
197 if not line.endswith('\\'):
198 break
199 line = (yield None)
200
201 # Output
202 line = ' '.join(parts)
170
203
171 # Utilities
204 # Utilities
172 def _make_help_call(target, esc, lspace, next_input=None):
205 def _make_help_call(target, esc, lspace, next_input=None):
173 """Prepares a pinfo(2)/psearch call from a target name and the escape
206 """Prepares a pinfo(2)/psearch call from a target name and the escape
174 (i.e. ? or ??)"""
207 (i.e. ? or ??)"""
175 method = 'pinfo2' if esc == '??' \
208 method = 'pinfo2' if esc == '??' \
176 else 'psearch' if '*' in target \
209 else 'psearch' if '*' in target \
177 else 'pinfo'
210 else 'pinfo'
178 arg = " ".join([method, target])
211 arg = " ".join([method, target])
179 if next_input is None:
212 if next_input is None:
180 return '%sget_ipython().magic(%r)' % (lspace, arg)
213 return '%sget_ipython().magic(%r)' % (lspace, arg)
181 else:
214 else:
182 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
215 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
183 (lspace, next_input, arg)
216 (lspace, next_input, arg)
184
217
185 @CoroutineInputTransformer.wrap
218 @CoroutineInputTransformer.wrap
186 def escaped_transformer():
219 def escaped_transformer():
187 """Translate lines beginning with one of IPython's escape characters.
220 """Translate lines beginning with one of IPython's escape characters.
188
221
189 This is stateful to allow magic commands etc. to be continued over several
222 This is stateful to allow magic commands etc. to be continued over several
190 lines using explicit line continuations (\ at the end of a line).
223 lines using explicit line continuations (\ at the end of a line).
191 """
224 """
192
225
193 # These define the transformations for the different escape characters.
226 # These define the transformations for the different escape characters.
194 def _tr_system(line_info):
227 def _tr_system(line_info):
195 "Translate lines escaped with: !"
228 "Translate lines escaped with: !"
196 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
229 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
197 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
230 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
198
231
199 def _tr_system2(line_info):
232 def _tr_system2(line_info):
200 "Translate lines escaped with: !!"
233 "Translate lines escaped with: !!"
201 cmd = line_info.line.lstrip()[2:]
234 cmd = line_info.line.lstrip()[2:]
202 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
235 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
203
236
204 def _tr_help(line_info):
237 def _tr_help(line_info):
205 "Translate lines escaped with: ?/??"
238 "Translate lines escaped with: ?/??"
206 # A naked help line should just fire the intro help screen
239 # A naked help line should just fire the intro help screen
207 if not line_info.line[1:]:
240 if not line_info.line[1:]:
208 return 'get_ipython().show_usage()'
241 return 'get_ipython().show_usage()'
209
242
210 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
243 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
211
244
212 def _tr_magic(line_info):
245 def _tr_magic(line_info):
213 "Translate lines escaped with: %"
246 "Translate lines escaped with: %"
214 tpl = '%sget_ipython().magic(%r)'
247 tpl = '%sget_ipython().magic(%r)'
215 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
248 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
216 return tpl % (line_info.pre, cmd)
249 return tpl % (line_info.pre, cmd)
217
250
218 def _tr_quote(line_info):
251 def _tr_quote(line_info):
219 "Translate lines escaped with: ,"
252 "Translate lines escaped with: ,"
220 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
253 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
221 '", "'.join(line_info.the_rest.split()) )
254 '", "'.join(line_info.the_rest.split()) )
222
255
223 def _tr_quote2(line_info):
256 def _tr_quote2(line_info):
224 "Translate lines escaped with: ;"
257 "Translate lines escaped with: ;"
225 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
258 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
226 line_info.the_rest)
259 line_info.the_rest)
227
260
228 def _tr_paren(line_info):
261 def _tr_paren(line_info):
229 "Translate lines escaped with: /"
262 "Translate lines escaped with: /"
230 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
263 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
231 ", ".join(line_info.the_rest.split()))
264 ", ".join(line_info.the_rest.split()))
232
265
233 tr = { ESC_SHELL : _tr_system,
266 tr = { ESC_SHELL : _tr_system,
234 ESC_SH_CAP : _tr_system2,
267 ESC_SH_CAP : _tr_system2,
235 ESC_HELP : _tr_help,
268 ESC_HELP : _tr_help,
236 ESC_HELP2 : _tr_help,
269 ESC_HELP2 : _tr_help,
237 ESC_MAGIC : _tr_magic,
270 ESC_MAGIC : _tr_magic,
238 ESC_QUOTE : _tr_quote,
271 ESC_QUOTE : _tr_quote,
239 ESC_QUOTE2 : _tr_quote2,
272 ESC_QUOTE2 : _tr_quote2,
240 ESC_PAREN : _tr_paren }
273 ESC_PAREN : _tr_paren }
241
274
242 line = ''
275 line = ''
243 while True:
276 while True:
244 line = (yield line)
277 line = (yield line)
245 if not line or line.isspace():
278 if not line or line.isspace():
246 continue
279 continue
247 lineinf = LineInfo(line)
280 lineinf = LineInfo(line)
248 if lineinf.esc not in tr:
281 if lineinf.esc not in tr:
249 continue
282 continue
250
283
251 parts = []
284 parts = []
252 while line is not None:
285 while line is not None:
253 parts.append(line.rstrip('\\'))
286 parts.append(line.rstrip('\\'))
254 if not line.endswith('\\'):
287 if not line.endswith('\\'):
255 break
288 break
256 line = (yield None)
289 line = (yield None)
257
290
258 # Output
291 # Output
259 lineinf = LineInfo(' '.join(parts))
292 lineinf = LineInfo(' '.join(parts))
260 line = tr[lineinf.esc](lineinf)
293 line = tr[lineinf.esc](lineinf)
261
294
262 _initial_space_re = re.compile(r'\s*')
295 _initial_space_re = re.compile(r'\s*')
263
296
264 _help_end_re = re.compile(r"""(%{0,2}
297 _help_end_re = re.compile(r"""(%{0,2}
265 [a-zA-Z_*][\w*]* # Variable name
298 [a-zA-Z_*][\w*]* # Variable name
266 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
299 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
267 )
300 )
268 (\?\??)$ # ? or ??""",
301 (\?\??)$ # ? or ??""",
269 re.VERBOSE)
302 re.VERBOSE)
270
303
271 def has_comment(src):
304 def has_comment(src):
272 """Indicate whether an input line has (i.e. ends in, or is) a comment.
305 """Indicate whether an input line has (i.e. ends in, or is) a comment.
273
306
274 This uses tokenize, so it can distinguish comments from # inside strings.
307 This uses tokenize, so it can distinguish comments from # inside strings.
275
308
276 Parameters
309 Parameters
277 ----------
310 ----------
278 src : string
311 src : string
279 A single line input string.
312 A single line input string.
280
313
281 Returns
314 Returns
282 -------
315 -------
283 Boolean: True if source has a comment.
316 Boolean: True if source has a comment.
284 """
317 """
285 readline = StringIO(src).readline
318 readline = StringIO(src).readline
286 toktypes = set()
319 toktypes = set()
287 try:
320 try:
288 for t in tokenize.generate_tokens(readline):
321 for t in tokenize.generate_tokens(readline):
289 toktypes.add(t[0])
322 toktypes.add(t[0])
290 except tokenize.TokenError:
323 except tokenize.TokenError:
291 pass
324 pass
292 return(tokenize.COMMENT in toktypes)
325 return(tokenize.COMMENT in toktypes)
293
326
294
327
295 @StatelessInputTransformer.wrap
328 @StatelessInputTransformer.wrap
296 def help_end(line):
329 def help_end(line):
297 """Translate lines with ?/?? at the end"""
330 """Translate lines with ?/?? at the end"""
298 m = _help_end_re.search(line)
331 m = _help_end_re.search(line)
299 if m is None or has_comment(line):
332 if m is None or has_comment(line):
300 return line
333 return line
301 target = m.group(1)
334 target = m.group(1)
302 esc = m.group(3)
335 esc = m.group(3)
303 lspace = _initial_space_re.match(line).group(0)
336 lspace = _initial_space_re.match(line).group(0)
304
337
305 # If we're mid-command, put it back on the next prompt for the user.
338 # If we're mid-command, put it back on the next prompt for the user.
306 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
339 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
307
340
308 return _make_help_call(target, esc, lspace, next_input)
341 return _make_help_call(target, esc, lspace, next_input)
309
342
310
343
311 @CoroutineInputTransformer.wrap
344 @CoroutineInputTransformer.wrap
312 def cellmagic():
345 def cellmagic():
313 """Captures & transforms cell magics.
346 """Captures & transforms cell magics.
314
347
315 After a cell magic is started, this stores up any lines it gets until it is
348 After a cell magic is started, this stores up any lines it gets until it is
316 reset (sent None).
349 reset (sent None).
317 """
350 """
318 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
351 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
319 cellmagic_help_re = re.compile('%%\w+\?')
352 cellmagic_help_re = re.compile('%%\w+\?')
320 line = ''
353 line = ''
321 while True:
354 while True:
322 line = (yield line)
355 line = (yield line)
323 if (not line) or (not line.startswith(ESC_MAGIC2)):
356 if (not line) or (not line.startswith(ESC_MAGIC2)):
324 continue
357 continue
325
358
326 if cellmagic_help_re.match(line):
359 if cellmagic_help_re.match(line):
327 # This case will be handled by help_end
360 # This case will be handled by help_end
328 continue
361 continue
329
362
330 first = line
363 first = line
331 body = []
364 body = []
332 line = (yield None)
365 line = (yield None)
333 while (line is not None) and (line.strip() != ''):
366 while (line is not None) and (line.strip() != ''):
334 body.append(line)
367 body.append(line)
335 line = (yield None)
368 line = (yield None)
336
369
337 # Output
370 # Output
338 magic_name, _, first = first.partition(' ')
371 magic_name, _, first = first.partition(' ')
339 magic_name = magic_name.lstrip(ESC_MAGIC2)
372 magic_name = magic_name.lstrip(ESC_MAGIC2)
340 line = tpl % (magic_name, first, u'\n'.join(body))
373 line = tpl % (magic_name, first, u'\n'.join(body))
341
374
342
375
343 def _strip_prompts(prompt1_re, prompt2_re):
376 def _strip_prompts(prompt1_re, prompt2_re):
344 """Remove matching input prompts from a block of input."""
377 """Remove matching input prompts from a block of input."""
345 line = ''
378 line = ''
346 while True:
379 while True:
347 line = (yield line)
380 line = (yield line)
348
381
349 if line is None:
382 if line is None:
350 continue
383 continue
351
384
352 m = prompt1_re.match(line)
385 m = prompt1_re.match(line)
353 if m:
386 if m:
354 while m:
387 while m:
355 line = (yield line[len(m.group(0)):])
388 line = (yield line[len(m.group(0)):])
356 if line is None:
389 if line is None:
357 break
390 break
358 m = prompt2_re.match(line)
391 m = prompt2_re.match(line)
359 else:
392 else:
360 # Prompts not in input - wait for reset
393 # Prompts not in input - wait for reset
361 while line is not None:
394 while line is not None:
362 line = (yield line)
395 line = (yield line)
363
396
364 @CoroutineInputTransformer.wrap
397 @CoroutineInputTransformer.wrap
365 def classic_prompt():
398 def classic_prompt():
366 """Strip the >>>/... prompts of the Python interactive shell."""
399 """Strip the >>>/... prompts of the Python interactive shell."""
367 prompt1_re = re.compile(r'^(>>> )')
400 prompt1_re = re.compile(r'^(>>> )')
368 prompt2_re = re.compile(r'^(>>> |^\.\.\. )')
401 prompt2_re = re.compile(r'^(>>> |^\.\.\. )')
369 return _strip_prompts(prompt1_re, prompt2_re)
402 return _strip_prompts(prompt1_re, prompt2_re)
370
403
371 classic_prompt.look_in_string = True
404 classic_prompt.look_in_string = True
372
405
373 @CoroutineInputTransformer.wrap
406 @CoroutineInputTransformer.wrap
374 def ipy_prompt():
407 def ipy_prompt():
375 """Strip IPython's In [1]:/...: prompts."""
408 """Strip IPython's In [1]:/...: prompts."""
376 prompt1_re = re.compile(r'^In \[\d+\]: ')
409 prompt1_re = re.compile(r'^In \[\d+\]: ')
377 prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )')
410 prompt2_re = re.compile(r'^(In \[\d+\]: |^\ \ \ \.\.\.+: )')
378 return _strip_prompts(prompt1_re, prompt2_re)
411 return _strip_prompts(prompt1_re, prompt2_re)
379
412
380 ipy_prompt.look_in_string = True
413 ipy_prompt.look_in_string = True
381
414
382
415
383 @CoroutineInputTransformer.wrap
416 @CoroutineInputTransformer.wrap
384 def leading_indent():
417 def leading_indent():
385 """Remove leading indentation.
418 """Remove leading indentation.
386
419
387 If the first line starts with a spaces or tabs, the same whitespace will be
420 If the first line starts with a spaces or tabs, the same whitespace will be
388 removed from each following line until it is reset.
421 removed from each following line until it is reset.
389 """
422 """
390 space_re = re.compile(r'^[ \t]+')
423 space_re = re.compile(r'^[ \t]+')
391 line = ''
424 line = ''
392 while True:
425 while True:
393 line = (yield line)
426 line = (yield line)
394
427
395 if line is None:
428 if line is None:
396 continue
429 continue
397
430
398 m = space_re.match(line)
431 m = space_re.match(line)
399 if m:
432 if m:
400 space = m.group(0)
433 space = m.group(0)
401 while line is not None:
434 while line is not None:
402 if line.startswith(space):
435 if line.startswith(space):
403 line = line[len(space):]
436 line = line[len(space):]
404 line = (yield line)
437 line = (yield line)
405 else:
438 else:
406 # No leading spaces - wait for reset
439 # No leading spaces - wait for reset
407 while line is not None:
440 while line is not None:
408 line = (yield line)
441 line = (yield line)
409
442
410 leading_indent.look_in_string = True
443 leading_indent.look_in_string = True
411
444
412
445
413 def _special_assignment(assignment_re, template):
446 def _special_assignment(assignment_re, template):
414 """Transform assignment from system & magic commands.
447 """Transform assignment from system & magic commands.
415
448
416 This is stateful so that it can handle magic commands continued on several
449 This is stateful so that it can handle magic commands continued on several
417 lines.
450 lines.
418 """
451 """
419 line = ''
452 line = ''
420 while True:
453 while True:
421 line = (yield line)
454 line = (yield line)
422 if not line or line.isspace():
455 if not line or line.isspace():
423 continue
456 continue
424
457
425 m = assignment_re.match(line)
458 m = assignment_re.match(line)
426 if not m:
459 if not m:
427 continue
460 continue
428
461
429 parts = []
462 parts = []
430 while line is not None:
463 while line is not None:
431 parts.append(line.rstrip('\\'))
464 parts.append(line.rstrip('\\'))
432 if not line.endswith('\\'):
465 if not line.endswith('\\'):
433 break
466 break
434 line = (yield None)
467 line = (yield None)
435
468
436 # Output
469 # Output
437 whole = assignment_re.match(' '.join(parts))
470 whole = assignment_re.match(' '.join(parts))
438 line = template % (whole.group('lhs'), whole.group('cmd'))
471 line = template % (whole.group('lhs'), whole.group('cmd'))
439
472
440 @CoroutineInputTransformer.wrap
473 @CoroutineInputTransformer.wrap
441 def assign_from_system():
474 def assign_from_system():
442 """Transform assignment from system commands (e.g. files = !ls)"""
475 """Transform assignment from system commands (e.g. files = !ls)"""
443 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
476 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
444 r'\s*=\s*!\s*(?P<cmd>.*)')
477 r'\s*=\s*!\s*(?P<cmd>.*)')
445 template = '%s = get_ipython().getoutput(%r)'
478 template = '%s = get_ipython().getoutput(%r)'
446 return _special_assignment(assignment_re, template)
479 return _special_assignment(assignment_re, template)
447
480
448 @CoroutineInputTransformer.wrap
481 @CoroutineInputTransformer.wrap
449 def assign_from_magic():
482 def assign_from_magic():
450 """Transform assignment from magic commands (e.g. a = %who_ls)"""
483 """Transform assignment from magic commands (e.g. a = %who_ls)"""
451 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
484 assignment_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
452 r'\s*=\s*%\s*(?P<cmd>.*)')
485 r'\s*=\s*%\s*(?P<cmd>.*)')
453 template = '%s = get_ipython().magic(%r)'
486 template = '%s = get_ipython().magic(%r)'
454 return _special_assignment(assignment_re, template)
487 return _special_assignment(assignment_re, template)
@@ -1,625 +1,626 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Fernando Perez
6 * Fernando Perez
7 * Robert Kern
7 * Robert Kern
8 """
8 """
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2010-2011 The IPython Development Team
10 # Copyright (C) 2010-2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # stdlib
19 # stdlib
20 import unittest
20 import unittest
21 import sys
21 import sys
22
22
23 # Third party
23 # Third party
24 import nose.tools as nt
24 import nose.tools as nt
25
25
26 # Our own
26 # Our own
27 from IPython.core import inputsplitter as isp
27 from IPython.core import inputsplitter as isp
28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
28 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 from IPython.testing import tools as tt
29 from IPython.testing import tools as tt
30 from IPython.utils import py3compat
30 from IPython.utils import py3compat
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Semi-complete examples (also used as tests)
33 # Semi-complete examples (also used as tests)
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 # Note: at the bottom, there's a slightly more complete version of this that
36 # Note: at the bottom, there's a slightly more complete version of this that
37 # can be useful during development of code here.
37 # can be useful during development of code here.
38
38
39 def mini_interactive_loop(input_func):
39 def mini_interactive_loop(input_func):
40 """Minimal example of the logic of an interactive interpreter loop.
40 """Minimal example of the logic of an interactive interpreter loop.
41
41
42 This serves as an example, and it is used by the test system with a fake
42 This serves as an example, and it is used by the test system with a fake
43 raw_input that simulates interactive input."""
43 raw_input that simulates interactive input."""
44
44
45 from IPython.core.inputsplitter import InputSplitter
45 from IPython.core.inputsplitter import InputSplitter
46
46
47 isp = InputSplitter()
47 isp = InputSplitter()
48 # In practice, this input loop would be wrapped in an outside loop to read
48 # In practice, this input loop would be wrapped in an outside loop to read
49 # input indefinitely, until some exit/quit command was issued. Here we
49 # input indefinitely, until some exit/quit command was issued. Here we
50 # only illustrate the basic inner loop.
50 # only illustrate the basic inner loop.
51 while isp.push_accepts_more():
51 while isp.push_accepts_more():
52 indent = ' '*isp.indent_spaces
52 indent = ' '*isp.indent_spaces
53 prompt = '>>> ' + indent
53 prompt = '>>> ' + indent
54 line = indent + input_func(prompt)
54 line = indent + input_func(prompt)
55 isp.push(line)
55 isp.push(line)
56
56
57 # Here we just return input so we can use it in a test suite, but a real
57 # Here we just return input so we can use it in a test suite, but a real
58 # interpreter would instead send it for execution somewhere.
58 # interpreter would instead send it for execution somewhere.
59 src = isp.source_reset()
59 src = isp.source_reset()
60 #print 'Input source was:\n', src # dbg
60 #print 'Input source was:\n', src # dbg
61 return src
61 return src
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Test utilities, just for local use
64 # Test utilities, just for local use
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67 def assemble(block):
67 def assemble(block):
68 """Assemble a block into multi-line sub-blocks."""
68 """Assemble a block into multi-line sub-blocks."""
69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
69 return ['\n'.join(sub_block)+'\n' for sub_block in block]
70
70
71
71
72 def pseudo_input(lines):
72 def pseudo_input(lines):
73 """Return a function that acts like raw_input but feeds the input list."""
73 """Return a function that acts like raw_input but feeds the input list."""
74 ilines = iter(lines)
74 ilines = iter(lines)
75 def raw_in(prompt):
75 def raw_in(prompt):
76 try:
76 try:
77 return next(ilines)
77 return next(ilines)
78 except StopIteration:
78 except StopIteration:
79 return ''
79 return ''
80 return raw_in
80 return raw_in
81
81
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83 # Tests
83 # Tests
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85 def test_spaces():
85 def test_spaces():
86 tests = [('', 0),
86 tests = [('', 0),
87 (' ', 1),
87 (' ', 1),
88 ('\n', 0),
88 ('\n', 0),
89 (' \n', 1),
89 (' \n', 1),
90 ('x', 0),
90 ('x', 0),
91 (' x', 1),
91 (' x', 1),
92 (' x',2),
92 (' x',2),
93 (' x',4),
93 (' x',4),
94 # Note: tabs are counted as a single whitespace!
94 # Note: tabs are counted as a single whitespace!
95 ('\tx', 1),
95 ('\tx', 1),
96 ('\t x', 2),
96 ('\t x', 2),
97 ]
97 ]
98 tt.check_pairs(isp.num_ini_spaces, tests)
98 tt.check_pairs(isp.num_ini_spaces, tests)
99
99
100
100
101 def test_remove_comments():
101 def test_remove_comments():
102 tests = [('text', 'text'),
102 tests = [('text', 'text'),
103 ('text # comment', 'text '),
103 ('text # comment', 'text '),
104 ('text # comment\n', 'text \n'),
104 ('text # comment\n', 'text \n'),
105 ('text # comment \n', 'text \n'),
105 ('text # comment \n', 'text \n'),
106 ('line # c \nline\n','line \nline\n'),
106 ('line # c \nline\n','line \nline\n'),
107 ('line # c \nline#c2 \nline\nline #c\n\n',
107 ('line # c \nline#c2 \nline\nline #c\n\n',
108 'line \nline\nline\nline \n\n'),
108 'line \nline\nline\nline \n\n'),
109 ]
109 ]
110 tt.check_pairs(isp.remove_comments, tests)
110 tt.check_pairs(isp.remove_comments, tests)
111
111
112
112
113 def test_get_input_encoding():
113 def test_get_input_encoding():
114 encoding = isp.get_input_encoding()
114 encoding = isp.get_input_encoding()
115 nt.assert_true(isinstance(encoding, basestring))
115 nt.assert_true(isinstance(encoding, basestring))
116 # simple-minded check that at least encoding a simple string works with the
116 # simple-minded check that at least encoding a simple string works with the
117 # encoding we got.
117 # encoding we got.
118 nt.assert_equal(u'test'.encode(encoding), b'test')
118 nt.assert_equal(u'test'.encode(encoding), b'test')
119
119
120
120
121 class NoInputEncodingTestCase(unittest.TestCase):
121 class NoInputEncodingTestCase(unittest.TestCase):
122 def setUp(self):
122 def setUp(self):
123 self.old_stdin = sys.stdin
123 self.old_stdin = sys.stdin
124 class X: pass
124 class X: pass
125 fake_stdin = X()
125 fake_stdin = X()
126 sys.stdin = fake_stdin
126 sys.stdin = fake_stdin
127
127
128 def test(self):
128 def test(self):
129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
129 # Verify that if sys.stdin has no 'encoding' attribute we do the right
130 # thing
130 # thing
131 enc = isp.get_input_encoding()
131 enc = isp.get_input_encoding()
132 self.assertEqual(enc, 'ascii')
132 self.assertEqual(enc, 'ascii')
133
133
134 def tearDown(self):
134 def tearDown(self):
135 sys.stdin = self.old_stdin
135 sys.stdin = self.old_stdin
136
136
137
137
138 class InputSplitterTestCase(unittest.TestCase):
138 class InputSplitterTestCase(unittest.TestCase):
139 def setUp(self):
139 def setUp(self):
140 self.isp = isp.InputSplitter()
140 self.isp = isp.InputSplitter()
141
141
142 def test_reset(self):
142 def test_reset(self):
143 isp = self.isp
143 isp = self.isp
144 isp.push('x=1')
144 isp.push('x=1')
145 isp.reset()
145 isp.reset()
146 self.assertEqual(isp._buffer, [])
146 self.assertEqual(isp._buffer, [])
147 self.assertEqual(isp.indent_spaces, 0)
147 self.assertEqual(isp.indent_spaces, 0)
148 self.assertEqual(isp.source, '')
148 self.assertEqual(isp.source, '')
149 self.assertEqual(isp.code, None)
149 self.assertEqual(isp.code, None)
150 self.assertEqual(isp._is_complete, False)
150 self.assertEqual(isp._is_complete, False)
151
151
152 def test_source(self):
152 def test_source(self):
153 self.isp._store('1')
153 self.isp._store('1')
154 self.isp._store('2')
154 self.isp._store('2')
155 self.assertEqual(self.isp.source, '1\n2\n')
155 self.assertEqual(self.isp.source, '1\n2\n')
156 self.assertTrue(len(self.isp._buffer)>0)
156 self.assertTrue(len(self.isp._buffer)>0)
157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
157 self.assertEqual(self.isp.source_reset(), '1\n2\n')
158 self.assertEqual(self.isp._buffer, [])
158 self.assertEqual(self.isp._buffer, [])
159 self.assertEqual(self.isp.source, '')
159 self.assertEqual(self.isp.source, '')
160
160
161 def test_indent(self):
161 def test_indent(self):
162 isp = self.isp # shorthand
162 isp = self.isp # shorthand
163 isp.push('x=1')
163 isp.push('x=1')
164 self.assertEqual(isp.indent_spaces, 0)
164 self.assertEqual(isp.indent_spaces, 0)
165 isp.push('if 1:\n x=1')
165 isp.push('if 1:\n x=1')
166 self.assertEqual(isp.indent_spaces, 4)
166 self.assertEqual(isp.indent_spaces, 4)
167 isp.push('y=2\n')
167 isp.push('y=2\n')
168 self.assertEqual(isp.indent_spaces, 0)
168 self.assertEqual(isp.indent_spaces, 0)
169
169
170 def test_indent2(self):
170 def test_indent2(self):
171 # In cell mode, inputs must be fed in whole blocks, so skip this test
171 # In cell mode, inputs must be fed in whole blocks, so skip this test
172 if self.isp.input_mode == 'cell': return
172 if self.isp.input_mode == 'cell': return
173
173
174 isp = self.isp
174 isp = self.isp
175 isp.push('if 1:')
175 isp.push('if 1:')
176 self.assertEqual(isp.indent_spaces, 4)
176 self.assertEqual(isp.indent_spaces, 4)
177 isp.push(' x=1')
177 isp.push(' x=1')
178 self.assertEqual(isp.indent_spaces, 4)
178 self.assertEqual(isp.indent_spaces, 4)
179 # Blank lines shouldn't change the indent level
179 # Blank lines shouldn't change the indent level
180 isp.push(' '*2)
180 isp.push(' '*2)
181 self.assertEqual(isp.indent_spaces, 4)
181 self.assertEqual(isp.indent_spaces, 4)
182
182
183 def test_indent3(self):
183 def test_indent3(self):
184 # In cell mode, inputs must be fed in whole blocks, so skip this test
184 # In cell mode, inputs must be fed in whole blocks, so skip this test
185 if self.isp.input_mode == 'cell': return
185 if self.isp.input_mode == 'cell': return
186
186
187 isp = self.isp
187 isp = self.isp
188 # When a multiline statement contains parens or multiline strings, we
188 # When a multiline statement contains parens or multiline strings, we
189 # shouldn't get confused.
189 # shouldn't get confused.
190 isp.push("if 1:")
190 isp.push("if 1:")
191 isp.push(" x = (1+\n 2)")
191 isp.push(" x = (1+\n 2)")
192 self.assertEqual(isp.indent_spaces, 4)
192 self.assertEqual(isp.indent_spaces, 4)
193
193
194 def test_indent4(self):
194 def test_indent4(self):
195 # In cell mode, inputs must be fed in whole blocks, so skip this test
195 # In cell mode, inputs must be fed in whole blocks, so skip this test
196 if self.isp.input_mode == 'cell': return
196 if self.isp.input_mode == 'cell': return
197
197
198 isp = self.isp
198 isp = self.isp
199 # whitespace after ':' should not screw up indent level
199 # whitespace after ':' should not screw up indent level
200 isp.push('if 1: \n x=1')
200 isp.push('if 1: \n x=1')
201 self.assertEqual(isp.indent_spaces, 4)
201 self.assertEqual(isp.indent_spaces, 4)
202 isp.push('y=2\n')
202 isp.push('y=2\n')
203 self.assertEqual(isp.indent_spaces, 0)
203 self.assertEqual(isp.indent_spaces, 0)
204 isp.push('if 1:\t\n x=1')
204 isp.push('if 1:\t\n x=1')
205 self.assertEqual(isp.indent_spaces, 4)
205 self.assertEqual(isp.indent_spaces, 4)
206 isp.push('y=2\n')
206 isp.push('y=2\n')
207 self.assertEqual(isp.indent_spaces, 0)
207 self.assertEqual(isp.indent_spaces, 0)
208
208
209 def test_dedent_pass(self):
209 def test_dedent_pass(self):
210 isp = self.isp # shorthand
210 isp = self.isp # shorthand
211 # should NOT cause dedent
211 # should NOT cause dedent
212 isp.push('if 1:\n passes = 5')
212 isp.push('if 1:\n passes = 5')
213 self.assertEqual(isp.indent_spaces, 4)
213 self.assertEqual(isp.indent_spaces, 4)
214 isp.push('if 1:\n pass')
214 isp.push('if 1:\n pass')
215 self.assertEqual(isp.indent_spaces, 0)
215 self.assertEqual(isp.indent_spaces, 0)
216 isp.push('if 1:\n pass ')
216 isp.push('if 1:\n pass ')
217 self.assertEqual(isp.indent_spaces, 0)
217 self.assertEqual(isp.indent_spaces, 0)
218
218
219 def test_dedent_break(self):
219 def test_dedent_break(self):
220 isp = self.isp # shorthand
220 isp = self.isp # shorthand
221 # should NOT cause dedent
221 # should NOT cause dedent
222 isp.push('while 1:\n breaks = 5')
222 isp.push('while 1:\n breaks = 5')
223 self.assertEqual(isp.indent_spaces, 4)
223 self.assertEqual(isp.indent_spaces, 4)
224 isp.push('while 1:\n break')
224 isp.push('while 1:\n break')
225 self.assertEqual(isp.indent_spaces, 0)
225 self.assertEqual(isp.indent_spaces, 0)
226 isp.push('while 1:\n break ')
226 isp.push('while 1:\n break ')
227 self.assertEqual(isp.indent_spaces, 0)
227 self.assertEqual(isp.indent_spaces, 0)
228
228
229 def test_dedent_continue(self):
229 def test_dedent_continue(self):
230 isp = self.isp # shorthand
230 isp = self.isp # shorthand
231 # should NOT cause dedent
231 # should NOT cause dedent
232 isp.push('while 1:\n continues = 5')
232 isp.push('while 1:\n continues = 5')
233 self.assertEqual(isp.indent_spaces, 4)
233 self.assertEqual(isp.indent_spaces, 4)
234 isp.push('while 1:\n continue')
234 isp.push('while 1:\n continue')
235 self.assertEqual(isp.indent_spaces, 0)
235 self.assertEqual(isp.indent_spaces, 0)
236 isp.push('while 1:\n continue ')
236 isp.push('while 1:\n continue ')
237 self.assertEqual(isp.indent_spaces, 0)
237 self.assertEqual(isp.indent_spaces, 0)
238
238
239 def test_dedent_raise(self):
239 def test_dedent_raise(self):
240 isp = self.isp # shorthand
240 isp = self.isp # shorthand
241 # should NOT cause dedent
241 # should NOT cause dedent
242 isp.push('if 1:\n raised = 4')
242 isp.push('if 1:\n raised = 4')
243 self.assertEqual(isp.indent_spaces, 4)
243 self.assertEqual(isp.indent_spaces, 4)
244 isp.push('if 1:\n raise TypeError()')
244 isp.push('if 1:\n raise TypeError()')
245 self.assertEqual(isp.indent_spaces, 0)
245 self.assertEqual(isp.indent_spaces, 0)
246 isp.push('if 1:\n raise')
246 isp.push('if 1:\n raise')
247 self.assertEqual(isp.indent_spaces, 0)
247 self.assertEqual(isp.indent_spaces, 0)
248 isp.push('if 1:\n raise ')
248 isp.push('if 1:\n raise ')
249 self.assertEqual(isp.indent_spaces, 0)
249 self.assertEqual(isp.indent_spaces, 0)
250
250
251 def test_dedent_return(self):
251 def test_dedent_return(self):
252 isp = self.isp # shorthand
252 isp = self.isp # shorthand
253 # should NOT cause dedent
253 # should NOT cause dedent
254 isp.push('if 1:\n returning = 4')
254 isp.push('if 1:\n returning = 4')
255 self.assertEqual(isp.indent_spaces, 4)
255 self.assertEqual(isp.indent_spaces, 4)
256 isp.push('if 1:\n return 5 + 493')
256 isp.push('if 1:\n return 5 + 493')
257 self.assertEqual(isp.indent_spaces, 0)
257 self.assertEqual(isp.indent_spaces, 0)
258 isp.push('if 1:\n return')
258 isp.push('if 1:\n return')
259 self.assertEqual(isp.indent_spaces, 0)
259 self.assertEqual(isp.indent_spaces, 0)
260 isp.push('if 1:\n return ')
260 isp.push('if 1:\n return ')
261 self.assertEqual(isp.indent_spaces, 0)
261 self.assertEqual(isp.indent_spaces, 0)
262 isp.push('if 1:\n return(0)')
262 isp.push('if 1:\n return(0)')
263 self.assertEqual(isp.indent_spaces, 0)
263 self.assertEqual(isp.indent_spaces, 0)
264
264
265 def test_push(self):
265 def test_push(self):
266 isp = self.isp
266 isp = self.isp
267 self.assertTrue(isp.push('x=1'))
267 self.assertTrue(isp.push('x=1'))
268
268
269 def test_push2(self):
269 def test_push2(self):
270 isp = self.isp
270 isp = self.isp
271 self.assertFalse(isp.push('if 1:'))
271 self.assertFalse(isp.push('if 1:'))
272 for line in [' x=1', '# a comment', ' y=2']:
272 for line in [' x=1', '# a comment', ' y=2']:
273 print(line)
273 self.assertTrue(isp.push(line))
274 self.assertTrue(isp.push(line))
274
275
275 def test_push3(self):
276 def test_push3(self):
276 isp = self.isp
277 isp = self.isp
277 isp.push('if True:')
278 isp.push('if True:')
278 isp.push(' a = 1')
279 isp.push(' a = 1')
279 self.assertFalse(isp.push('b = [1,'))
280 self.assertFalse(isp.push('b = [1,'))
280
281
281 def test_replace_mode(self):
282 def test_replace_mode(self):
282 isp = self.isp
283 isp = self.isp
283 isp.input_mode = 'cell'
284 isp.input_mode = 'cell'
284 isp.push('x=1')
285 isp.push('x=1')
285 self.assertEqual(isp.source, 'x=1\n')
286 self.assertEqual(isp.source, 'x=1\n')
286 isp.push('x=2')
287 isp.push('x=2')
287 self.assertEqual(isp.source, 'x=2\n')
288 self.assertEqual(isp.source, 'x=2\n')
288
289
289 def test_push_accepts_more(self):
290 def test_push_accepts_more(self):
290 isp = self.isp
291 isp = self.isp
291 isp.push('x=1')
292 isp.push('x=1')
292 self.assertFalse(isp.push_accepts_more())
293 self.assertFalse(isp.push_accepts_more())
293
294
294 def test_push_accepts_more2(self):
295 def test_push_accepts_more2(self):
295 # In cell mode, inputs must be fed in whole blocks, so skip this test
296 # In cell mode, inputs must be fed in whole blocks, so skip this test
296 if self.isp.input_mode == 'cell': return
297 if self.isp.input_mode == 'cell': return
297
298
298 isp = self.isp
299 isp = self.isp
299 isp.push('if 1:')
300 isp.push('if 1:')
300 self.assertTrue(isp.push_accepts_more())
301 self.assertTrue(isp.push_accepts_more())
301 isp.push(' x=1')
302 isp.push(' x=1')
302 self.assertTrue(isp.push_accepts_more())
303 self.assertTrue(isp.push_accepts_more())
303 isp.push('')
304 isp.push('')
304 self.assertFalse(isp.push_accepts_more())
305 self.assertFalse(isp.push_accepts_more())
305
306
306 def test_push_accepts_more3(self):
307 def test_push_accepts_more3(self):
307 isp = self.isp
308 isp = self.isp
308 isp.push("x = (2+\n3)")
309 isp.push("x = (2+\n3)")
309 self.assertFalse(isp.push_accepts_more())
310 self.assertFalse(isp.push_accepts_more())
310
311
311 def test_push_accepts_more4(self):
312 def test_push_accepts_more4(self):
312 # In cell mode, inputs must be fed in whole blocks, so skip this test
313 # In cell mode, inputs must be fed in whole blocks, so skip this test
313 if self.isp.input_mode == 'cell': return
314 if self.isp.input_mode == 'cell': return
314
315
315 isp = self.isp
316 isp = self.isp
316 # When a multiline statement contains parens or multiline strings, we
317 # When a multiline statement contains parens or multiline strings, we
317 # shouldn't get confused.
318 # shouldn't get confused.
318 # FIXME: we should be able to better handle de-dents in statements like
319 # FIXME: we should be able to better handle de-dents in statements like
319 # multiline strings and multiline expressions (continued with \ or
320 # multiline strings and multiline expressions (continued with \ or
320 # parens). Right now we aren't handling the indentation tracking quite
321 # parens). Right now we aren't handling the indentation tracking quite
321 # correctly with this, though in practice it may not be too much of a
322 # correctly with this, though in practice it may not be too much of a
322 # problem. We'll need to see.
323 # problem. We'll need to see.
323 isp.push("if 1:")
324 isp.push("if 1:")
324 isp.push(" x = (2+")
325 isp.push(" x = (2+")
325 isp.push(" 3)")
326 isp.push(" 3)")
326 self.assertTrue(isp.push_accepts_more())
327 self.assertTrue(isp.push_accepts_more())
327 isp.push(" y = 3")
328 isp.push(" y = 3")
328 self.assertTrue(isp.push_accepts_more())
329 self.assertTrue(isp.push_accepts_more())
329 isp.push('')
330 isp.push('')
330 self.assertFalse(isp.push_accepts_more())
331 self.assertFalse(isp.push_accepts_more())
331
332
332 def test_push_accepts_more5(self):
333 def test_push_accepts_more5(self):
333 # In cell mode, inputs must be fed in whole blocks, so skip this test
334 # In cell mode, inputs must be fed in whole blocks, so skip this test
334 if self.isp.input_mode == 'cell': return
335 if self.isp.input_mode == 'cell': return
335
336
336 isp = self.isp
337 isp = self.isp
337 isp.push('try:')
338 isp.push('try:')
338 isp.push(' a = 5')
339 isp.push(' a = 5')
339 isp.push('except:')
340 isp.push('except:')
340 isp.push(' raise')
341 isp.push(' raise')
341 self.assertTrue(isp.push_accepts_more())
342 self.assertTrue(isp.push_accepts_more())
342
343
343 def test_continuation(self):
344 def test_continuation(self):
344 isp = self.isp
345 isp = self.isp
345 isp.push("import os, \\")
346 isp.push("import os, \\")
346 self.assertTrue(isp.push_accepts_more())
347 self.assertTrue(isp.push_accepts_more())
347 isp.push("sys")
348 isp.push("sys")
348 self.assertFalse(isp.push_accepts_more())
349 self.assertFalse(isp.push_accepts_more())
349
350
350 def test_syntax_error(self):
351 def test_syntax_error(self):
351 isp = self.isp
352 isp = self.isp
352 # Syntax errors immediately produce a 'ready' block, so the invalid
353 # Syntax errors immediately produce a 'ready' block, so the invalid
353 # Python can be sent to the kernel for evaluation with possible ipython
354 # Python can be sent to the kernel for evaluation with possible ipython
354 # special-syntax conversion.
355 # special-syntax conversion.
355 isp.push('run foo')
356 isp.push('run foo')
356 self.assertFalse(isp.push_accepts_more())
357 self.assertFalse(isp.push_accepts_more())
357
358
358 def test_unicode(self):
359 def test_unicode(self):
359 self.isp.push(u"PΓ©rez")
360 self.isp.push(u"PΓ©rez")
360 self.isp.push(u'\xc3\xa9')
361 self.isp.push(u'\xc3\xa9')
361 self.isp.push(u"u'\xc3\xa9'")
362 self.isp.push(u"u'\xc3\xa9'")
362
363
363 def test_line_continuation(self):
364 def test_line_continuation(self):
364 """ Test issue #2108."""
365 """ Test issue #2108."""
365 isp = self.isp
366 isp = self.isp
366 # A blank line after a line continuation should not accept more
367 # A blank line after a line continuation should not accept more
367 isp.push("1 \\\n\n")
368 isp.push("1 \\\n\n")
368 self.assertFalse(isp.push_accepts_more())
369 self.assertFalse(isp.push_accepts_more())
369 # Whitespace after a \ is a SyntaxError. The only way to test that
370 # Whitespace after a \ is a SyntaxError. The only way to test that
370 # here is to test that push doesn't accept more (as with
371 # here is to test that push doesn't accept more (as with
371 # test_syntax_error() above).
372 # test_syntax_error() above).
372 isp.push(r"1 \ ")
373 isp.push(r"1 \ ")
373 self.assertFalse(isp.push_accepts_more())
374 self.assertFalse(isp.push_accepts_more())
374 # Even if the line is continuable (c.f. the regular Python
375 # Even if the line is continuable (c.f. the regular Python
375 # interpreter)
376 # interpreter)
376 isp.push(r"(1 \ ")
377 isp.push(r"(1 \ ")
377 self.assertFalse(isp.push_accepts_more())
378 self.assertFalse(isp.push_accepts_more())
378
379
379 class InteractiveLoopTestCase(unittest.TestCase):
380 class InteractiveLoopTestCase(unittest.TestCase):
380 """Tests for an interactive loop like a python shell.
381 """Tests for an interactive loop like a python shell.
381 """
382 """
382 def check_ns(self, lines, ns):
383 def check_ns(self, lines, ns):
383 """Validate that the given input lines produce the resulting namespace.
384 """Validate that the given input lines produce the resulting namespace.
384
385
385 Note: the input lines are given exactly as they would be typed in an
386 Note: the input lines are given exactly as they would be typed in an
386 auto-indenting environment, as mini_interactive_loop above already does
387 auto-indenting environment, as mini_interactive_loop above already does
387 auto-indenting and prepends spaces to the input.
388 auto-indenting and prepends spaces to the input.
388 """
389 """
389 src = mini_interactive_loop(pseudo_input(lines))
390 src = mini_interactive_loop(pseudo_input(lines))
390 test_ns = {}
391 test_ns = {}
391 exec src in test_ns
392 exec src in test_ns
392 # We can't check that the provided ns is identical to the test_ns,
393 # We can't check that the provided ns is identical to the test_ns,
393 # because Python fills test_ns with extra keys (copyright, etc). But
394 # because Python fills test_ns with extra keys (copyright, etc). But
394 # we can check that the given dict is *contained* in test_ns
395 # we can check that the given dict is *contained* in test_ns
395 for k,v in ns.iteritems():
396 for k,v in ns.iteritems():
396 self.assertEqual(test_ns[k], v)
397 self.assertEqual(test_ns[k], v)
397
398
398 def test_simple(self):
399 def test_simple(self):
399 self.check_ns(['x=1'], dict(x=1))
400 self.check_ns(['x=1'], dict(x=1))
400
401
401 def test_simple2(self):
402 def test_simple2(self):
402 self.check_ns(['if 1:', 'x=2'], dict(x=2))
403 self.check_ns(['if 1:', 'x=2'], dict(x=2))
403
404
404 def test_xy(self):
405 def test_xy(self):
405 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
406 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
406
407
407 def test_abc(self):
408 def test_abc(self):
408 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
409 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
409
410
410 def test_multi(self):
411 def test_multi(self):
411 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
412 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
412
413
413
414
414 def test_LineInfo():
415 def test_LineInfo():
415 """Simple test for LineInfo construction and str()"""
416 """Simple test for LineInfo construction and str()"""
416 linfo = isp.LineInfo(' %cd /home')
417 linfo = isp.LineInfo(' %cd /home')
417 nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]')
418 nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]')
418
419
419
420
420
421
421
422
422 class IPythonInputTestCase(InputSplitterTestCase):
423 class IPythonInputTestCase(InputSplitterTestCase):
423 """By just creating a new class whose .isp is a different instance, we
424 """By just creating a new class whose .isp is a different instance, we
424 re-run the same test battery on the new input splitter.
425 re-run the same test battery on the new input splitter.
425
426
426 In addition, this runs the tests over the syntax and syntax_ml dicts that
427 In addition, this runs the tests over the syntax and syntax_ml dicts that
427 were tested by individual functions, as part of the OO interface.
428 were tested by individual functions, as part of the OO interface.
428
429
429 It also makes some checks on the raw buffer storage.
430 It also makes some checks on the raw buffer storage.
430 """
431 """
431
432
432 def setUp(self):
433 def setUp(self):
433 self.isp = isp.IPythonInputSplitter(input_mode='line')
434 self.isp = isp.IPythonInputSplitter(input_mode='line')
434
435
435 def test_syntax(self):
436 def test_syntax(self):
436 """Call all single-line syntax tests from the main object"""
437 """Call all single-line syntax tests from the main object"""
437 isp = self.isp
438 isp = self.isp
438 for example in syntax.itervalues():
439 for example in syntax.itervalues():
439 for raw, out_t in example:
440 for raw, out_t in example:
440 if raw.startswith(' '):
441 if raw.startswith(' '):
441 continue
442 continue
442
443
443 isp.push(raw+'\n')
444 isp.push(raw+'\n')
444 out, out_raw = isp.source_raw_reset()
445 out, out_raw = isp.source_raw_reset()
445 self.assertEqual(out.rstrip(), out_t,
446 self.assertEqual(out.rstrip(), out_t,
446 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
447 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
447 self.assertEqual(out_raw.rstrip(), raw.rstrip())
448 self.assertEqual(out_raw.rstrip(), raw.rstrip())
448
449
449 def test_syntax_multiline(self):
450 def test_syntax_multiline(self):
450 isp = self.isp
451 isp = self.isp
451 for example in syntax_ml.itervalues():
452 for example in syntax_ml.itervalues():
452 for line_pairs in example:
453 for line_pairs in example:
453 out_t_parts = []
454 out_t_parts = []
454 raw_parts = []
455 raw_parts = []
455 for lraw, out_t_part in line_pairs:
456 for lraw, out_t_part in line_pairs:
456 if out_t_part is not None:
457 if out_t_part is not None:
457 out_t_parts.append(out_t_part)
458 out_t_parts.append(out_t_part)
458
459
459 if lraw is not None:
460 if lraw is not None:
460 isp.push(lraw)
461 isp.push(lraw)
461 raw_parts.append(lraw)
462 raw_parts.append(lraw)
462
463
463 out, out_raw = isp.source_raw_reset()
464 out, out_raw = isp.source_raw_reset()
464 out_t = '\n'.join(out_t_parts).rstrip()
465 out_t = '\n'.join(out_t_parts).rstrip()
465 raw = '\n'.join(raw_parts).rstrip()
466 raw = '\n'.join(raw_parts).rstrip()
466 self.assertEqual(out.rstrip(), out_t)
467 self.assertEqual(out.rstrip(), out_t)
467 self.assertEqual(out_raw.rstrip(), raw)
468 self.assertEqual(out_raw.rstrip(), raw)
468
469
469
470
470 class BlockIPythonInputTestCase(IPythonInputTestCase):
471 class BlockIPythonInputTestCase(IPythonInputTestCase):
471
472
472 # Deactivate tests that don't make sense for the block mode
473 # Deactivate tests that don't make sense for the block mode
473 test_push3 = test_split = lambda s: None
474 test_push3 = test_split = lambda s: None
474
475
475 def setUp(self):
476 def setUp(self):
476 self.isp = isp.IPythonInputSplitter(input_mode='cell')
477 self.isp = isp.IPythonInputSplitter(input_mode='cell')
477
478
478 def test_syntax_multiline(self):
479 def test_syntax_multiline(self):
479 isp = self.isp
480 isp = self.isp
480 for example in syntax_ml.itervalues():
481 for example in syntax_ml.itervalues():
481 raw_parts = []
482 raw_parts = []
482 out_t_parts = []
483 out_t_parts = []
483 for line_pairs in example:
484 for line_pairs in example:
484 raw_parts, out_t_parts = zip(*line_pairs)
485 raw_parts, out_t_parts = zip(*line_pairs)
485
486
486 raw = '\n'.join(r for r in raw_parts if r is not None)
487 raw = '\n'.join(r for r in raw_parts if r is not None)
487 out_t = '\n'.join(o for o in out_t_parts if o is not None)
488 out_t = '\n'.join(o for o in out_t_parts if o is not None)
488
489
489 isp.push(raw)
490 isp.push(raw)
490 out, out_raw = isp.source_raw_reset()
491 out, out_raw = isp.source_raw_reset()
491 # Match ignoring trailing whitespace
492 # Match ignoring trailing whitespace
492 self.assertEqual(out.rstrip(), out_t.rstrip())
493 self.assertEqual(out.rstrip(), out_t.rstrip())
493 self.assertEqual(out_raw.rstrip(), raw.rstrip())
494 self.assertEqual(out_raw.rstrip(), raw.rstrip())
494
495
495 def test_syntax_multiline_cell(self):
496 def test_syntax_multiline_cell(self):
496 isp = self.isp
497 isp = self.isp
497 for example in syntax_ml.itervalues():
498 for example in syntax_ml.itervalues():
498
499
499 out_t_parts = []
500 out_t_parts = []
500 for line_pairs in example:
501 for line_pairs in example:
501 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
502 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
502 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
503 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
503 out = isp.transform_cell(raw)
504 out = isp.transform_cell(raw)
504 # Match ignoring trailing whitespace
505 # Match ignoring trailing whitespace
505 self.assertEqual(out.rstrip(), out_t.rstrip())
506 self.assertEqual(out.rstrip(), out_t.rstrip())
506
507
507 #-----------------------------------------------------------------------------
508 #-----------------------------------------------------------------------------
508 # Main - use as a script, mostly for developer experiments
509 # Main - use as a script, mostly for developer experiments
509 #-----------------------------------------------------------------------------
510 #-----------------------------------------------------------------------------
510
511
511 if __name__ == '__main__':
512 if __name__ == '__main__':
512 # A simple demo for interactive experimentation. This code will not get
513 # A simple demo for interactive experimentation. This code will not get
513 # picked up by any test suite.
514 # picked up by any test suite.
514 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
515 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
515
516
516 # configure here the syntax to use, prompt and whether to autoindent
517 # configure here the syntax to use, prompt and whether to autoindent
517 #isp, start_prompt = InputSplitter(), '>>> '
518 #isp, start_prompt = InputSplitter(), '>>> '
518 isp, start_prompt = IPythonInputSplitter(), 'In> '
519 isp, start_prompt = IPythonInputSplitter(), 'In> '
519
520
520 autoindent = True
521 autoindent = True
521 #autoindent = False
522 #autoindent = False
522
523
523 try:
524 try:
524 while True:
525 while True:
525 prompt = start_prompt
526 prompt = start_prompt
526 while isp.push_accepts_more():
527 while isp.push_accepts_more():
527 indent = ' '*isp.indent_spaces
528 indent = ' '*isp.indent_spaces
528 if autoindent:
529 if autoindent:
529 line = indent + raw_input(prompt+indent)
530 line = indent + raw_input(prompt+indent)
530 else:
531 else:
531 line = raw_input(prompt)
532 line = raw_input(prompt)
532 isp.push(line)
533 isp.push(line)
533 prompt = '... '
534 prompt = '... '
534
535
535 # Here we just return input so we can use it in a test suite, but a
536 # Here we just return input so we can use it in a test suite, but a
536 # real interpreter would instead send it for execution somewhere.
537 # real interpreter would instead send it for execution somewhere.
537 #src = isp.source; raise EOFError # dbg
538 #src = isp.source; raise EOFError # dbg
538 src, raw = isp.source_raw_reset()
539 src, raw = isp.source_raw_reset()
539 print 'Input source was:\n', src
540 print 'Input source was:\n', src
540 print 'Raw source was:\n', raw
541 print 'Raw source was:\n', raw
541 except EOFError:
542 except EOFError:
542 print 'Bye'
543 print 'Bye'
543
544
544 # Tests for cell magics support
545 # Tests for cell magics support
545
546
546 def test_last_blank():
547 def test_last_blank():
547 nt.assert_false(isp.last_blank(''))
548 nt.assert_false(isp.last_blank(''))
548 nt.assert_false(isp.last_blank('abc'))
549 nt.assert_false(isp.last_blank('abc'))
549 nt.assert_false(isp.last_blank('abc\n'))
550 nt.assert_false(isp.last_blank('abc\n'))
550 nt.assert_false(isp.last_blank('abc\na'))
551 nt.assert_false(isp.last_blank('abc\na'))
551
552
552 nt.assert_true(isp.last_blank('\n'))
553 nt.assert_true(isp.last_blank('\n'))
553 nt.assert_true(isp.last_blank('\n '))
554 nt.assert_true(isp.last_blank('\n '))
554 nt.assert_true(isp.last_blank('abc\n '))
555 nt.assert_true(isp.last_blank('abc\n '))
555 nt.assert_true(isp.last_blank('abc\n\n'))
556 nt.assert_true(isp.last_blank('abc\n\n'))
556 nt.assert_true(isp.last_blank('abc\nd\n\n'))
557 nt.assert_true(isp.last_blank('abc\nd\n\n'))
557 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
558 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
558 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
559 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
559
560
560
561
561 def test_last_two_blanks():
562 def test_last_two_blanks():
562 nt.assert_false(isp.last_two_blanks(''))
563 nt.assert_false(isp.last_two_blanks(''))
563 nt.assert_false(isp.last_two_blanks('abc'))
564 nt.assert_false(isp.last_two_blanks('abc'))
564 nt.assert_false(isp.last_two_blanks('abc\n'))
565 nt.assert_false(isp.last_two_blanks('abc\n'))
565 nt.assert_false(isp.last_two_blanks('abc\n\na'))
566 nt.assert_false(isp.last_two_blanks('abc\n\na'))
566 nt.assert_false(isp.last_two_blanks('abc\n \n'))
567 nt.assert_false(isp.last_two_blanks('abc\n \n'))
567 nt.assert_false(isp.last_two_blanks('abc\n\n'))
568 nt.assert_false(isp.last_two_blanks('abc\n\n'))
568
569
569 nt.assert_true(isp.last_two_blanks('\n\n'))
570 nt.assert_true(isp.last_two_blanks('\n\n'))
570 nt.assert_true(isp.last_two_blanks('\n\n '))
571 nt.assert_true(isp.last_two_blanks('\n\n '))
571 nt.assert_true(isp.last_two_blanks('\n \n'))
572 nt.assert_true(isp.last_two_blanks('\n \n'))
572 nt.assert_true(isp.last_two_blanks('abc\n\n '))
573 nt.assert_true(isp.last_two_blanks('abc\n\n '))
573 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
574 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
574 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
575 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
575 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
576 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
576 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
577 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
577 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
578 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
578 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
579 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
579
580
580
581
581 class CellMagicsCommon(object):
582 class CellMagicsCommon(object):
582
583
583 def test_whole_cell(self):
584 def test_whole_cell(self):
584 src = "%%cellm line\nbody\n"
585 src = "%%cellm line\nbody\n"
585 sp = self.sp
586 sp = self.sp
586 sp.push(src)
587 sp.push(src)
587 out = sp.source_reset()
588 out = sp.source_reset()
588 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
589 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
589 nt.assert_equal(out, py3compat.u_format(ref))
590 nt.assert_equal(out, py3compat.u_format(ref))
590
591
591 def tearDown(self):
592 def tearDown(self):
592 self.sp.reset()
593 self.sp.reset()
593
594
594
595
595 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
596 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
596 sp = isp.IPythonInputSplitter(input_mode='cell')
597 sp = isp.IPythonInputSplitter(input_mode='cell')
597
598
598 def test_incremental(self):
599 def test_incremental(self):
599 sp = self.sp
600 sp = self.sp
600 src = '%%cellm line2\n'
601 src = '%%cellm line2\n'
601 sp.push(src)
602 sp.push(src)
602 nt.assert_true(sp.push_accepts_more()) #1
603 nt.assert_true(sp.push_accepts_more()) #1
603 src += '\n'
604 src += '\n'
604 sp.push(src)
605 sp.push(src)
605 # Note: if we ever change the logic to allow full blank lines (see
606 # Note: if we ever change the logic to allow full blank lines (see
606 # _handle_cell_magic), then the following test should change to true
607 # _handle_cell_magic), then the following test should change to true
607 nt.assert_false(sp.push_accepts_more()) #2
608 nt.assert_false(sp.push_accepts_more()) #2
608 # By now, even with full blanks allowed, a second blank should signal
609 # By now, even with full blanks allowed, a second blank should signal
609 # the end. For now this test is only a redundancy safety, but don't
610 # the end. For now this test is only a redundancy safety, but don't
610 # delete it in case we change our mind and the previous one goes to
611 # delete it in case we change our mind and the previous one goes to
611 # true.
612 # true.
612 src += '\n'
613 src += '\n'
613 sp.push(src)
614 sp.push(src)
614 nt.assert_false(sp.push_accepts_more()) #3
615 nt.assert_false(sp.push_accepts_more()) #3
615
616
616
617
617 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
618 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
618 sp = isp.IPythonInputSplitter(input_mode='line')
619 sp = isp.IPythonInputSplitter(input_mode='line')
619
620
620 def test_incremental(self):
621 def test_incremental(self):
621 sp = self.sp
622 sp = self.sp
622 sp.push('%%cellm line2\n')
623 sp.push('%%cellm line2\n')
623 nt.assert_true(sp.push_accepts_more()) #1
624 nt.assert_true(sp.push_accepts_more()) #1
624 sp.push('\n')
625 sp.push('\n')
625 nt.assert_false(sp.push_accepts_more()) #2
626 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,368 +1,368 b''
1 import tokenize
1 import tokenize
2 import unittest
2 import unittest
3 import nose.tools as nt
3 import nose.tools as nt
4
4
5 from IPython.testing import tools as tt
5 from IPython.testing import tools as tt
6 from IPython.utils import py3compat
6 from IPython.utils import py3compat
7 u_fmt = py3compat.u_format
7 u_fmt = py3compat.u_format
8
8
9 from IPython.core import inputtransformer as ipt
9 from IPython.core import inputtransformer as ipt
10
10
11 def transform_and_reset(transformer):
11 def transform_and_reset(transformer):
12 transformer = transformer()
12 transformer = transformer()
13 def transform(inp):
13 def transform(inp):
14 try:
14 try:
15 return transformer.push(inp)
15 return transformer.push(inp)
16 finally:
16 finally:
17 transformer.reset()
17 transformer.reset()
18
18
19 return transform
19 return transform
20
20
21 # Transformer tests
21 # Transformer tests
22 def transform_checker(tests, transformer):
22 def transform_checker(tests, transformer):
23 """Utility to loop over test inputs"""
23 """Utility to loop over test inputs"""
24 transformer = transformer()
24 transformer = transformer()
25 try:
25 try:
26 for inp, tr in tests:
26 for inp, tr in tests:
27 if inp is None:
27 if inp is None:
28 out = transformer.reset()
28 out = transformer.reset()
29 else:
29 else:
30 out = transformer.push(inp)
30 out = transformer.push(inp)
31 nt.assert_equal(out, tr)
31 nt.assert_equal(out, tr)
32 finally:
32 finally:
33 transformer.reset()
33 transformer.reset()
34
34
35 # Data for all the syntax tests in the form of lists of pairs of
35 # Data for all the syntax tests in the form of lists of pairs of
36 # raw/transformed input. We store it here as a global dict so that we can use
36 # raw/transformed input. We store it here as a global dict so that we can use
37 # it both within single-function tests and also to validate the behavior of the
37 # it both within single-function tests and also to validate the behavior of the
38 # larger objects
38 # larger objects
39
39
40 syntax = \
40 syntax = \
41 dict(assign_system =
41 dict(assign_system =
42 [(i,py3compat.u_format(o)) for i,o in \
42 [(i,py3compat.u_format(o)) for i,o in \
43 [(u'a =! ls', "a = get_ipython().getoutput({u}'ls')"),
43 [(u'a =! ls', "a = get_ipython().getoutput({u}'ls')"),
44 (u'b = !ls', "b = get_ipython().getoutput({u}'ls')"),
44 (u'b = !ls', "b = get_ipython().getoutput({u}'ls')"),
45 ('x=1', 'x=1'), # normal input is unmodified
45 ('x=1', 'x=1'), # normal input is unmodified
46 (' ',' '), # blank lines are kept intact
46 (' ',' '), # blank lines are kept intact
47 ]],
47 ]],
48
48
49 assign_magic =
49 assign_magic =
50 [(i,py3compat.u_format(o)) for i,o in \
50 [(i,py3compat.u_format(o)) for i,o in \
51 [(u'a =% who', "a = get_ipython().magic({u}'who')"),
51 [(u'a =% who', "a = get_ipython().magic({u}'who')"),
52 (u'b = %who', "b = get_ipython().magic({u}'who')"),
52 (u'b = %who', "b = get_ipython().magic({u}'who')"),
53 ('x=1', 'x=1'), # normal input is unmodified
53 ('x=1', 'x=1'), # normal input is unmodified
54 (' ',' '), # blank lines are kept intact
54 (' ',' '), # blank lines are kept intact
55 ]],
55 ]],
56
56
57 classic_prompt =
57 classic_prompt =
58 [('>>> x=1', 'x=1'),
58 [('>>> x=1', 'x=1'),
59 ('x=1', 'x=1'), # normal input is unmodified
59 ('x=1', 'x=1'), # normal input is unmodified
60 (' ', ' '), # blank lines are kept intact
60 (' ', ' '), # blank lines are kept intact
61 ],
61 ],
62
62
63 ipy_prompt =
63 ipy_prompt =
64 [('In [1]: x=1', 'x=1'),
64 [('In [1]: x=1', 'x=1'),
65 ('x=1', 'x=1'), # normal input is unmodified
65 ('x=1', 'x=1'), # normal input is unmodified
66 (' ',' '), # blank lines are kept intact
66 (' ',' '), # blank lines are kept intact
67 ],
67 ],
68
68
69 # Tests for the escape transformer to leave normal code alone
69 # Tests for the escape transformer to leave normal code alone
70 escaped_noesc =
70 escaped_noesc =
71 [ (' ', ' '),
71 [ (' ', ' '),
72 ('x=1', 'x=1'),
72 ('x=1', 'x=1'),
73 ],
73 ],
74
74
75 # System calls
75 # System calls
76 escaped_shell =
76 escaped_shell =
77 [(i,py3compat.u_format(o)) for i,o in \
77 [(i,py3compat.u_format(o)) for i,o in \
78 [ (u'!ls', "get_ipython().system({u}'ls')"),
78 [ (u'!ls', "get_ipython().system({u}'ls')"),
79 # Double-escape shell, this means to capture the output of the
79 # Double-escape shell, this means to capture the output of the
80 # subprocess and return it
80 # subprocess and return it
81 (u'!!ls', "get_ipython().getoutput({u}'ls')"),
81 (u'!!ls', "get_ipython().getoutput({u}'ls')"),
82 ]],
82 ]],
83
83
84 # Help/object info
84 # Help/object info
85 escaped_help =
85 escaped_help =
86 [(i,py3compat.u_format(o)) for i,o in \
86 [(i,py3compat.u_format(o)) for i,o in \
87 [ (u'?', 'get_ipython().show_usage()'),
87 [ (u'?', 'get_ipython().show_usage()'),
88 (u'?x1', "get_ipython().magic({u}'pinfo x1')"),
88 (u'?x1', "get_ipython().magic({u}'pinfo x1')"),
89 (u'??x2', "get_ipython().magic({u}'pinfo2 x2')"),
89 (u'??x2', "get_ipython().magic({u}'pinfo2 x2')"),
90 (u'?a.*s', "get_ipython().magic({u}'psearch a.*s')"),
90 (u'?a.*s', "get_ipython().magic({u}'psearch a.*s')"),
91 (u'?%hist1', "get_ipython().magic({u}'pinfo %hist1')"),
91 (u'?%hist1', "get_ipython().magic({u}'pinfo %hist1')"),
92 (u'?%%hist2', "get_ipython().magic({u}'pinfo %%hist2')"),
92 (u'?%%hist2', "get_ipython().magic({u}'pinfo %%hist2')"),
93 (u'?abc = qwe', "get_ipython().magic({u}'pinfo abc')"),
93 (u'?abc = qwe', "get_ipython().magic({u}'pinfo abc')"),
94 ]],
94 ]],
95
95
96 end_help =
96 end_help =
97 [(i,py3compat.u_format(o)) for i,o in \
97 [(i,py3compat.u_format(o)) for i,o in \
98 [ (u'x3?', "get_ipython().magic({u}'pinfo x3')"),
98 [ (u'x3?', "get_ipython().magic({u}'pinfo x3')"),
99 (u'x4??', "get_ipython().magic({u}'pinfo2 x4')"),
99 (u'x4??', "get_ipython().magic({u}'pinfo2 x4')"),
100 (u'%hist1?', "get_ipython().magic({u}'pinfo %hist1')"),
100 (u'%hist1?', "get_ipython().magic({u}'pinfo %hist1')"),
101 (u'%hist2??', "get_ipython().magic({u}'pinfo2 %hist2')"),
101 (u'%hist2??', "get_ipython().magic({u}'pinfo2 %hist2')"),
102 (u'%%hist3?', "get_ipython().magic({u}'pinfo %%hist3')"),
102 (u'%%hist3?', "get_ipython().magic({u}'pinfo %%hist3')"),
103 (u'%%hist4??', "get_ipython().magic({u}'pinfo2 %%hist4')"),
103 (u'%%hist4??', "get_ipython().magic({u}'pinfo2 %%hist4')"),
104 (u'f*?', "get_ipython().magic({u}'psearch f*')"),
104 (u'f*?', "get_ipython().magic({u}'psearch f*')"),
105 (u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"),
105 (u'ax.*aspe*?', "get_ipython().magic({u}'psearch ax.*aspe*')"),
106 (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');"
106 (u'a = abc?', "get_ipython().set_next_input({u}'a = abc');"
107 "get_ipython().magic({u}'pinfo abc')"),
107 "get_ipython().magic({u}'pinfo abc')"),
108 (u'a = abc.qe??', "get_ipython().set_next_input({u}'a = abc.qe');"
108 (u'a = abc.qe??', "get_ipython().set_next_input({u}'a = abc.qe');"
109 "get_ipython().magic({u}'pinfo2 abc.qe')"),
109 "get_ipython().magic({u}'pinfo2 abc.qe')"),
110 (u'a = *.items?', "get_ipython().set_next_input({u}'a = *.items');"
110 (u'a = *.items?', "get_ipython().set_next_input({u}'a = *.items');"
111 "get_ipython().magic({u}'psearch *.items')"),
111 "get_ipython().magic({u}'psearch *.items')"),
112 (u'plot(a?', "get_ipython().set_next_input({u}'plot(a');"
112 (u'plot(a?', "get_ipython().set_next_input({u}'plot(a');"
113 "get_ipython().magic({u}'pinfo a')"),
113 "get_ipython().magic({u}'pinfo a')"),
114 (u'a*2 #comment?', 'a*2 #comment?'),
114 (u'a*2 #comment?', 'a*2 #comment?'),
115 ]],
115 ]],
116
116
117 # Explicit magic calls
117 # Explicit magic calls
118 escaped_magic =
118 escaped_magic =
119 [(i,py3compat.u_format(o)) for i,o in \
119 [(i,py3compat.u_format(o)) for i,o in \
120 [ (u'%cd', "get_ipython().magic({u}'cd')"),
120 [ (u'%cd', "get_ipython().magic({u}'cd')"),
121 (u'%cd /home', "get_ipython().magic({u}'cd /home')"),
121 (u'%cd /home', "get_ipython().magic({u}'cd /home')"),
122 # Backslashes need to be escaped.
122 # Backslashes need to be escaped.
123 (u'%cd C:\\User', "get_ipython().magic({u}'cd C:\\\\User')"),
123 (u'%cd C:\\User', "get_ipython().magic({u}'cd C:\\\\User')"),
124 (u' %magic', " get_ipython().magic({u}'magic')"),
124 (u' %magic', " get_ipython().magic({u}'magic')"),
125 ]],
125 ]],
126
126
127 # Quoting with separate arguments
127 # Quoting with separate arguments
128 escaped_quote =
128 escaped_quote =
129 [ (',f', 'f("")'),
129 [ (',f', 'f("")'),
130 (',f x', 'f("x")'),
130 (',f x', 'f("x")'),
131 (' ,f y', ' f("y")'),
131 (' ,f y', ' f("y")'),
132 (',f a b', 'f("a", "b")'),
132 (',f a b', 'f("a", "b")'),
133 ],
133 ],
134
134
135 # Quoting with single argument
135 # Quoting with single argument
136 escaped_quote2 =
136 escaped_quote2 =
137 [ (';f', 'f("")'),
137 [ (';f', 'f("")'),
138 (';f x', 'f("x")'),
138 (';f x', 'f("x")'),
139 (' ;f y', ' f("y")'),
139 (' ;f y', ' f("y")'),
140 (';f a b', 'f("a b")'),
140 (';f a b', 'f("a b")'),
141 ],
141 ],
142
142
143 # Simply apply parens
143 # Simply apply parens
144 escaped_paren =
144 escaped_paren =
145 [ ('/f', 'f()'),
145 [ ('/f', 'f()'),
146 ('/f x', 'f(x)'),
146 ('/f x', 'f(x)'),
147 (' /f y', ' f(y)'),
147 (' /f y', ' f(y)'),
148 ('/f a b', 'f(a, b)'),
148 ('/f a b', 'f(a, b)'),
149 ],
149 ],
150
150
151 # Check that we transform prompts before other transforms
151 # Check that we transform prompts before other transforms
152 mixed =
152 mixed =
153 [(i,py3compat.u_format(o)) for i,o in \
153 [(i,py3compat.u_format(o)) for i,o in \
154 [ (u'In [1]: %lsmagic', "get_ipython().magic({u}'lsmagic')"),
154 [ (u'In [1]: %lsmagic', "get_ipython().magic({u}'lsmagic')"),
155 (u'>>> %lsmagic', "get_ipython().magic({u}'lsmagic')"),
155 (u'>>> %lsmagic', "get_ipython().magic({u}'lsmagic')"),
156 (u'In [2]: !ls', "get_ipython().system({u}'ls')"),
156 (u'In [2]: !ls', "get_ipython().system({u}'ls')"),
157 (u'In [3]: abs?', "get_ipython().magic({u}'pinfo abs')"),
157 (u'In [3]: abs?', "get_ipython().magic({u}'pinfo abs')"),
158 (u'In [4]: b = %who', "b = get_ipython().magic({u}'who')"),
158 (u'In [4]: b = %who', "b = get_ipython().magic({u}'who')"),
159 ]],
159 ]],
160 )
160 )
161
161
162 # multiline syntax examples. Each of these should be a list of lists, with
162 # multiline syntax examples. Each of these should be a list of lists, with
163 # each entry itself having pairs of raw/transformed input. The union (with
163 # each entry itself having pairs of raw/transformed input. The union (with
164 # '\n'.join() of the transformed inputs is what the splitter should produce
164 # '\n'.join() of the transformed inputs is what the splitter should produce
165 # when fed the raw lines one at a time via push.
165 # when fed the raw lines one at a time via push.
166 syntax_ml = \
166 syntax_ml = \
167 dict(classic_prompt =
167 dict(classic_prompt =
168 [ [('>>> for i in range(10):','for i in range(10):'),
168 [ [('>>> for i in range(10):','for i in range(10):'),
169 ('... print i',' print i'),
169 ('... print i',' print i'),
170 ('... ', ''),
170 ('... ', ''),
171 ],
171 ],
172 [('>>> a="""','a="""'),
172 [('>>> a="""','a="""'),
173 ('... 123"""','123"""'),
173 ('... 123"""','123"""'),
174 ],
174 ],
175 [('a="""','a="""'),
175 [('a="""','a="""'),
176 ('... 123"""','... 123"""'),
176 ('... 123"""','... 123"""'),
177 ],
177 ],
178 ],
178 ],
179
179
180 ipy_prompt =
180 ipy_prompt =
181 [ [('In [24]: for i in range(10):','for i in range(10):'),
181 [ [('In [24]: for i in range(10):','for i in range(10):'),
182 (' ....: print i',' print i'),
182 (' ....: print i',' print i'),
183 (' ....: ', ''),
183 (' ....: ', ''),
184 ],
184 ],
185 [('In [2]: a="""','a="""'),
185 [('In [2]: a="""','a="""'),
186 (' ...: 123"""','123"""'),
186 (' ...: 123"""','123"""'),
187 ],
187 ],
188 [('a="""','a="""'),
188 [('a="""','a="""'),
189 (' ...: 123"""',' ...: 123"""'),
189 (' ...: 123"""',' ...: 123"""'),
190 ],
190 ],
191 ],
191 ],
192
192
193 multiline_datastructure =
193 multiline_datastructure =
194 [ [('>>> a = [1,','a = [1,'),
194 [ [('>>> a = [1,','a = [1,'),
195 ('... 2]','2]'),
195 ('... 2]','2]'),
196 ],
196 ],
197 ],
197 ],
198
198
199 leading_indent =
199 leading_indent =
200 [ [(' print "hi"','print "hi"'),
200 [ [(' print "hi"','print "hi"'),
201 ],
201 ],
202 [(' for a in range(5):','for a in range(5):'),
202 [(' for a in range(5):','for a in range(5):'),
203 (' a*2',' a*2'),
203 (' a*2',' a*2'),
204 ],
204 ],
205 [(' a="""','a="""'),
205 [(' a="""','a="""'),
206 (' 123"""','123"""'),
206 (' 123"""','123"""'),
207 ],
207 ],
208 [('a="""','a="""'),
208 [('a="""','a="""'),
209 (' 123"""',' 123"""'),
209 (' 123"""',' 123"""'),
210 ],
210 ],
211 ],
211 ],
212
212
213 cellmagic =
213 cellmagic =
214 [ [(u'%%foo a', None),
214 [ [(u'%%foo a', None),
215 (None, u_fmt("get_ipython().run_cell_magic({u}'foo', {u}'a', {u}'')")),
215 (None, u_fmt("get_ipython().run_cell_magic({u}'foo', {u}'a', {u}'')")),
216 ],
216 ],
217 [(u'%%bar 123', None),
217 [(u'%%bar 123', None),
218 (u'hello', None),
218 (u'hello', None),
219 (u'', u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")),
219 (u'', u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")),
220 ],
220 ],
221 ],
221 ],
222
222
223 escaped =
223 escaped =
224 [ [('%abc def \\', None),
224 [ [('%abc def \\', None),
225 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
225 ('ghi', u_fmt("get_ipython().magic({u}'abc def ghi')")),
226 ],
226 ],
227 [('%abc def \\', None),
227 [('%abc def \\', None),
228 ('ghi\\', None),
228 ('ghi\\', None),
229 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
229 (None, u_fmt("get_ipython().magic({u}'abc def ghi')")),
230 ],
230 ],
231 ],
231 ],
232
232
233 assign_magic =
233 assign_magic =
234 [ [(u'a = %bc de \\', None),
234 [ [(u'a = %bc de \\', None),
235 (u'fg', u_fmt("a = get_ipython().magic({u}'bc de fg')")),
235 (u'fg', u_fmt("a = get_ipython().magic({u}'bc de fg')")),
236 ],
236 ],
237 [(u'a = %bc de \\', None),
237 [(u'a = %bc de \\', None),
238 (u'fg\\', None),
238 (u'fg\\', None),
239 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
239 (None, u_fmt("a = get_ipython().magic({u}'bc de fg')")),
240 ],
240 ],
241 ],
241 ],
242
242
243 assign_system =
243 assign_system =
244 [ [(u'a = !bc de \\', None),
244 [ [(u'a = !bc de \\', None),
245 (u'fg', u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
245 (u'fg', u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
246 ],
246 ],
247 [(u'a = !bc de \\', None),
247 [(u'a = !bc de \\', None),
248 (u'fg\\', None),
248 (u'fg\\', None),
249 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
249 (None, u_fmt("a = get_ipython().getoutput({u}'bc de fg')")),
250 ],
250 ],
251 ],
251 ],
252 )
252 )
253
253
254
254
255 def test_assign_system():
255 def test_assign_system():
256 tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system'])
256 tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system'])
257 for example in syntax_ml['assign_system']:
257 for example in syntax_ml['assign_system']:
258 transform_checker(example, ipt.assign_from_system)
258 transform_checker(example, ipt.assign_from_system)
259
259
260 def test_assign_magic():
260 def test_assign_magic():
261 tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic'])
261 tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic'])
262 for example in syntax_ml['assign_magic']:
262 for example in syntax_ml['assign_magic']:
263 transform_checker(example, ipt.assign_from_magic)
263 transform_checker(example, ipt.assign_from_magic)
264
264
265
265
266 def test_classic_prompt():
266 def test_classic_prompt():
267 tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt'])
267 tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt'])
268 for example in syntax_ml['classic_prompt']:
268 for example in syntax_ml['classic_prompt']:
269 transform_checker(example, ipt.classic_prompt)
269 transform_checker(example, ipt.classic_prompt)
270 for example in syntax_ml['multiline_datastructure']:
270 for example in syntax_ml['multiline_datastructure']:
271 transform_checker(example, ipt.classic_prompt)
271 transform_checker(example, ipt.classic_prompt)
272
272
273
273
274 def test_ipy_prompt():
274 def test_ipy_prompt():
275 tt.check_pairs(transform_and_reset(ipt.ipy_prompt), syntax['ipy_prompt'])
275 tt.check_pairs(transform_and_reset(ipt.ipy_prompt), syntax['ipy_prompt'])
276 for example in syntax_ml['ipy_prompt']:
276 for example in syntax_ml['ipy_prompt']:
277 transform_checker(example, ipt.ipy_prompt)
277 transform_checker(example, ipt.ipy_prompt)
278
278
279 def test_help_end():
279 def test_help_end():
280 tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help'])
280 tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help'])
281
281
282 def test_escaped_noesc():
282 def test_escaped_noesc():
283 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_noesc'])
283 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_noesc'])
284
284
285
285
286 def test_escaped_shell():
286 def test_escaped_shell():
287 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_shell'])
287 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_shell'])
288
288
289
289
290 def test_escaped_help():
290 def test_escaped_help():
291 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_help'])
291 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_help'])
292
292
293
293
294 def test_escaped_magic():
294 def test_escaped_magic():
295 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_magic'])
295 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_magic'])
296
296
297
297
298 def test_escaped_quote():
298 def test_escaped_quote():
299 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_quote'])
299 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_quote'])
300
300
301
301
302 def test_escaped_quote2():
302 def test_escaped_quote2():
303 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_quote2'])
303 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_quote2'])
304
304
305
305
306 def test_escaped_paren():
306 def test_escaped_paren():
307 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_paren'])
307 tt.check_pairs(transform_and_reset(ipt.escaped_transformer), syntax['escaped_paren'])
308
308
309 def test_escaped_multiline():
309 def test_escaped_multiline():
310 for example in syntax_ml['escaped']:
310 for example in syntax_ml['escaped']:
311 transform_checker(example, ipt.escaped_transformer)
311 transform_checker(example, ipt.escaped_transformer)
312
312
313 def test_cellmagic():
313 def test_cellmagic():
314 for example in syntax_ml['cellmagic']:
314 for example in syntax_ml['cellmagic']:
315 transform_checker(example, ipt.cellmagic)
315 transform_checker(example, ipt.cellmagic)
316
316
317 def test_has_comment():
317 def test_has_comment():
318 tests = [('text', False),
318 tests = [('text', False),
319 ('text #comment', True),
319 ('text #comment', True),
320 ('text #comment\n', True),
320 ('text #comment\n', True),
321 ('#comment', True),
321 ('#comment', True),
322 ('#comment\n', True),
322 ('#comment\n', True),
323 ('a = "#string"', False),
323 ('a = "#string"', False),
324 ('a = "#string" # comment', True),
324 ('a = "#string" # comment', True),
325 ('a #comment not "string"', True),
325 ('a #comment not "string"', True),
326 ]
326 ]
327 tt.check_pairs(ipt.has_comment, tests)
327 tt.check_pairs(ipt.has_comment, tests)
328
328
329 @ipt.TokenInputTransformer.wrap
329 @ipt.TokenInputTransformer.wrap
330 def decistmt(tokens):
330 def decistmt(tokens):
331 """Substitute Decimals for floats in a string of statements.
331 """Substitute Decimals for floats in a string of statements.
332
332
333 Based on an example from the tokenize module docs.
333 Based on an example from the tokenize module docs.
334 """
334 """
335 result = []
335 result = []
336 for toknum, tokval, _, _, _ in tokens:
336 for toknum, tokval, _, _, _ in tokens:
337 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
337 if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens
338 for newtok in [
338 for newtok in [
339 (tokenize.NAME, 'Decimal'),
339 (tokenize.NAME, 'Decimal'),
340 (tokenize.OP, '('),
340 (tokenize.OP, '('),
341 (tokenize.STRING, repr(tokval)),
341 (tokenize.STRING, repr(tokval)),
342 (tokenize.OP, ')')
342 (tokenize.OP, ')')
343 ]:
343 ]:
344 yield newtok
344 yield newtok
345 else:
345 else:
346 yield (toknum, tokval)
346 yield (toknum, tokval)
347
347
348
348
349
349
350 def test_token_input_transformer():
350 def test_token_input_transformer():
351 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
351 tests = [(u'1.2', u_fmt(u"Decimal ({u}'1.2')")),
352 (u'"1.2"', u'"1.2"'),
352 (u'"1.2"', u'"1.2"'),
353 ]
353 ]
354 tt.check_pairs(transform_and_reset(decistmt), tests)
354 tt.check_pairs(transform_and_reset(decistmt), tests)
355 ml_tests = \
355 ml_tests = \
356 [ [(u"a = 1.2; b = '''x", None),
356 [ [(u"a = 1.2; b = '''x", None),
357 (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")),
357 (u"y'''", u_fmt(u"a =Decimal ({u}'1.2');b ='''x\ny'''")),
358 ],
358 ],
359 [(u"a = [1.2,", u_fmt(u"a =[Decimal ({u}'1.2'),")),
359 [(u"a = [1.2,", None),
360 (u"3]", u"3 ]"),
360 (u"3]", u_fmt(u"a =[Decimal ({u}'1.2'),\n3 ]")),
361 ],
361 ],
362 [(u"a = '''foo", None), # Test resetting when within a multi-line string
362 [(u"a = '''foo", None), # Test resetting when within a multi-line string
363 (u"bar", None),
363 (u"bar", None),
364 (None, u"a = '''foo\nbar"),
364 (None, u"a = '''foo\nbar"),
365 ],
365 ],
366 ]
366 ]
367 for example in ml_tests:
367 for example in ml_tests:
368 transform_checker(example, decistmt)
368 transform_checker(example, decistmt)
General Comments 0
You need to be logged in to leave comments. Login now