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