##// END OF EJS Templates
Fix bug with lines ending in continuation markers (\)....
Fernando Perez -
Show More
@@ -1,977 +1,981 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 from __future__ import print_function
63 from __future__ import print_function
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Imports
66 # Imports
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68 # stdlib
68 # stdlib
69 import codeop
69 import codeop
70 import re
70 import re
71 import sys
71 import sys
72
72
73 # IPython modules
73 # IPython modules
74 from IPython.utils.text import make_quoted_expr
74 from IPython.utils.text import make_quoted_expr
75
75
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77 # Globals
77 # Globals
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79
79
80 # The escape sequences that define the syntax transformations IPython will
80 # The escape sequences that define the syntax transformations IPython will
81 # apply to user input. These can NOT be just changed here: many regular
81 # apply to user input. These can NOT be just changed here: many regular
82 # expressions and other parts of the code may use their hardcoded values, and
82 # expressions and other parts of the code may use their hardcoded values, and
83 # for all intents and purposes they constitute the 'IPython syntax', so they
83 # for all intents and purposes they constitute the 'IPython syntax', so they
84 # should be considered fixed.
84 # should be considered fixed.
85
85
86 ESC_SHELL = '!' # Send line to underlying system shell
86 ESC_SHELL = '!' # Send line to underlying system shell
87 ESC_SH_CAP = '!!' # Send line to system shell and capture output
87 ESC_SH_CAP = '!!' # Send line to system shell and capture output
88 ESC_HELP = '?' # Find information about object
88 ESC_HELP = '?' # Find information about object
89 ESC_HELP2 = '??' # Find extra-detailed information about object
89 ESC_HELP2 = '??' # Find extra-detailed information about object
90 ESC_MAGIC = '%' # Call magic function
90 ESC_MAGIC = '%' # Call magic function
91 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
91 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
92 ESC_QUOTE2 = ';' # Quote all args as a single string, call
92 ESC_QUOTE2 = ';' # Quote all args as a single string, call
93 ESC_PAREN = '/' # Call first argument with rest of line as arguments
93 ESC_PAREN = '/' # Call first argument with rest of line as arguments
94
94
95 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
96 # Utilities
96 # Utilities
97 #-----------------------------------------------------------------------------
97 #-----------------------------------------------------------------------------
98
98
99 # FIXME: These are general-purpose utilities that later can be moved to the
99 # FIXME: These are general-purpose utilities that later can be moved to the
100 # general ward. Kept here for now because we're being very strict about test
100 # general ward. Kept here for now because we're being very strict about test
101 # coverage with this code, and this lets us ensure that we keep 100% coverage
101 # coverage with this code, and this lets us ensure that we keep 100% coverage
102 # while developing.
102 # while developing.
103
103
104 # compiled regexps for autoindent management
104 # compiled regexps for autoindent management
105 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
105 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
106 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
106 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
107
107
108 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
108 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
109 # before pure comments
109 # before pure comments
110 comment_line_re = re.compile('^\s*\#')
110 comment_line_re = re.compile('^\s*\#')
111
111
112
112
113 def num_ini_spaces(s):
113 def num_ini_spaces(s):
114 """Return the number of initial spaces in a string.
114 """Return the number of initial spaces in a string.
115
115
116 Note that tabs are counted as a single space. For now, we do *not* support
116 Note that tabs are counted as a single space. For now, we do *not* support
117 mixing of tabs and spaces in the user's input.
117 mixing of tabs and spaces in the user's input.
118
118
119 Parameters
119 Parameters
120 ----------
120 ----------
121 s : string
121 s : string
122
122
123 Returns
123 Returns
124 -------
124 -------
125 n : int
125 n : int
126 """
126 """
127
127
128 ini_spaces = ini_spaces_re.match(s)
128 ini_spaces = ini_spaces_re.match(s)
129 if ini_spaces:
129 if ini_spaces:
130 return ini_spaces.end()
130 return ini_spaces.end()
131 else:
131 else:
132 return 0
132 return 0
133
133
134
134
135 def remove_comments(src):
135 def remove_comments(src):
136 """Remove all comments from input source.
136 """Remove all comments from input source.
137
137
138 Note: comments are NOT recognized inside of strings!
138 Note: comments are NOT recognized inside of strings!
139
139
140 Parameters
140 Parameters
141 ----------
141 ----------
142 src : string
142 src : string
143 A single or multiline input string.
143 A single or multiline input string.
144
144
145 Returns
145 Returns
146 -------
146 -------
147 String with all Python comments removed.
147 String with all Python comments removed.
148 """
148 """
149
149
150 return re.sub('#.*', '', src)
150 return re.sub('#.*', '', src)
151
151
152
152
153 def get_input_encoding():
153 def get_input_encoding():
154 """Return the default standard input encoding.
154 """Return the default standard input encoding.
155
155
156 If sys.stdin has no encoding, 'ascii' is returned."""
156 If sys.stdin has no encoding, 'ascii' is returned."""
157 # There are strange environments for which sys.stdin.encoding is None. We
157 # There are strange environments for which sys.stdin.encoding is None. We
158 # ensure that a valid encoding is returned.
158 # ensure that a valid encoding is returned.
159 encoding = getattr(sys.stdin, 'encoding', None)
159 encoding = getattr(sys.stdin, 'encoding', None)
160 if encoding is None:
160 if encoding is None:
161 encoding = 'ascii'
161 encoding = 'ascii'
162 return encoding
162 return encoding
163
163
164 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
165 # Classes and functions for normal Python syntax handling
165 # Classes and functions for normal Python syntax handling
166 #-----------------------------------------------------------------------------
166 #-----------------------------------------------------------------------------
167
167
168 # HACK! This implementation, written by Robert K a while ago using the
168 # HACK! This implementation, written by Robert K a while ago using the
169 # compiler module, is more robust than the other one below, but it expects its
169 # compiler module, is more robust than the other one below, but it expects its
170 # input to be pure python (no ipython syntax). For now we're using it as a
170 # input to be pure python (no ipython syntax). For now we're using it as a
171 # second-pass splitter after the first pass transforms the input to pure
171 # second-pass splitter after the first pass transforms the input to pure
172 # python.
172 # python.
173
173
174 def split_blocks(python):
174 def split_blocks(python):
175 """ Split multiple lines of code into discrete commands that can be
175 """ Split multiple lines of code into discrete commands that can be
176 executed singly.
176 executed singly.
177
177
178 Parameters
178 Parameters
179 ----------
179 ----------
180 python : str
180 python : str
181 Pure, exec'able Python code.
181 Pure, exec'able Python code.
182
182
183 Returns
183 Returns
184 -------
184 -------
185 commands : list of str
185 commands : list of str
186 Separate commands that can be exec'ed independently.
186 Separate commands that can be exec'ed independently.
187 """
187 """
188
188
189 import compiler
189 import compiler
190
190
191 # compiler.parse treats trailing spaces after a newline as a
191 # compiler.parse treats trailing spaces after a newline as a
192 # SyntaxError. This is different than codeop.CommandCompiler, which
192 # SyntaxError. This is different than codeop.CommandCompiler, which
193 # will compile the trailng spaces just fine. We simply strip any
193 # will compile the trailng spaces just fine. We simply strip any
194 # trailing whitespace off. Passing a string with trailing whitespace
194 # trailing whitespace off. Passing a string with trailing whitespace
195 # to exec will fail however. There seems to be some inconsistency in
195 # to exec will fail however. There seems to be some inconsistency in
196 # how trailing whitespace is handled, but this seems to work.
196 # how trailing whitespace is handled, but this seems to work.
197 python_ori = python # save original in case we bail on error
197 python_ori = python # save original in case we bail on error
198 python = python.strip()
198 python = python.strip()
199
199
200 # The compiler module does not like unicode. We need to convert
200 # The compiler module does not like unicode. We need to convert
201 # it encode it:
201 # it encode it:
202 if isinstance(python, unicode):
202 if isinstance(python, unicode):
203 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
203 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
204 # encode string.
204 # encode string.
205 python = '\xef\xbb\xbf' + python.encode('utf-8')
205 python = '\xef\xbb\xbf' + python.encode('utf-8')
206
206
207 # The compiler module will parse the code into an abstract syntax tree.
207 # The compiler module will parse the code into an abstract syntax tree.
208 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
208 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
209 try:
209 try:
210 ast = compiler.parse(python)
210 ast = compiler.parse(python)
211 except:
211 except:
212 return [python_ori]
212 return [python_ori]
213
213
214 # Uncomment to help debug the ast tree
214 # Uncomment to help debug the ast tree
215 # for n in ast.node:
215 # for n in ast.node:
216 # print n.lineno,'->',n
216 # print n.lineno,'->',n
217
217
218 # Each separate command is available by iterating over ast.node. The
218 # Each separate command is available by iterating over ast.node. The
219 # lineno attribute is the line number (1-indexed) beginning the commands
219 # lineno attribute is the line number (1-indexed) beginning the commands
220 # suite.
220 # suite.
221 # lines ending with ";" yield a Discard Node that doesn't have a lineno
221 # lines ending with ";" yield a Discard Node that doesn't have a lineno
222 # attribute. These nodes can and should be discarded. But there are
222 # attribute. These nodes can and should be discarded. But there are
223 # other situations that cause Discard nodes that shouldn't be discarded.
223 # other situations that cause Discard nodes that shouldn't be discarded.
224 # We might eventually discover other cases where lineno is None and have
224 # We might eventually discover other cases where lineno is None and have
225 # to put in a more sophisticated test.
225 # to put in a more sophisticated test.
226 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
226 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
227
227
228 # When we finally get the slices, we will need to slice all the way to
228 # When we finally get the slices, we will need to slice all the way to
229 # the end even though we don't have a line number for it. Fortunately,
229 # the end even though we don't have a line number for it. Fortunately,
230 # None does the job nicely.
230 # None does the job nicely.
231 linenos.append(None)
231 linenos.append(None)
232
232
233 # Same problem at the other end: sometimes the ast tree has its
233 # Same problem at the other end: sometimes the ast tree has its
234 # first complete statement not starting on line 0. In this case
234 # first complete statement not starting on line 0. In this case
235 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
235 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
236 linenos[0] = 0
236 linenos[0] = 0
237
237
238 lines = python.splitlines()
238 lines = python.splitlines()
239
239
240 # Create a list of atomic commands.
240 # Create a list of atomic commands.
241 cmds = []
241 cmds = []
242 for i, j in zip(linenos[:-1], linenos[1:]):
242 for i, j in zip(linenos[:-1], linenos[1:]):
243 cmd = lines[i:j]
243 cmd = lines[i:j]
244 if cmd:
244 if cmd:
245 cmds.append('\n'.join(cmd)+'\n')
245 cmds.append('\n'.join(cmd)+'\n')
246
246
247 return cmds
247 return cmds
248
248
249
249
250 class InputSplitter(object):
250 class InputSplitter(object):
251 """An object that can split Python source input in executable blocks.
251 """An object that can split Python source input in executable blocks.
252
252
253 This object is designed to be used in one of two basic modes:
253 This object is designed to be used in one of two basic modes:
254
254
255 1. By feeding it python source line-by-line, using :meth:`push`. In this
255 1. By feeding it python source line-by-line, using :meth:`push`. In this
256 mode, it will return on each push whether the currently pushed code
256 mode, it will return on each push whether the currently pushed code
257 could be executed already. In addition, it provides a method called
257 could be executed already. In addition, it provides a method called
258 :meth:`push_accepts_more` that can be used to query whether more input
258 :meth:`push_accepts_more` that can be used to query whether more input
259 can be pushed into a single interactive block.
259 can be pushed into a single interactive block.
260
260
261 2. By calling :meth:`split_blocks` with a single, multiline Python string,
261 2. By calling :meth:`split_blocks` with a single, multiline Python string,
262 that is then split into blocks each of which can be executed
262 that is then split into blocks each of which can be executed
263 interactively as a single statement.
263 interactively as a single statement.
264
264
265 This is a simple example of how an interactive terminal-based client can use
265 This is a simple example of how an interactive terminal-based client can use
266 this tool::
266 this tool::
267
267
268 isp = InputSplitter()
268 isp = InputSplitter()
269 while isp.push_accepts_more():
269 while isp.push_accepts_more():
270 indent = ' '*isp.indent_spaces
270 indent = ' '*isp.indent_spaces
271 prompt = '>>> ' + indent
271 prompt = '>>> ' + indent
272 line = indent + raw_input(prompt)
272 line = indent + raw_input(prompt)
273 isp.push(line)
273 isp.push(line)
274 print 'Input source was:\n', isp.source_reset(),
274 print 'Input source was:\n', isp.source_reset(),
275 """
275 """
276 # Number of spaces of indentation computed from input that has been pushed
276 # Number of spaces of indentation computed from input that has been pushed
277 # so far. This is the attributes callers should query to get the current
277 # so far. This is the attributes callers should query to get the current
278 # indentation level, in order to provide auto-indent facilities.
278 # indentation level, in order to provide auto-indent facilities.
279 indent_spaces = 0
279 indent_spaces = 0
280 # String, indicating the default input encoding. It is computed by default
280 # String, indicating the default input encoding. It is computed by default
281 # at initialization time via get_input_encoding(), but it can be reset by a
281 # at initialization time via get_input_encoding(), but it can be reset by a
282 # client with specific knowledge of the encoding.
282 # client with specific knowledge of the encoding.
283 encoding = ''
283 encoding = ''
284 # String where the current full source input is stored, properly encoded.
284 # String where the current full source input is stored, properly encoded.
285 # Reading this attribute is the normal way of querying the currently pushed
285 # Reading this attribute is the normal way of querying the currently pushed
286 # source code, that has been properly encoded.
286 # source code, that has been properly encoded.
287 source = ''
287 source = ''
288 # Code object corresponding to the current source. It is automatically
288 # Code object corresponding to the current source. It is automatically
289 # synced to the source, so it can be queried at any time to obtain the code
289 # synced to the source, so it can be queried at any time to obtain the code
290 # object; it will be None if the source doesn't compile to valid Python.
290 # object; it will be None if the source doesn't compile to valid Python.
291 code = None
291 code = None
292 # Input mode
292 # Input mode
293 input_mode = 'line'
293 input_mode = 'line'
294
294
295 # Private attributes
295 # Private attributes
296
296
297 # List with lines of input accumulated so far
297 # List with lines of input accumulated so far
298 _buffer = None
298 _buffer = None
299 # Command compiler
299 # Command compiler
300 _compile = None
300 _compile = None
301 # Mark when input has changed indentation all the way back to flush-left
301 # Mark when input has changed indentation all the way back to flush-left
302 _full_dedent = False
302 _full_dedent = False
303 # Boolean indicating whether the current block is complete
303 # Boolean indicating whether the current block is complete
304 _is_complete = None
304 _is_complete = None
305
305
306 def __init__(self, input_mode=None):
306 def __init__(self, input_mode=None):
307 """Create a new InputSplitter instance.
307 """Create a new InputSplitter instance.
308
308
309 Parameters
309 Parameters
310 ----------
310 ----------
311 input_mode : str
311 input_mode : str
312
312
313 One of ['line', 'cell']; default is 'line'.
313 One of ['line', 'cell']; default is 'line'.
314
314
315 The input_mode parameter controls how new inputs are used when fed via
315 The input_mode parameter controls how new inputs are used when fed via
316 the :meth:`push` method:
316 the :meth:`push` method:
317
317
318 - 'line': meant for line-oriented clients, inputs are appended one at a
318 - 'line': meant for line-oriented clients, inputs are appended one at a
319 time to the internal buffer and the whole buffer is compiled.
319 time to the internal buffer and the whole buffer is compiled.
320
320
321 - 'cell': meant for clients that can edit multi-line 'cells' of text at
321 - 'cell': meant for clients that can edit multi-line 'cells' of text at
322 a time. A cell can contain one or more blocks that can be compile in
322 a time. A cell can contain one or more blocks that can be compile in
323 'single' mode by Python. In this mode, each new input new input
323 'single' mode by Python. In this mode, each new input new input
324 completely replaces all prior inputs. Cell mode is thus equivalent
324 completely replaces all prior inputs. Cell mode is thus equivalent
325 to prepending a full reset() to every push() call.
325 to prepending a full reset() to every push() call.
326 """
326 """
327 self._buffer = []
327 self._buffer = []
328 self._compile = codeop.CommandCompiler()
328 self._compile = codeop.CommandCompiler()
329 self.encoding = get_input_encoding()
329 self.encoding = get_input_encoding()
330 self.input_mode = InputSplitter.input_mode if input_mode is None \
330 self.input_mode = InputSplitter.input_mode if input_mode is None \
331 else input_mode
331 else input_mode
332
332
333 def reset(self):
333 def reset(self):
334 """Reset the input buffer and associated state."""
334 """Reset the input buffer and associated state."""
335 self.indent_spaces = 0
335 self.indent_spaces = 0
336 self._buffer[:] = []
336 self._buffer[:] = []
337 self.source = ''
337 self.source = ''
338 self.code = None
338 self.code = None
339 self._is_complete = False
339 self._is_complete = False
340 self._full_dedent = False
340 self._full_dedent = False
341
341
342 def source_reset(self):
342 def source_reset(self):
343 """Return the input source and perform a full reset.
343 """Return the input source and perform a full reset.
344 """
344 """
345 out = self.source
345 out = self.source
346 self.reset()
346 self.reset()
347 return out
347 return out
348
348
349 def push(self, lines):
349 def push(self, lines):
350 """Push one ore more lines of input.
350 """Push one ore more lines of input.
351
351
352 This stores the given lines and returns a status code indicating
352 This stores the given lines and returns a status code indicating
353 whether the code forms a complete Python block or not.
353 whether the code forms a complete Python block or not.
354
354
355 Any exceptions generated in compilation are swallowed, but if an
355 Any exceptions generated in compilation are swallowed, but if an
356 exception was produced, the method returns True.
356 exception was produced, the method returns True.
357
357
358 Parameters
358 Parameters
359 ----------
359 ----------
360 lines : string
360 lines : string
361 One or more lines of Python input.
361 One or more lines of Python input.
362
362
363 Returns
363 Returns
364 -------
364 -------
365 is_complete : boolean
365 is_complete : boolean
366 True if the current input source (the result of the current input
366 True if the current input source (the result of the current input
367 plus prior inputs) forms a complete Python execution block. Note that
367 plus prior inputs) forms a complete Python execution block. Note that
368 this value is also stored as a private attribute (_is_complete), so it
368 this value is also stored as a private attribute (_is_complete), so it
369 can be queried at any time.
369 can be queried at any time.
370 """
370 """
371 if self.input_mode == 'cell':
371 if self.input_mode == 'cell':
372 self.reset()
372 self.reset()
373
373
374 # If the source code has leading blanks, add 'if 1:\n' to it
374 # If the source code has leading blanks, add 'if 1:\n' to it
375 # this allows execution of indented pasted code. It is tempting
375 # this allows execution of indented pasted code. It is tempting
376 # to add '\n' at the end of source to run commands like ' a=1'
376 # to add '\n' at the end of source to run commands like ' a=1'
377 # directly, but this fails for more complicated scenarios
377 # directly, but this fails for more complicated scenarios
378
378
379 if not self._buffer and lines[:1] in [' ', '\t'] and \
379 if not self._buffer and lines[:1] in [' ', '\t'] and \
380 not comment_line_re.match(lines):
380 not comment_line_re.match(lines):
381 lines = 'if 1:\n%s' % lines
381 lines = 'if 1:\n%s' % lines
382
382
383 self._store(lines)
383 self._store(lines)
384 source = self.source
384 source = self.source
385
385
386 # Before calling _compile(), reset the code object to None so that if an
386 # Before calling _compile(), reset the code object to None so that if an
387 # exception is raised in compilation, we don't mislead by having
387 # exception is raised in compilation, we don't mislead by having
388 # inconsistent code/source attributes.
388 # inconsistent code/source attributes.
389 self.code, self._is_complete = None, None
389 self.code, self._is_complete = None, None
390
390
391 # Honor termination lines properly
392 if source.rstrip().endswith('\\'):
393 return False
394
391 self._update_indent(lines)
395 self._update_indent(lines)
392 try:
396 try:
393 self.code = self._compile(source)
397 self.code = self._compile(source)
394 # Invalid syntax can produce any of a number of different errors from
398 # Invalid syntax can produce any of a number of different errors from
395 # inside the compiler, so we have to catch them all. Syntax errors
399 # inside the compiler, so we have to catch them all. Syntax errors
396 # immediately produce a 'ready' block, so the invalid Python can be
400 # immediately produce a 'ready' block, so the invalid Python can be
397 # sent to the kernel for evaluation with possible ipython
401 # sent to the kernel for evaluation with possible ipython
398 # special-syntax conversion.
402 # special-syntax conversion.
399 except (SyntaxError, OverflowError, ValueError, TypeError,
403 except (SyntaxError, OverflowError, ValueError, TypeError,
400 MemoryError):
404 MemoryError):
401 self._is_complete = True
405 self._is_complete = True
402 else:
406 else:
403 # Compilation didn't produce any exceptions (though it may not have
407 # Compilation didn't produce any exceptions (though it may not have
404 # given a complete code object)
408 # given a complete code object)
405 self._is_complete = self.code is not None
409 self._is_complete = self.code is not None
406
410
407 return self._is_complete
411 return self._is_complete
408
412
409 def push_accepts_more(self):
413 def push_accepts_more(self):
410 """Return whether a block of interactive input can accept more input.
414 """Return whether a block of interactive input can accept more input.
411
415
412 This method is meant to be used by line-oriented frontends, who need to
416 This method is meant to be used by line-oriented frontends, who need to
413 guess whether a block is complete or not based solely on prior and
417 guess whether a block is complete or not based solely on prior and
414 current input lines. The InputSplitter considers it has a complete
418 current input lines. The InputSplitter considers it has a complete
415 interactive block and will not accept more input only when either a
419 interactive block and will not accept more input only when either a
416 SyntaxError is raised, or *all* of the following are true:
420 SyntaxError is raised, or *all* of the following are true:
417
421
418 1. The input compiles to a complete statement.
422 1. The input compiles to a complete statement.
419
423
420 2. The indentation level is flush-left (because if we are indented,
424 2. The indentation level is flush-left (because if we are indented,
421 like inside a function definition or for loop, we need to keep
425 like inside a function definition or for loop, we need to keep
422 reading new input).
426 reading new input).
423
427
424 3. There is one extra line consisting only of whitespace.
428 3. There is one extra line consisting only of whitespace.
425
429
426 Because of condition #3, this method should be used only by
430 Because of condition #3, this method should be used only by
427 *line-oriented* frontends, since it means that intermediate blank lines
431 *line-oriented* frontends, since it means that intermediate blank lines
428 are not allowed in function definitions (or any other indented block).
432 are not allowed in function definitions (or any other indented block).
429
433
430 Block-oriented frontends that have a separate keyboard event to
434 Block-oriented frontends that have a separate keyboard event to
431 indicate execution should use the :meth:`split_blocks` method instead.
435 indicate execution should use the :meth:`split_blocks` method instead.
432
436
433 If the current input produces a syntax error, this method immediately
437 If the current input produces a syntax error, this method immediately
434 returns False but does *not* raise the syntax error exception, as
438 returns False but does *not* raise the syntax error exception, as
435 typically clients will want to send invalid syntax to an execution
439 typically clients will want to send invalid syntax to an execution
436 backend which might convert the invalid syntax into valid Python via
440 backend which might convert the invalid syntax into valid Python via
437 one of the dynamic IPython mechanisms.
441 one of the dynamic IPython mechanisms.
438 """
442 """
439
443
440 # With incomplete input, unconditionally accept more
444 # With incomplete input, unconditionally accept more
441 if not self._is_complete:
445 if not self._is_complete:
442 return True
446 return True
443
447
444 # If we already have complete input and we're flush left, the answer
448 # If we already have complete input and we're flush left, the answer
445 # depends. In line mode, we're done. But in cell mode, we need to
449 # depends. In line mode, we're done. But in cell mode, we need to
446 # check how many blocks the input so far compiles into, because if
450 # check how many blocks the input so far compiles into, because if
447 # there's already more than one full independent block of input, then
451 # there's already more than one full independent block of input, then
448 # the client has entered full 'cell' mode and is feeding lines that
452 # the client has entered full 'cell' mode and is feeding lines that
449 # each is complete. In this case we should then keep accepting.
453 # each is complete. In this case we should then keep accepting.
450 # The Qt terminal-like console does precisely this, to provide the
454 # The Qt terminal-like console does precisely this, to provide the
451 # convenience of terminal-like input of single expressions, but
455 # convenience of terminal-like input of single expressions, but
452 # allowing the user (with a separate keystroke) to switch to 'cell'
456 # allowing the user (with a separate keystroke) to switch to 'cell'
453 # mode and type multiple expressions in one shot.
457 # mode and type multiple expressions in one shot.
454 if self.indent_spaces==0:
458 if self.indent_spaces==0:
455 if self.input_mode=='line':
459 if self.input_mode=='line':
456 return False
460 return False
457 else:
461 else:
458 nblocks = len(split_blocks(''.join(self._buffer)))
462 nblocks = len(split_blocks(''.join(self._buffer)))
459 if nblocks==1:
463 if nblocks==1:
460 return False
464 return False
461
465
462 # When input is complete, then termination is marked by an extra blank
466 # When input is complete, then termination is marked by an extra blank
463 # line at the end.
467 # line at the end.
464 last_line = self.source.splitlines()[-1]
468 last_line = self.source.splitlines()[-1]
465 return bool(last_line and not last_line.isspace())
469 return bool(last_line and not last_line.isspace())
466
470
467 def split_blocks(self, lines):
471 def split_blocks(self, lines):
468 """Split a multiline string into multiple input blocks.
472 """Split a multiline string into multiple input blocks.
469
473
470 Note: this method starts by performing a full reset().
474 Note: this method starts by performing a full reset().
471
475
472 Parameters
476 Parameters
473 ----------
477 ----------
474 lines : str
478 lines : str
475 A possibly multiline string.
479 A possibly multiline string.
476
480
477 Returns
481 Returns
478 -------
482 -------
479 blocks : list
483 blocks : list
480 A list of strings, each possibly multiline. Each string corresponds
484 A list of strings, each possibly multiline. Each string corresponds
481 to a single block that can be compiled in 'single' mode (unless it
485 to a single block that can be compiled in 'single' mode (unless it
482 has a syntax error)."""
486 has a syntax error)."""
483
487
484 # This code is fairly delicate. If you make any changes here, make
488 # This code is fairly delicate. If you make any changes here, make
485 # absolutely sure that you do run the full test suite and ALL tests
489 # absolutely sure that you do run the full test suite and ALL tests
486 # pass.
490 # pass.
487
491
488 self.reset()
492 self.reset()
489 blocks = []
493 blocks = []
490
494
491 # Reversed copy so we can use pop() efficiently and consume the input
495 # Reversed copy so we can use pop() efficiently and consume the input
492 # as a stack
496 # as a stack
493 lines = lines.splitlines()[::-1]
497 lines = lines.splitlines()[::-1]
494 # Outer loop over all input
498 # Outer loop over all input
495 while lines:
499 while lines:
496 #print 'Current lines:', lines # dbg
500 #print 'Current lines:', lines # dbg
497 # Inner loop to build each block
501 # Inner loop to build each block
498 while True:
502 while True:
499 # Safety exit from inner loop
503 # Safety exit from inner loop
500 if not lines:
504 if not lines:
501 break
505 break
502 # Grab next line but don't push it yet
506 # Grab next line but don't push it yet
503 next_line = lines.pop()
507 next_line = lines.pop()
504 # Blank/empty lines are pushed as-is
508 # Blank/empty lines are pushed as-is
505 if not next_line or next_line.isspace():
509 if not next_line or next_line.isspace():
506 self.push(next_line)
510 self.push(next_line)
507 continue
511 continue
508
512
509 # Check indentation changes caused by the *next* line
513 # Check indentation changes caused by the *next* line
510 indent_spaces, _full_dedent = self._find_indent(next_line)
514 indent_spaces, _full_dedent = self._find_indent(next_line)
511
515
512 # If the next line causes a dedent, it can be for two differnt
516 # If the next line causes a dedent, it can be for two differnt
513 # reasons: either an explicit de-dent by the user or a
517 # reasons: either an explicit de-dent by the user or a
514 # return/raise/pass statement. These MUST be handled
518 # return/raise/pass statement. These MUST be handled
515 # separately:
519 # separately:
516 #
520 #
517 # 1. the first case is only detected when the actual explicit
521 # 1. the first case is only detected when the actual explicit
518 # dedent happens, and that would be the *first* line of a *new*
522 # dedent happens, and that would be the *first* line of a *new*
519 # block. Thus, we must put the line back into the input buffer
523 # block. Thus, we must put the line back into the input buffer
520 # so that it starts a new block on the next pass.
524 # so that it starts a new block on the next pass.
521 #
525 #
522 # 2. the second case is detected in the line before the actual
526 # 2. the second case is detected in the line before the actual
523 # dedent happens, so , we consume the line and we can break out
527 # dedent happens, so , we consume the line and we can break out
524 # to start a new block.
528 # to start a new block.
525
529
526 # Case 1, explicit dedent causes a break.
530 # Case 1, explicit dedent causes a break.
527 # Note: check that we weren't on the very last line, else we'll
531 # Note: check that we weren't on the very last line, else we'll
528 # enter an infinite loop adding/removing the last line.
532 # enter an infinite loop adding/removing the last line.
529 if _full_dedent and lines and not next_line.startswith(' '):
533 if _full_dedent and lines and not next_line.startswith(' '):
530 lines.append(next_line)
534 lines.append(next_line)
531 break
535 break
532
536
533 # Otherwise any line is pushed
537 # Otherwise any line is pushed
534 self.push(next_line)
538 self.push(next_line)
535
539
536 # Case 2, full dedent with full block ready:
540 # Case 2, full dedent with full block ready:
537 if _full_dedent or \
541 if _full_dedent or \
538 self.indent_spaces==0 and not self.push_accepts_more():
542 self.indent_spaces==0 and not self.push_accepts_more():
539 break
543 break
540 # Form the new block with the current source input
544 # Form the new block with the current source input
541 blocks.append(self.source_reset())
545 blocks.append(self.source_reset())
542
546
543 #return blocks
547 #return blocks
544 # HACK!!! Now that our input is in blocks but guaranteed to be pure
548 # HACK!!! Now that our input is in blocks but guaranteed to be pure
545 # python syntax, feed it back a second time through the AST-based
549 # python syntax, feed it back a second time through the AST-based
546 # splitter, which is more accurate than ours.
550 # splitter, which is more accurate than ours.
547 return split_blocks(''.join(blocks))
551 return split_blocks(''.join(blocks))
548
552
549 #------------------------------------------------------------------------
553 #------------------------------------------------------------------------
550 # Private interface
554 # Private interface
551 #------------------------------------------------------------------------
555 #------------------------------------------------------------------------
552
556
553 def _find_indent(self, line):
557 def _find_indent(self, line):
554 """Compute the new indentation level for a single line.
558 """Compute the new indentation level for a single line.
555
559
556 Parameters
560 Parameters
557 ----------
561 ----------
558 line : str
562 line : str
559 A single new line of non-whitespace, non-comment Python input.
563 A single new line of non-whitespace, non-comment Python input.
560
564
561 Returns
565 Returns
562 -------
566 -------
563 indent_spaces : int
567 indent_spaces : int
564 New value for the indent level (it may be equal to self.indent_spaces
568 New value for the indent level (it may be equal to self.indent_spaces
565 if indentation doesn't change.
569 if indentation doesn't change.
566
570
567 full_dedent : boolean
571 full_dedent : boolean
568 Whether the new line causes a full flush-left dedent.
572 Whether the new line causes a full flush-left dedent.
569 """
573 """
570 indent_spaces = self.indent_spaces
574 indent_spaces = self.indent_spaces
571 full_dedent = self._full_dedent
575 full_dedent = self._full_dedent
572
576
573 inisp = num_ini_spaces(line)
577 inisp = num_ini_spaces(line)
574 if inisp < indent_spaces:
578 if inisp < indent_spaces:
575 indent_spaces = inisp
579 indent_spaces = inisp
576 if indent_spaces <= 0:
580 if indent_spaces <= 0:
577 #print 'Full dedent in text',self.source # dbg
581 #print 'Full dedent in text',self.source # dbg
578 full_dedent = True
582 full_dedent = True
579
583
580 if line[-1] == ':':
584 if line[-1] == ':':
581 indent_spaces += 4
585 indent_spaces += 4
582 elif dedent_re.match(line):
586 elif dedent_re.match(line):
583 indent_spaces -= 4
587 indent_spaces -= 4
584 if indent_spaces <= 0:
588 if indent_spaces <= 0:
585 full_dedent = True
589 full_dedent = True
586
590
587 # Safety
591 # Safety
588 if indent_spaces < 0:
592 if indent_spaces < 0:
589 indent_spaces = 0
593 indent_spaces = 0
590 #print 'safety' # dbg
594 #print 'safety' # dbg
591
595
592 return indent_spaces, full_dedent
596 return indent_spaces, full_dedent
593
597
594 def _update_indent(self, lines):
598 def _update_indent(self, lines):
595 for line in remove_comments(lines).splitlines():
599 for line in remove_comments(lines).splitlines():
596 if line and not line.isspace():
600 if line and not line.isspace():
597 self.indent_spaces, self._full_dedent = self._find_indent(line)
601 self.indent_spaces, self._full_dedent = self._find_indent(line)
598
602
599 def _store(self, lines):
603 def _store(self, lines):
600 """Store one or more lines of input.
604 """Store one or more lines of input.
601
605
602 If input lines are not newline-terminated, a newline is automatically
606 If input lines are not newline-terminated, a newline is automatically
603 appended."""
607 appended."""
604
608
605 if lines.endswith('\n'):
609 if lines.endswith('\n'):
606 self._buffer.append(lines)
610 self._buffer.append(lines)
607 else:
611 else:
608 self._buffer.append(lines+'\n')
612 self._buffer.append(lines+'\n')
609 self._set_source()
613 self._set_source()
610
614
611 def _set_source(self):
615 def _set_source(self):
612 self.source = ''.join(self._buffer).encode(self.encoding)
616 self.source = ''.join(self._buffer).encode(self.encoding)
613
617
614
618
615 #-----------------------------------------------------------------------------
619 #-----------------------------------------------------------------------------
616 # Functions and classes for IPython-specific syntactic support
620 # Functions and classes for IPython-specific syntactic support
617 #-----------------------------------------------------------------------------
621 #-----------------------------------------------------------------------------
618
622
619 # RegExp for splitting line contents into pre-char//first word-method//rest.
623 # RegExp for splitting line contents into pre-char//first word-method//rest.
620 # For clarity, each group in on one line.
624 # For clarity, each group in on one line.
621
625
622 line_split = re.compile("""
626 line_split = re.compile("""
623 ^(\s*) # any leading space
627 ^(\s*) # any leading space
624 ([,;/%]|!!?|\?\??) # escape character or characters
628 ([,;/%]|!!?|\?\??) # escape character or characters
625 \s*(%?[\w\.]*) # function/method, possibly with leading %
629 \s*(%?[\w\.]*) # function/method, possibly with leading %
626 # to correctly treat things like '?%magic'
630 # to correctly treat things like '?%magic'
627 (\s+.*$|$) # rest of line
631 (\s+.*$|$) # rest of line
628 """, re.VERBOSE)
632 """, re.VERBOSE)
629
633
630
634
631 def split_user_input(line):
635 def split_user_input(line):
632 """Split user input into early whitespace, esc-char, function part and rest.
636 """Split user input into early whitespace, esc-char, function part and rest.
633
637
634 This is currently handles lines with '=' in them in a very inconsistent
638 This is currently handles lines with '=' in them in a very inconsistent
635 manner.
639 manner.
636
640
637 Examples
641 Examples
638 ========
642 ========
639 >>> split_user_input('x=1')
643 >>> split_user_input('x=1')
640 ('', '', 'x=1', '')
644 ('', '', 'x=1', '')
641 >>> split_user_input('?')
645 >>> split_user_input('?')
642 ('', '?', '', '')
646 ('', '?', '', '')
643 >>> split_user_input('??')
647 >>> split_user_input('??')
644 ('', '??', '', '')
648 ('', '??', '', '')
645 >>> split_user_input(' ?')
649 >>> split_user_input(' ?')
646 (' ', '?', '', '')
650 (' ', '?', '', '')
647 >>> split_user_input(' ??')
651 >>> split_user_input(' ??')
648 (' ', '??', '', '')
652 (' ', '??', '', '')
649 >>> split_user_input('??x')
653 >>> split_user_input('??x')
650 ('', '??', 'x', '')
654 ('', '??', 'x', '')
651 >>> split_user_input('?x=1')
655 >>> split_user_input('?x=1')
652 ('', '', '?x=1', '')
656 ('', '', '?x=1', '')
653 >>> split_user_input('!ls')
657 >>> split_user_input('!ls')
654 ('', '!', 'ls', '')
658 ('', '!', 'ls', '')
655 >>> split_user_input(' !ls')
659 >>> split_user_input(' !ls')
656 (' ', '!', 'ls', '')
660 (' ', '!', 'ls', '')
657 >>> split_user_input('!!ls')
661 >>> split_user_input('!!ls')
658 ('', '!!', 'ls', '')
662 ('', '!!', 'ls', '')
659 >>> split_user_input(' !!ls')
663 >>> split_user_input(' !!ls')
660 (' ', '!!', 'ls', '')
664 (' ', '!!', 'ls', '')
661 >>> split_user_input(',ls')
665 >>> split_user_input(',ls')
662 ('', ',', 'ls', '')
666 ('', ',', 'ls', '')
663 >>> split_user_input(';ls')
667 >>> split_user_input(';ls')
664 ('', ';', 'ls', '')
668 ('', ';', 'ls', '')
665 >>> split_user_input(' ;ls')
669 >>> split_user_input(' ;ls')
666 (' ', ';', 'ls', '')
670 (' ', ';', 'ls', '')
667 >>> split_user_input('f.g(x)')
671 >>> split_user_input('f.g(x)')
668 ('', '', 'f.g(x)', '')
672 ('', '', 'f.g(x)', '')
669 >>> split_user_input('f.g (x)')
673 >>> split_user_input('f.g (x)')
670 ('', '', 'f.g', '(x)')
674 ('', '', 'f.g', '(x)')
671 >>> split_user_input('?%hist')
675 >>> split_user_input('?%hist')
672 ('', '?', '%hist', '')
676 ('', '?', '%hist', '')
673 """
677 """
674 match = line_split.match(line)
678 match = line_split.match(line)
675 if match:
679 if match:
676 lspace, esc, fpart, rest = match.groups()
680 lspace, esc, fpart, rest = match.groups()
677 else:
681 else:
678 # print "match failed for line '%s'" % line
682 # print "match failed for line '%s'" % line
679 try:
683 try:
680 fpart, rest = line.split(None, 1)
684 fpart, rest = line.split(None, 1)
681 except ValueError:
685 except ValueError:
682 # print "split failed for line '%s'" % line
686 # print "split failed for line '%s'" % line
683 fpart, rest = line,''
687 fpart, rest = line,''
684 lspace = re.match('^(\s*)(.*)', line).groups()[0]
688 lspace = re.match('^(\s*)(.*)', line).groups()[0]
685 esc = ''
689 esc = ''
686
690
687 # fpart has to be a valid python identifier, so it better be only pure
691 # fpart has to be a valid python identifier, so it better be only pure
688 # ascii, no unicode:
692 # ascii, no unicode:
689 try:
693 try:
690 fpart = fpart.encode('ascii')
694 fpart = fpart.encode('ascii')
691 except UnicodeEncodeError:
695 except UnicodeEncodeError:
692 lspace = unicode(lspace)
696 lspace = unicode(lspace)
693 rest = fpart + u' ' + rest
697 rest = fpart + u' ' + rest
694 fpart = u''
698 fpart = u''
695
699
696 #print 'line:<%s>' % line # dbg
700 #print 'line:<%s>' % line # dbg
697 #print 'esc <%s> fpart <%s> rest <%s>' % (esc,fpart.strip(),rest) # dbg
701 #print 'esc <%s> fpart <%s> rest <%s>' % (esc,fpart.strip(),rest) # dbg
698 return lspace, esc, fpart.strip(), rest.lstrip()
702 return lspace, esc, fpart.strip(), rest.lstrip()
699
703
700
704
701 # The escaped translators ALL receive a line where their own escape has been
705 # The escaped translators ALL receive a line where their own escape has been
702 # stripped. Only '?' is valid at the end of the line, all others can only be
706 # stripped. Only '?' is valid at the end of the line, all others can only be
703 # placed at the start.
707 # placed at the start.
704
708
705 class LineInfo(object):
709 class LineInfo(object):
706 """A single line of input and associated info.
710 """A single line of input and associated info.
707
711
708 This is a utility class that mostly wraps the output of
712 This is a utility class that mostly wraps the output of
709 :func:`split_user_input` into a convenient object to be passed around
713 :func:`split_user_input` into a convenient object to be passed around
710 during input transformations.
714 during input transformations.
711
715
712 Includes the following as properties:
716 Includes the following as properties:
713
717
714 line
718 line
715 The original, raw line
719 The original, raw line
716
720
717 lspace
721 lspace
718 Any early whitespace before actual text starts.
722 Any early whitespace before actual text starts.
719
723
720 esc
724 esc
721 The initial esc character (or characters, for double-char escapes like
725 The initial esc character (or characters, for double-char escapes like
722 '??' or '!!').
726 '??' or '!!').
723
727
724 fpart
728 fpart
725 The 'function part', which is basically the maximal initial sequence
729 The 'function part', which is basically the maximal initial sequence
726 of valid python identifiers and the '.' character. This is what is
730 of valid python identifiers and the '.' character. This is what is
727 checked for alias and magic transformations, used for auto-calling,
731 checked for alias and magic transformations, used for auto-calling,
728 etc.
732 etc.
729
733
730 rest
734 rest
731 Everything else on the line.
735 Everything else on the line.
732 """
736 """
733 def __init__(self, line):
737 def __init__(self, line):
734 self.line = line
738 self.line = line
735 self.lspace, self.esc, self.fpart, self.rest = \
739 self.lspace, self.esc, self.fpart, self.rest = \
736 split_user_input(line)
740 split_user_input(line)
737
741
738 def __str__(self):
742 def __str__(self):
739 return "LineInfo [%s|%s|%s|%s]" % (self.lspace, self.esc,
743 return "LineInfo [%s|%s|%s|%s]" % (self.lspace, self.esc,
740 self.fpart, self.rest)
744 self.fpart, self.rest)
741
745
742
746
743 # Transformations of the special syntaxes that don't rely on an explicit escape
747 # Transformations of the special syntaxes that don't rely on an explicit escape
744 # character but instead on patterns on the input line
748 # character but instead on patterns on the input line
745
749
746 # The core transformations are implemented as standalone functions that can be
750 # The core transformations are implemented as standalone functions that can be
747 # tested and validated in isolation. Each of these uses a regexp, we
751 # tested and validated in isolation. Each of these uses a regexp, we
748 # pre-compile these and keep them close to each function definition for clarity
752 # pre-compile these and keep them close to each function definition for clarity
749
753
750 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
754 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
751 r'\s*=\s*!\s*(?P<cmd>.*)')
755 r'\s*=\s*!\s*(?P<cmd>.*)')
752
756
753 def transform_assign_system(line):
757 def transform_assign_system(line):
754 """Handle the `files = !ls` syntax."""
758 """Handle the `files = !ls` syntax."""
755 m = _assign_system_re.match(line)
759 m = _assign_system_re.match(line)
756 if m is not None:
760 if m is not None:
757 cmd = m.group('cmd')
761 cmd = m.group('cmd')
758 lhs = m.group('lhs')
762 lhs = m.group('lhs')
759 expr = make_quoted_expr(cmd)
763 expr = make_quoted_expr(cmd)
760 new_line = '%s = get_ipython().getoutput(%s)' % (lhs, expr)
764 new_line = '%s = get_ipython().getoutput(%s)' % (lhs, expr)
761 return new_line
765 return new_line
762 return line
766 return line
763
767
764
768
765 _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
769 _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
766 r'\s*=\s*%\s*(?P<cmd>.*)')
770 r'\s*=\s*%\s*(?P<cmd>.*)')
767
771
768 def transform_assign_magic(line):
772 def transform_assign_magic(line):
769 """Handle the `a = %who` syntax."""
773 """Handle the `a = %who` syntax."""
770 m = _assign_magic_re.match(line)
774 m = _assign_magic_re.match(line)
771 if m is not None:
775 if m is not None:
772 cmd = m.group('cmd')
776 cmd = m.group('cmd')
773 lhs = m.group('lhs')
777 lhs = m.group('lhs')
774 expr = make_quoted_expr(cmd)
778 expr = make_quoted_expr(cmd)
775 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
779 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
776 return new_line
780 return new_line
777 return line
781 return line
778
782
779
783
780 _classic_prompt_re = re.compile(r'^([ \t]*>>> |^[ \t]*\.\.\. )')
784 _classic_prompt_re = re.compile(r'^([ \t]*>>> |^[ \t]*\.\.\. )')
781
785
782 def transform_classic_prompt(line):
786 def transform_classic_prompt(line):
783 """Handle inputs that start with '>>> ' syntax."""
787 """Handle inputs that start with '>>> ' syntax."""
784
788
785 if not line or line.isspace():
789 if not line or line.isspace():
786 return line
790 return line
787 m = _classic_prompt_re.match(line)
791 m = _classic_prompt_re.match(line)
788 if m:
792 if m:
789 return line[len(m.group(0)):]
793 return line[len(m.group(0)):]
790 else:
794 else:
791 return line
795 return line
792
796
793
797
794 _ipy_prompt_re = re.compile(r'^([ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
798 _ipy_prompt_re = re.compile(r'^([ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
795
799
796 def transform_ipy_prompt(line):
800 def transform_ipy_prompt(line):
797 """Handle inputs that start classic IPython prompt syntax."""
801 """Handle inputs that start classic IPython prompt syntax."""
798
802
799 if not line or line.isspace():
803 if not line or line.isspace():
800 return line
804 return line
801 #print 'LINE: %r' % line # dbg
805 #print 'LINE: %r' % line # dbg
802 m = _ipy_prompt_re.match(line)
806 m = _ipy_prompt_re.match(line)
803 if m:
807 if m:
804 #print 'MATCH! %r -> %r' % (line, line[len(m.group(0)):]) # dbg
808 #print 'MATCH! %r -> %r' % (line, line[len(m.group(0)):]) # dbg
805 return line[len(m.group(0)):]
809 return line[len(m.group(0)):]
806 else:
810 else:
807 return line
811 return line
808
812
809
813
810 class EscapedTransformer(object):
814 class EscapedTransformer(object):
811 """Class to transform lines that are explicitly escaped out."""
815 """Class to transform lines that are explicitly escaped out."""
812
816
813 def __init__(self):
817 def __init__(self):
814 tr = { ESC_SHELL : self._tr_system,
818 tr = { ESC_SHELL : self._tr_system,
815 ESC_SH_CAP : self._tr_system2,
819 ESC_SH_CAP : self._tr_system2,
816 ESC_HELP : self._tr_help,
820 ESC_HELP : self._tr_help,
817 ESC_HELP2 : self._tr_help,
821 ESC_HELP2 : self._tr_help,
818 ESC_MAGIC : self._tr_magic,
822 ESC_MAGIC : self._tr_magic,
819 ESC_QUOTE : self._tr_quote,
823 ESC_QUOTE : self._tr_quote,
820 ESC_QUOTE2 : self._tr_quote2,
824 ESC_QUOTE2 : self._tr_quote2,
821 ESC_PAREN : self._tr_paren }
825 ESC_PAREN : self._tr_paren }
822 self.tr = tr
826 self.tr = tr
823
827
824 # Support for syntax transformations that use explicit escapes typed by the
828 # Support for syntax transformations that use explicit escapes typed by the
825 # user at the beginning of a line
829 # user at the beginning of a line
826 @staticmethod
830 @staticmethod
827 def _tr_system(line_info):
831 def _tr_system(line_info):
828 "Translate lines escaped with: !"
832 "Translate lines escaped with: !"
829 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
833 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
830 return '%sget_ipython().system(%s)' % (line_info.lspace,
834 return '%sget_ipython().system(%s)' % (line_info.lspace,
831 make_quoted_expr(cmd))
835 make_quoted_expr(cmd))
832
836
833 @staticmethod
837 @staticmethod
834 def _tr_system2(line_info):
838 def _tr_system2(line_info):
835 "Translate lines escaped with: !!"
839 "Translate lines escaped with: !!"
836 cmd = line_info.line.lstrip()[2:]
840 cmd = line_info.line.lstrip()[2:]
837 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
841 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
838 make_quoted_expr(cmd))
842 make_quoted_expr(cmd))
839
843
840 @staticmethod
844 @staticmethod
841 def _tr_help(line_info):
845 def _tr_help(line_info):
842 "Translate lines escaped with: ?/??"
846 "Translate lines escaped with: ?/??"
843 # A naked help line should just fire the intro help screen
847 # A naked help line should just fire the intro help screen
844 if not line_info.line[1:]:
848 if not line_info.line[1:]:
845 return 'get_ipython().show_usage()'
849 return 'get_ipython().show_usage()'
846
850
847 # There may be one or two '?' at the end, move them to the front so that
851 # There may be one or two '?' at the end, move them to the front so that
848 # the rest of the logic can assume escapes are at the start
852 # the rest of the logic can assume escapes are at the start
849 line = line_info.line
853 line = line_info.line
850 if line.endswith('?'):
854 if line.endswith('?'):
851 line = line[-1] + line[:-1]
855 line = line[-1] + line[:-1]
852 if line.endswith('?'):
856 if line.endswith('?'):
853 line = line[-1] + line[:-1]
857 line = line[-1] + line[:-1]
854 line_info = LineInfo(line)
858 line_info = LineInfo(line)
855
859
856 # From here on, simply choose which level of detail to get.
860 # From here on, simply choose which level of detail to get.
857 if line_info.esc == '?':
861 if line_info.esc == '?':
858 pinfo = 'pinfo'
862 pinfo = 'pinfo'
859 elif line_info.esc == '??':
863 elif line_info.esc == '??':
860 pinfo = 'pinfo2'
864 pinfo = 'pinfo2'
861
865
862 tpl = '%sget_ipython().magic("%s %s")'
866 tpl = '%sget_ipython().magic("%s %s")'
863 return tpl % (line_info.lspace, pinfo,
867 return tpl % (line_info.lspace, pinfo,
864 ' '.join([line_info.fpart, line_info.rest]).strip())
868 ' '.join([line_info.fpart, line_info.rest]).strip())
865
869
866 @staticmethod
870 @staticmethod
867 def _tr_magic(line_info):
871 def _tr_magic(line_info):
868 "Translate lines escaped with: %"
872 "Translate lines escaped with: %"
869 tpl = '%sget_ipython().magic(%s)'
873 tpl = '%sget_ipython().magic(%s)'
870 cmd = make_quoted_expr(' '.join([line_info.fpart,
874 cmd = make_quoted_expr(' '.join([line_info.fpart,
871 line_info.rest]).strip())
875 line_info.rest]).strip())
872 return tpl % (line_info.lspace, cmd)
876 return tpl % (line_info.lspace, cmd)
873
877
874 @staticmethod
878 @staticmethod
875 def _tr_quote(line_info):
879 def _tr_quote(line_info):
876 "Translate lines escaped with: ,"
880 "Translate lines escaped with: ,"
877 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
881 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
878 '", "'.join(line_info.rest.split()) )
882 '", "'.join(line_info.rest.split()) )
879
883
880 @staticmethod
884 @staticmethod
881 def _tr_quote2(line_info):
885 def _tr_quote2(line_info):
882 "Translate lines escaped with: ;"
886 "Translate lines escaped with: ;"
883 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
887 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
884 line_info.rest)
888 line_info.rest)
885
889
886 @staticmethod
890 @staticmethod
887 def _tr_paren(line_info):
891 def _tr_paren(line_info):
888 "Translate lines escaped with: /"
892 "Translate lines escaped with: /"
889 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
893 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
890 ", ".join(line_info.rest.split()))
894 ", ".join(line_info.rest.split()))
891
895
892 def __call__(self, line):
896 def __call__(self, line):
893 """Class to transform lines that are explicitly escaped out.
897 """Class to transform lines that are explicitly escaped out.
894
898
895 This calls the above _tr_* static methods for the actual line
899 This calls the above _tr_* static methods for the actual line
896 translations."""
900 translations."""
897
901
898 # Empty lines just get returned unmodified
902 # Empty lines just get returned unmodified
899 if not line or line.isspace():
903 if not line or line.isspace():
900 return line
904 return line
901
905
902 # Get line endpoints, where the escapes can be
906 # Get line endpoints, where the escapes can be
903 line_info = LineInfo(line)
907 line_info = LineInfo(line)
904
908
905 # If the escape is not at the start, only '?' needs to be special-cased.
909 # If the escape is not at the start, only '?' needs to be special-cased.
906 # All other escapes are only valid at the start
910 # All other escapes are only valid at the start
907 if not line_info.esc in self.tr:
911 if not line_info.esc in self.tr:
908 if line.endswith(ESC_HELP):
912 if line.endswith(ESC_HELP):
909 return self._tr_help(line_info)
913 return self._tr_help(line_info)
910 else:
914 else:
911 # If we don't recognize the escape, don't modify the line
915 # If we don't recognize the escape, don't modify the line
912 return line
916 return line
913
917
914 return self.tr[line_info.esc](line_info)
918 return self.tr[line_info.esc](line_info)
915
919
916
920
917 # A function-looking object to be used by the rest of the code. The purpose of
921 # A function-looking object to be used by the rest of the code. The purpose of
918 # the class in this case is to organize related functionality, more than to
922 # the class in this case is to organize related functionality, more than to
919 # manage state.
923 # manage state.
920 transform_escaped = EscapedTransformer()
924 transform_escaped = EscapedTransformer()
921
925
922
926
923 class IPythonInputSplitter(InputSplitter):
927 class IPythonInputSplitter(InputSplitter):
924 """An input splitter that recognizes all of IPython's special syntax."""
928 """An input splitter that recognizes all of IPython's special syntax."""
925
929
926 def push(self, lines):
930 def push(self, lines):
927 """Push one or more lines of IPython input.
931 """Push one or more lines of IPython input.
928 """
932 """
929 if not lines:
933 if not lines:
930 return super(IPythonInputSplitter, self).push(lines)
934 return super(IPythonInputSplitter, self).push(lines)
931
935
932 lines_list = lines.splitlines()
936 lines_list = lines.splitlines()
933
937
934 transforms = [transform_escaped, transform_assign_system,
938 transforms = [transform_escaped, transform_assign_system,
935 transform_assign_magic, transform_ipy_prompt,
939 transform_assign_magic, transform_ipy_prompt,
936 transform_classic_prompt]
940 transform_classic_prompt]
937
941
938 # Transform logic
942 # Transform logic
939 #
943 #
940 # We only apply the line transformers to the input if we have either no
944 # We only apply the line transformers to the input if we have either no
941 # input yet, or complete input, or if the last line of the buffer ends
945 # input yet, or complete input, or if the last line of the buffer ends
942 # with ':' (opening an indented block). This prevents the accidental
946 # with ':' (opening an indented block). This prevents the accidental
943 # transformation of escapes inside multiline expressions like
947 # transformation of escapes inside multiline expressions like
944 # triple-quoted strings or parenthesized expressions.
948 # triple-quoted strings or parenthesized expressions.
945 #
949 #
946 # The last heuristic, while ugly, ensures that the first line of an
950 # The last heuristic, while ugly, ensures that the first line of an
947 # indented block is correctly transformed.
951 # indented block is correctly transformed.
948 #
952 #
949 # FIXME: try to find a cleaner approach for this last bit.
953 # FIXME: try to find a cleaner approach for this last bit.
950
954
951 # If we were in 'block' mode, since we're going to pump the parent
955 # If we were in 'block' mode, since we're going to pump the parent
952 # class by hand line by line, we need to temporarily switch out to
956 # class by hand line by line, we need to temporarily switch out to
953 # 'line' mode, do a single manual reset and then feed the lines one
957 # 'line' mode, do a single manual reset and then feed the lines one
954 # by one. Note that this only matters if the input has more than one
958 # by one. Note that this only matters if the input has more than one
955 # line.
959 # line.
956 changed_input_mode = False
960 changed_input_mode = False
957
961
958 if len(lines_list)>1 and self.input_mode == 'cell':
962 if len(lines_list)>1 and self.input_mode == 'cell':
959 self.reset()
963 self.reset()
960 changed_input_mode = True
964 changed_input_mode = True
961 saved_input_mode = 'cell'
965 saved_input_mode = 'cell'
962 self.input_mode = 'line'
966 self.input_mode = 'line'
963
967
964 try:
968 try:
965 push = super(IPythonInputSplitter, self).push
969 push = super(IPythonInputSplitter, self).push
966 for line in lines_list:
970 for line in lines_list:
967 if self._is_complete or not self._buffer or \
971 if self._is_complete or not self._buffer or \
968 (self._buffer and self._buffer[-1].rstrip().endswith(':')):
972 (self._buffer and self._buffer[-1].rstrip().endswith(':')):
969 for f in transforms:
973 for f in transforms:
970 line = f(line)
974 line = f(line)
971
975
972 out = push(line)
976 out = push(line)
973 finally:
977 finally:
974 if changed_input_mode:
978 if changed_input_mode:
975 self.input_mode = saved_input_mode
979 self.input_mode = saved_input_mode
976
980
977 return out
981 return out
@@ -1,649 +1,656 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2010 The IPython Development Team
5 # Copyright (C) 2010 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # stdlib
14 # stdlib
15 import unittest
15 import unittest
16 import sys
16 import sys
17
17
18 # Third party
18 # Third party
19 import nose.tools as nt
19 import nose.tools as nt
20
20
21 # Our own
21 # Our own
22 from IPython.core import inputsplitter as isp
22 from IPython.core import inputsplitter as isp
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Semi-complete examples (also used as tests)
25 # Semi-complete examples (also used as tests)
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 # Note: at the bottom, there's a slightly more complete version of this that
28 # Note: at the bottom, there's a slightly more complete version of this that
29 # can be useful during development of code here.
29 # can be useful during development of code here.
30
30
31 def mini_interactive_loop(raw_input):
31 def mini_interactive_loop(raw_input):
32 """Minimal example of the logic of an interactive interpreter loop.
32 """Minimal example of the logic of an interactive interpreter loop.
33
33
34 This serves as an example, and it is used by the test system with a fake
34 This serves as an example, and it is used by the test system with a fake
35 raw_input that simulates interactive input."""
35 raw_input that simulates interactive input."""
36
36
37 from IPython.core.inputsplitter import InputSplitter
37 from IPython.core.inputsplitter import InputSplitter
38
38
39 isp = InputSplitter()
39 isp = InputSplitter()
40 # In practice, this input loop would be wrapped in an outside loop to read
40 # In practice, this input loop would be wrapped in an outside loop to read
41 # input indefinitely, until some exit/quit command was issued. Here we
41 # input indefinitely, until some exit/quit command was issued. Here we
42 # only illustrate the basic inner loop.
42 # only illustrate the basic inner loop.
43 while isp.push_accepts_more():
43 while isp.push_accepts_more():
44 indent = ' '*isp.indent_spaces
44 indent = ' '*isp.indent_spaces
45 prompt = '>>> ' + indent
45 prompt = '>>> ' + indent
46 line = indent + raw_input(prompt)
46 line = indent + raw_input(prompt)
47 isp.push(line)
47 isp.push(line)
48
48
49 # Here we just return input so we can use it in a test suite, but a real
49 # Here we just return input so we can use it in a test suite, but a real
50 # interpreter would instead send it for execution somewhere.
50 # interpreter would instead send it for execution somewhere.
51 src = isp.source_reset()
51 src = isp.source_reset()
52 #print 'Input source was:\n', src # dbg
52 #print 'Input source was:\n', src # dbg
53 return src
53 return src
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Test utilities, just for local use
56 # Test utilities, just for local use
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59 def assemble(block):
59 def assemble(block):
60 """Assemble a block into multi-line sub-blocks."""
60 """Assemble a block into multi-line sub-blocks."""
61 return ['\n'.join(sub_block)+'\n' for sub_block in block]
61 return ['\n'.join(sub_block)+'\n' for sub_block in block]
62
62
63
63
64 def pseudo_input(lines):
64 def pseudo_input(lines):
65 """Return a function that acts like raw_input but feeds the input list."""
65 """Return a function that acts like raw_input but feeds the input list."""
66 ilines = iter(lines)
66 ilines = iter(lines)
67 def raw_in(prompt):
67 def raw_in(prompt):
68 try:
68 try:
69 return next(ilines)
69 return next(ilines)
70 except StopIteration:
70 except StopIteration:
71 return ''
71 return ''
72 return raw_in
72 return raw_in
73
73
74 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
75 # Tests
75 # Tests
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77 def test_spaces():
77 def test_spaces():
78 tests = [('', 0),
78 tests = [('', 0),
79 (' ', 1),
79 (' ', 1),
80 ('\n', 0),
80 ('\n', 0),
81 (' \n', 1),
81 (' \n', 1),
82 ('x', 0),
82 ('x', 0),
83 (' x', 1),
83 (' x', 1),
84 (' x',2),
84 (' x',2),
85 (' x',4),
85 (' x',4),
86 # Note: tabs are counted as a single whitespace!
86 # Note: tabs are counted as a single whitespace!
87 ('\tx', 1),
87 ('\tx', 1),
88 ('\t x', 2),
88 ('\t x', 2),
89 ]
89 ]
90
90
91 for s, nsp in tests:
91 for s, nsp in tests:
92 nt.assert_equal(isp.num_ini_spaces(s), nsp)
92 nt.assert_equal(isp.num_ini_spaces(s), nsp)
93
93
94
94
95 def test_remove_comments():
95 def test_remove_comments():
96 tests = [('text', 'text'),
96 tests = [('text', 'text'),
97 ('text # comment', 'text '),
97 ('text # comment', 'text '),
98 ('text # comment\n', 'text \n'),
98 ('text # comment\n', 'text \n'),
99 ('text # comment \n', 'text \n'),
99 ('text # comment \n', 'text \n'),
100 ('line # c \nline\n','line \nline\n'),
100 ('line # c \nline\n','line \nline\n'),
101 ('line # c \nline#c2 \nline\nline #c\n\n',
101 ('line # c \nline#c2 \nline\nline #c\n\n',
102 'line \nline\nline\nline \n\n'),
102 'line \nline\nline\nline \n\n'),
103 ]
103 ]
104
104
105 for inp, out in tests:
105 for inp, out in tests:
106 nt.assert_equal(isp.remove_comments(inp), out)
106 nt.assert_equal(isp.remove_comments(inp), out)
107
107
108
108
109 def test_get_input_encoding():
109 def test_get_input_encoding():
110 encoding = isp.get_input_encoding()
110 encoding = isp.get_input_encoding()
111 nt.assert_true(isinstance(encoding, basestring))
111 nt.assert_true(isinstance(encoding, basestring))
112 # simple-minded check that at least encoding a simple string works with the
112 # simple-minded check that at least encoding a simple string works with the
113 # encoding we got.
113 # encoding we got.
114 nt.assert_equal('test'.encode(encoding), 'test')
114 nt.assert_equal('test'.encode(encoding), 'test')
115
115
116
116
117 class NoInputEncodingTestCase(unittest.TestCase):
117 class NoInputEncodingTestCase(unittest.TestCase):
118 def setUp(self):
118 def setUp(self):
119 self.old_stdin = sys.stdin
119 self.old_stdin = sys.stdin
120 class X: pass
120 class X: pass
121 fake_stdin = X()
121 fake_stdin = X()
122 sys.stdin = fake_stdin
122 sys.stdin = fake_stdin
123
123
124 def test(self):
124 def test(self):
125 # Verify that if sys.stdin has no 'encoding' attribute we do the right
125 # Verify that if sys.stdin has no 'encoding' attribute we do the right
126 # thing
126 # thing
127 enc = isp.get_input_encoding()
127 enc = isp.get_input_encoding()
128 self.assertEqual(enc, 'ascii')
128 self.assertEqual(enc, 'ascii')
129
129
130 def tearDown(self):
130 def tearDown(self):
131 sys.stdin = self.old_stdin
131 sys.stdin = self.old_stdin
132
132
133
133
134 class InputSplitterTestCase(unittest.TestCase):
134 class InputSplitterTestCase(unittest.TestCase):
135 def setUp(self):
135 def setUp(self):
136 self.isp = isp.InputSplitter()
136 self.isp = isp.InputSplitter()
137
137
138 def test_reset(self):
138 def test_reset(self):
139 isp = self.isp
139 isp = self.isp
140 isp.push('x=1')
140 isp.push('x=1')
141 isp.reset()
141 isp.reset()
142 self.assertEqual(isp._buffer, [])
142 self.assertEqual(isp._buffer, [])
143 self.assertEqual(isp.indent_spaces, 0)
143 self.assertEqual(isp.indent_spaces, 0)
144 self.assertEqual(isp.source, '')
144 self.assertEqual(isp.source, '')
145 self.assertEqual(isp.code, None)
145 self.assertEqual(isp.code, None)
146 self.assertEqual(isp._is_complete, False)
146 self.assertEqual(isp._is_complete, False)
147
147
148 def test_source(self):
148 def test_source(self):
149 self.isp._store('1')
149 self.isp._store('1')
150 self.isp._store('2')
150 self.isp._store('2')
151 self.assertEqual(self.isp.source, '1\n2\n')
151 self.assertEqual(self.isp.source, '1\n2\n')
152 self.assertTrue(len(self.isp._buffer)>0)
152 self.assertTrue(len(self.isp._buffer)>0)
153 self.assertEqual(self.isp.source_reset(), '1\n2\n')
153 self.assertEqual(self.isp.source_reset(), '1\n2\n')
154 self.assertEqual(self.isp._buffer, [])
154 self.assertEqual(self.isp._buffer, [])
155 self.assertEqual(self.isp.source, '')
155 self.assertEqual(self.isp.source, '')
156
156
157 def test_indent(self):
157 def test_indent(self):
158 isp = self.isp # shorthand
158 isp = self.isp # shorthand
159 isp.push('x=1')
159 isp.push('x=1')
160 self.assertEqual(isp.indent_spaces, 0)
160 self.assertEqual(isp.indent_spaces, 0)
161 isp.push('if 1:\n x=1')
161 isp.push('if 1:\n x=1')
162 self.assertEqual(isp.indent_spaces, 4)
162 self.assertEqual(isp.indent_spaces, 4)
163 isp.push('y=2\n')
163 isp.push('y=2\n')
164 self.assertEqual(isp.indent_spaces, 0)
164 self.assertEqual(isp.indent_spaces, 0)
165 isp.push('if 1:')
165 isp.push('if 1:')
166 self.assertEqual(isp.indent_spaces, 4)
166 self.assertEqual(isp.indent_spaces, 4)
167 isp.push(' x=1')
167 isp.push(' x=1')
168 self.assertEqual(isp.indent_spaces, 4)
168 self.assertEqual(isp.indent_spaces, 4)
169 # Blank lines shouldn't change the indent level
169 # Blank lines shouldn't change the indent level
170 isp.push(' '*2)
170 isp.push(' '*2)
171 self.assertEqual(isp.indent_spaces, 4)
171 self.assertEqual(isp.indent_spaces, 4)
172
172
173 def test_indent2(self):
173 def test_indent2(self):
174 isp = self.isp
174 isp = self.isp
175 # When a multiline statement contains parens or multiline strings, we
175 # When a multiline statement contains parens or multiline strings, we
176 # shouldn't get confused.
176 # shouldn't get confused.
177 isp.push("if 1:")
177 isp.push("if 1:")
178 isp.push(" x = (1+\n 2)")
178 isp.push(" x = (1+\n 2)")
179 self.assertEqual(isp.indent_spaces, 4)
179 self.assertEqual(isp.indent_spaces, 4)
180
180
181 def test_dedent(self):
181 def test_dedent(self):
182 isp = self.isp # shorthand
182 isp = self.isp # shorthand
183 isp.push('if 1:')
183 isp.push('if 1:')
184 self.assertEqual(isp.indent_spaces, 4)
184 self.assertEqual(isp.indent_spaces, 4)
185 isp.push(' pass')
185 isp.push(' pass')
186 self.assertEqual(isp.indent_spaces, 0)
186 self.assertEqual(isp.indent_spaces, 0)
187
187
188 def test_push(self):
188 def test_push(self):
189 isp = self.isp
189 isp = self.isp
190 self.assertTrue(isp.push('x=1'))
190 self.assertTrue(isp.push('x=1'))
191
191
192 def test_push2(self):
192 def test_push2(self):
193 isp = self.isp
193 isp = self.isp
194 self.assertFalse(isp.push('if 1:'))
194 self.assertFalse(isp.push('if 1:'))
195 for line in [' x=1', '# a comment', ' y=2']:
195 for line in [' x=1', '# a comment', ' y=2']:
196 self.assertTrue(isp.push(line))
196 self.assertTrue(isp.push(line))
197
197
198 def test_push3(self):
198 def test_push3(self):
199 """Test input with leading whitespace"""
199 """Test input with leading whitespace"""
200 isp = self.isp
200 isp = self.isp
201 isp.push(' x=1')
201 isp.push(' x=1')
202 isp.push(' y=2')
202 isp.push(' y=2')
203 self.assertEqual(isp.source, 'if 1:\n x=1\n y=2\n')
203 self.assertEqual(isp.source, 'if 1:\n x=1\n y=2\n')
204
204
205 def test_replace_mode(self):
205 def test_replace_mode(self):
206 isp = self.isp
206 isp = self.isp
207 isp.input_mode = 'cell'
207 isp.input_mode = 'cell'
208 isp.push('x=1')
208 isp.push('x=1')
209 self.assertEqual(isp.source, 'x=1\n')
209 self.assertEqual(isp.source, 'x=1\n')
210 isp.push('x=2')
210 isp.push('x=2')
211 self.assertEqual(isp.source, 'x=2\n')
211 self.assertEqual(isp.source, 'x=2\n')
212
212
213 def test_push_accepts_more(self):
213 def test_push_accepts_more(self):
214 isp = self.isp
214 isp = self.isp
215 isp.push('x=1')
215 isp.push('x=1')
216 self.assertFalse(isp.push_accepts_more())
216 self.assertFalse(isp.push_accepts_more())
217
217
218 def test_push_accepts_more2(self):
218 def test_push_accepts_more2(self):
219 isp = self.isp
219 isp = self.isp
220 isp.push('if 1:')
220 isp.push('if 1:')
221 self.assertTrue(isp.push_accepts_more())
221 self.assertTrue(isp.push_accepts_more())
222 isp.push(' x=1')
222 isp.push(' x=1')
223 self.assertTrue(isp.push_accepts_more())
223 self.assertTrue(isp.push_accepts_more())
224 isp.push('')
224 isp.push('')
225 self.assertFalse(isp.push_accepts_more())
225 self.assertFalse(isp.push_accepts_more())
226
226
227 def test_push_accepts_more3(self):
227 def test_push_accepts_more3(self):
228 isp = self.isp
228 isp = self.isp
229 isp.push("x = (2+\n3)")
229 isp.push("x = (2+\n3)")
230 self.assertFalse(isp.push_accepts_more())
230 self.assertFalse(isp.push_accepts_more())
231
231
232 def test_push_accepts_more4(self):
232 def test_push_accepts_more4(self):
233 isp = self.isp
233 isp = self.isp
234 # When a multiline statement contains parens or multiline strings, we
234 # When a multiline statement contains parens or multiline strings, we
235 # shouldn't get confused.
235 # shouldn't get confused.
236 # FIXME: we should be able to better handle de-dents in statements like
236 # FIXME: we should be able to better handle de-dents in statements like
237 # multiline strings and multiline expressions (continued with \ or
237 # multiline strings and multiline expressions (continued with \ or
238 # parens). Right now we aren't handling the indentation tracking quite
238 # parens). Right now we aren't handling the indentation tracking quite
239 # correctly with this, though in practice it may not be too much of a
239 # correctly with this, though in practice it may not be too much of a
240 # problem. We'll need to see.
240 # problem. We'll need to see.
241 isp.push("if 1:")
241 isp.push("if 1:")
242 isp.push(" x = (2+")
242 isp.push(" x = (2+")
243 isp.push(" 3)")
243 isp.push(" 3)")
244 self.assertTrue(isp.push_accepts_more())
244 self.assertTrue(isp.push_accepts_more())
245 isp.push(" y = 3")
245 isp.push(" y = 3")
246 self.assertTrue(isp.push_accepts_more())
246 self.assertTrue(isp.push_accepts_more())
247 isp.push('')
247 isp.push('')
248 self.assertFalse(isp.push_accepts_more())
248 self.assertFalse(isp.push_accepts_more())
249
249
250 def test_continuation(self):
251 isp = self.isp
252 isp.push("import os, \\")
253 self.assertTrue(isp.push_accepts_more())
254 isp.push("sys")
255 self.assertFalse(isp.push_accepts_more())
256
250 def test_syntax_error(self):
257 def test_syntax_error(self):
251 isp = self.isp
258 isp = self.isp
252 # Syntax errors immediately produce a 'ready' block, so the invalid
259 # Syntax errors immediately produce a 'ready' block, so the invalid
253 # Python can be sent to the kernel for evaluation with possible ipython
260 # Python can be sent to the kernel for evaluation with possible ipython
254 # special-syntax conversion.
261 # special-syntax conversion.
255 isp.push('run foo')
262 isp.push('run foo')
256 self.assertFalse(isp.push_accepts_more())
263 self.assertFalse(isp.push_accepts_more())
257
264
258 def check_split(self, block_lines, compile=True):
265 def check_split(self, block_lines, compile=True):
259 blocks = assemble(block_lines)
266 blocks = assemble(block_lines)
260 lines = ''.join(blocks)
267 lines = ''.join(blocks)
261 oblock = self.isp.split_blocks(lines)
268 oblock = self.isp.split_blocks(lines)
262 self.assertEqual(oblock, blocks)
269 self.assertEqual(oblock, blocks)
263 if compile:
270 if compile:
264 for block in blocks:
271 for block in blocks:
265 self.isp._compile(block)
272 self.isp._compile(block)
266
273
267 def test_split(self):
274 def test_split(self):
268 # All blocks of input we want to test in a list. The format for each
275 # All blocks of input we want to test in a list. The format for each
269 # block is a list of lists, with each inner lists consisting of all the
276 # block is a list of lists, with each inner lists consisting of all the
270 # lines (as single-lines) that should make up a sub-block.
277 # lines (as single-lines) that should make up a sub-block.
271
278
272 # Note: do NOT put here sub-blocks that don't compile, as the
279 # Note: do NOT put here sub-blocks that don't compile, as the
273 # check_split() routine makes a final verification pass to check that
280 # check_split() routine makes a final verification pass to check that
274 # each sub_block, as returned by split_blocks(), does compile
281 # each sub_block, as returned by split_blocks(), does compile
275 # correctly.
282 # correctly.
276 all_blocks = [ [['x=1']],
283 all_blocks = [ [['x=1']],
277
284
278 [['x=1'],
285 [['x=1'],
279 ['y=2']],
286 ['y=2']],
280
287
281 [['x=1',
288 [['x=1',
282 '# a comment'],
289 '# a comment'],
283 ['y=11']],
290 ['y=11']],
284
291
285 [['if 1:',
292 [['if 1:',
286 ' x=1'],
293 ' x=1'],
287 ['y=3']],
294 ['y=3']],
288
295
289 [['def f(x):',
296 [['def f(x):',
290 ' return x'],
297 ' return x'],
291 ['x=1']],
298 ['x=1']],
292
299
293 [['def f(x):',
300 [['def f(x):',
294 ' x+=1',
301 ' x+=1',
295 ' ',
302 ' ',
296 ' return x'],
303 ' return x'],
297 ['x=1']],
304 ['x=1']],
298
305
299 [['def f(x):',
306 [['def f(x):',
300 ' if x>0:',
307 ' if x>0:',
301 ' y=1',
308 ' y=1',
302 ' # a comment',
309 ' # a comment',
303 ' else:',
310 ' else:',
304 ' y=4',
311 ' y=4',
305 ' ',
312 ' ',
306 ' return y'],
313 ' return y'],
307 ['x=1'],
314 ['x=1'],
308 ['if 1:',
315 ['if 1:',
309 ' y=11'] ],
316 ' y=11'] ],
310
317
311 [['for i in range(10):'
318 [['for i in range(10):'
312 ' x=i**2']],
319 ' x=i**2']],
313
320
314 [['for i in range(10):'
321 [['for i in range(10):'
315 ' x=i**2'],
322 ' x=i**2'],
316 ['z = 1']],
323 ['z = 1']],
317 ]
324 ]
318 for block_lines in all_blocks:
325 for block_lines in all_blocks:
319 self.check_split(block_lines)
326 self.check_split(block_lines)
320
327
321 def test_split_syntax_errors(self):
328 def test_split_syntax_errors(self):
322 # Block splitting with invalid syntax
329 # Block splitting with invalid syntax
323 all_blocks = [ [['a syntax error']],
330 all_blocks = [ [['a syntax error']],
324
331
325 [['x=1',
332 [['x=1',
326 'another syntax error']],
333 'another syntax error']],
327
334
328 [['for i in range(10):'
335 [['for i in range(10):'
329 ' yet another error']],
336 ' yet another error']],
330
337
331 ]
338 ]
332 for block_lines in all_blocks:
339 for block_lines in all_blocks:
333 self.check_split(block_lines, compile=False)
340 self.check_split(block_lines, compile=False)
334
341
335
342
336 class InteractiveLoopTestCase(unittest.TestCase):
343 class InteractiveLoopTestCase(unittest.TestCase):
337 """Tests for an interactive loop like a python shell.
344 """Tests for an interactive loop like a python shell.
338 """
345 """
339 def check_ns(self, lines, ns):
346 def check_ns(self, lines, ns):
340 """Validate that the given input lines produce the resulting namespace.
347 """Validate that the given input lines produce the resulting namespace.
341
348
342 Note: the input lines are given exactly as they would be typed in an
349 Note: the input lines are given exactly as they would be typed in an
343 auto-indenting environment, as mini_interactive_loop above already does
350 auto-indenting environment, as mini_interactive_loop above already does
344 auto-indenting and prepends spaces to the input.
351 auto-indenting and prepends spaces to the input.
345 """
352 """
346 src = mini_interactive_loop(pseudo_input(lines))
353 src = mini_interactive_loop(pseudo_input(lines))
347 test_ns = {}
354 test_ns = {}
348 exec src in test_ns
355 exec src in test_ns
349 # We can't check that the provided ns is identical to the test_ns,
356 # We can't check that the provided ns is identical to the test_ns,
350 # because Python fills test_ns with extra keys (copyright, etc). But
357 # because Python fills test_ns with extra keys (copyright, etc). But
351 # we can check that the given dict is *contained* in test_ns
358 # we can check that the given dict is *contained* in test_ns
352 for k,v in ns.items():
359 for k,v in ns.items():
353 self.assertEqual(test_ns[k], v)
360 self.assertEqual(test_ns[k], v)
354
361
355 def test_simple(self):
362 def test_simple(self):
356 self.check_ns(['x=1'], dict(x=1))
363 self.check_ns(['x=1'], dict(x=1))
357
364
358 def test_simple2(self):
365 def test_simple2(self):
359 self.check_ns(['if 1:', 'x=2'], dict(x=2))
366 self.check_ns(['if 1:', 'x=2'], dict(x=2))
360
367
361 def test_xy(self):
368 def test_xy(self):
362 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
369 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
363
370
364 def test_abc(self):
371 def test_abc(self):
365 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
372 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
366
373
367 def test_multi(self):
374 def test_multi(self):
368 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
375 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
369
376
370
377
371 def test_LineInfo():
378 def test_LineInfo():
372 """Simple test for LineInfo construction and str()"""
379 """Simple test for LineInfo construction and str()"""
373 linfo = isp.LineInfo(' %cd /home')
380 linfo = isp.LineInfo(' %cd /home')
374 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
381 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
375
382
376
383
377 def test_split_user_input():
384 def test_split_user_input():
378 """Unicode test - split_user_input already has good doctests"""
385 """Unicode test - split_user_input already has good doctests"""
379 line = u"PΓ©rez Fernando"
386 line = u"PΓ©rez Fernando"
380 parts = isp.split_user_input(line)
387 parts = isp.split_user_input(line)
381 parts_expected = (u'', u'', u'', line)
388 parts_expected = (u'', u'', u'', line)
382 nt.assert_equal(parts, parts_expected)
389 nt.assert_equal(parts, parts_expected)
383
390
384
391
385 # Transformer tests
392 # Transformer tests
386 def transform_checker(tests, func):
393 def transform_checker(tests, func):
387 """Utility to loop over test inputs"""
394 """Utility to loop over test inputs"""
388 for inp, tr in tests:
395 for inp, tr in tests:
389 nt.assert_equals(func(inp), tr)
396 nt.assert_equals(func(inp), tr)
390
397
391 # Data for all the syntax tests in the form of lists of pairs of
398 # Data for all the syntax tests in the form of lists of pairs of
392 # raw/transformed input. We store it here as a global dict so that we can use
399 # raw/transformed input. We store it here as a global dict so that we can use
393 # it both within single-function tests and also to validate the behavior of the
400 # it both within single-function tests and also to validate the behavior of the
394 # larger objects
401 # larger objects
395
402
396 syntax = \
403 syntax = \
397 dict(assign_system =
404 dict(assign_system =
398 [('a =! ls', 'a = get_ipython().getoutput("ls")'),
405 [('a =! ls', 'a = get_ipython().getoutput("ls")'),
399 ('b = !ls', 'b = get_ipython().getoutput("ls")'),
406 ('b = !ls', 'b = get_ipython().getoutput("ls")'),
400 ('x=1', 'x=1'), # normal input is unmodified
407 ('x=1', 'x=1'), # normal input is unmodified
401 (' ',' '), # blank lines are kept intact
408 (' ',' '), # blank lines are kept intact
402 ],
409 ],
403
410
404 assign_magic =
411 assign_magic =
405 [('a =% who', 'a = get_ipython().magic("who")'),
412 [('a =% who', 'a = get_ipython().magic("who")'),
406 ('b = %who', 'b = get_ipython().magic("who")'),
413 ('b = %who', 'b = get_ipython().magic("who")'),
407 ('x=1', 'x=1'), # normal input is unmodified
414 ('x=1', 'x=1'), # normal input is unmodified
408 (' ',' '), # blank lines are kept intact
415 (' ',' '), # blank lines are kept intact
409 ],
416 ],
410
417
411 classic_prompt =
418 classic_prompt =
412 [('>>> x=1', 'x=1'),
419 [('>>> x=1', 'x=1'),
413 ('x=1', 'x=1'), # normal input is unmodified
420 ('x=1', 'x=1'), # normal input is unmodified
414 (' ', ' '), # blank lines are kept intact
421 (' ', ' '), # blank lines are kept intact
415 ('... ', ''), # continuation prompts
422 ('... ', ''), # continuation prompts
416 ],
423 ],
417
424
418 ipy_prompt =
425 ipy_prompt =
419 [('In [1]: x=1', 'x=1'),
426 [('In [1]: x=1', 'x=1'),
420 ('x=1', 'x=1'), # normal input is unmodified
427 ('x=1', 'x=1'), # normal input is unmodified
421 (' ',' '), # blank lines are kept intact
428 (' ',' '), # blank lines are kept intact
422 (' ....: ', ''), # continuation prompts
429 (' ....: ', ''), # continuation prompts
423 ],
430 ],
424
431
425 # Tests for the escape transformer to leave normal code alone
432 # Tests for the escape transformer to leave normal code alone
426 escaped_noesc =
433 escaped_noesc =
427 [ (' ', ' '),
434 [ (' ', ' '),
428 ('x=1', 'x=1'),
435 ('x=1', 'x=1'),
429 ],
436 ],
430
437
431 # System calls
438 # System calls
432 escaped_shell =
439 escaped_shell =
433 [ ('!ls', 'get_ipython().system("ls")'),
440 [ ('!ls', 'get_ipython().system("ls")'),
434 # Double-escape shell, this means to capture the output of the
441 # Double-escape shell, this means to capture the output of the
435 # subprocess and return it
442 # subprocess and return it
436 ('!!ls', 'get_ipython().getoutput("ls")'),
443 ('!!ls', 'get_ipython().getoutput("ls")'),
437 ],
444 ],
438
445
439 # Help/object info
446 # Help/object info
440 escaped_help =
447 escaped_help =
441 [ ('?', 'get_ipython().show_usage()'),
448 [ ('?', 'get_ipython().show_usage()'),
442 ('?x1', 'get_ipython().magic("pinfo x1")'),
449 ('?x1', 'get_ipython().magic("pinfo x1")'),
443 ('??x2', 'get_ipython().magic("pinfo2 x2")'),
450 ('??x2', 'get_ipython().magic("pinfo2 x2")'),
444 ('x3?', 'get_ipython().magic("pinfo x3")'),
451 ('x3?', 'get_ipython().magic("pinfo x3")'),
445 ('x4??', 'get_ipython().magic("pinfo2 x4")'),
452 ('x4??', 'get_ipython().magic("pinfo2 x4")'),
446 ('%hist?', 'get_ipython().magic("pinfo %hist")'),
453 ('%hist?', 'get_ipython().magic("pinfo %hist")'),
447 ],
454 ],
448
455
449 # Explicit magic calls
456 # Explicit magic calls
450 escaped_magic =
457 escaped_magic =
451 [ ('%cd', 'get_ipython().magic("cd")'),
458 [ ('%cd', 'get_ipython().magic("cd")'),
452 ('%cd /home', 'get_ipython().magic("cd /home")'),
459 ('%cd /home', 'get_ipython().magic("cd /home")'),
453 (' %magic', ' get_ipython().magic("magic")'),
460 (' %magic', ' get_ipython().magic("magic")'),
454 ],
461 ],
455
462
456 # Quoting with separate arguments
463 # Quoting with separate arguments
457 escaped_quote =
464 escaped_quote =
458 [ (',f', 'f("")'),
465 [ (',f', 'f("")'),
459 (',f x', 'f("x")'),
466 (',f x', 'f("x")'),
460 (' ,f y', ' f("y")'),
467 (' ,f y', ' f("y")'),
461 (',f a b', 'f("a", "b")'),
468 (',f a b', 'f("a", "b")'),
462 ],
469 ],
463
470
464 # Quoting with single argument
471 # Quoting with single argument
465 escaped_quote2 =
472 escaped_quote2 =
466 [ (';f', 'f("")'),
473 [ (';f', 'f("")'),
467 (';f x', 'f("x")'),
474 (';f x', 'f("x")'),
468 (' ;f y', ' f("y")'),
475 (' ;f y', ' f("y")'),
469 (';f a b', 'f("a b")'),
476 (';f a b', 'f("a b")'),
470 ],
477 ],
471
478
472 # Simply apply parens
479 # Simply apply parens
473 escaped_paren =
480 escaped_paren =
474 [ ('/f', 'f()'),
481 [ ('/f', 'f()'),
475 ('/f x', 'f(x)'),
482 ('/f x', 'f(x)'),
476 (' /f y', ' f(y)'),
483 (' /f y', ' f(y)'),
477 ('/f a b', 'f(a, b)'),
484 ('/f a b', 'f(a, b)'),
478 ],
485 ],
479
486
480 )
487 )
481
488
482 # multiline syntax examples. Each of these should be a list of lists, with
489 # multiline syntax examples. Each of these should be a list of lists, with
483 # each entry itself having pairs of raw/transformed input. The union (with
490 # each entry itself having pairs of raw/transformed input. The union (with
484 # '\n'.join() of the transformed inputs is what the splitter should produce
491 # '\n'.join() of the transformed inputs is what the splitter should produce
485 # when fed the raw lines one at a time via push.
492 # when fed the raw lines one at a time via push.
486 syntax_ml = \
493 syntax_ml = \
487 dict(classic_prompt =
494 dict(classic_prompt =
488 [ [('>>> for i in range(10):','for i in range(10):'),
495 [ [('>>> for i in range(10):','for i in range(10):'),
489 ('... print i',' print i'),
496 ('... print i',' print i'),
490 ('... ', ''),
497 ('... ', ''),
491 ],
498 ],
492 ],
499 ],
493
500
494 ipy_prompt =
501 ipy_prompt =
495 [ [('In [24]: for i in range(10):','for i in range(10):'),
502 [ [('In [24]: for i in range(10):','for i in range(10):'),
496 (' ....: print i',' print i'),
503 (' ....: print i',' print i'),
497 (' ....: ', ''),
504 (' ....: ', ''),
498 ],
505 ],
499 ],
506 ],
500 )
507 )
501
508
502
509
503 def test_assign_system():
510 def test_assign_system():
504 transform_checker(syntax['assign_system'], isp.transform_assign_system)
511 transform_checker(syntax['assign_system'], isp.transform_assign_system)
505
512
506
513
507 def test_assign_magic():
514 def test_assign_magic():
508 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
515 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
509
516
510
517
511 def test_classic_prompt():
518 def test_classic_prompt():
512 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
519 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
513 for example in syntax_ml['classic_prompt']:
520 for example in syntax_ml['classic_prompt']:
514 transform_checker(example, isp.transform_classic_prompt)
521 transform_checker(example, isp.transform_classic_prompt)
515
522
516
523
517 def test_ipy_prompt():
524 def test_ipy_prompt():
518 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
525 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
519 for example in syntax_ml['ipy_prompt']:
526 for example in syntax_ml['ipy_prompt']:
520 transform_checker(example, isp.transform_ipy_prompt)
527 transform_checker(example, isp.transform_ipy_prompt)
521
528
522
529
523 def test_escaped_noesc():
530 def test_escaped_noesc():
524 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
531 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
525
532
526
533
527 def test_escaped_shell():
534 def test_escaped_shell():
528 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
535 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
529
536
530
537
531 def test_escaped_help():
538 def test_escaped_help():
532 transform_checker(syntax['escaped_help'], isp.transform_escaped)
539 transform_checker(syntax['escaped_help'], isp.transform_escaped)
533
540
534
541
535 def test_escaped_magic():
542 def test_escaped_magic():
536 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
543 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
537
544
538
545
539 def test_escaped_quote():
546 def test_escaped_quote():
540 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
547 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
541
548
542
549
543 def test_escaped_quote2():
550 def test_escaped_quote2():
544 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
551 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
545
552
546
553
547 def test_escaped_paren():
554 def test_escaped_paren():
548 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
555 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
549
556
550
557
551 class IPythonInputTestCase(InputSplitterTestCase):
558 class IPythonInputTestCase(InputSplitterTestCase):
552 """By just creating a new class whose .isp is a different instance, we
559 """By just creating a new class whose .isp is a different instance, we
553 re-run the same test battery on the new input splitter.
560 re-run the same test battery on the new input splitter.
554
561
555 In addition, this runs the tests over the syntax and syntax_ml dicts that
562 In addition, this runs the tests over the syntax and syntax_ml dicts that
556 were tested by individual functions, as part of the OO interface.
563 were tested by individual functions, as part of the OO interface.
557 """
564 """
558
565
559 def setUp(self):
566 def setUp(self):
560 self.isp = isp.IPythonInputSplitter(input_mode='line')
567 self.isp = isp.IPythonInputSplitter(input_mode='line')
561
568
562 def test_syntax(self):
569 def test_syntax(self):
563 """Call all single-line syntax tests from the main object"""
570 """Call all single-line syntax tests from the main object"""
564 isp = self.isp
571 isp = self.isp
565 for example in syntax.itervalues():
572 for example in syntax.itervalues():
566 for raw, out_t in example:
573 for raw, out_t in example:
567 if raw.startswith(' '):
574 if raw.startswith(' '):
568 continue
575 continue
569
576
570 isp.push(raw)
577 isp.push(raw)
571 out = isp.source_reset().rstrip()
578 out = isp.source_reset().rstrip()
572 self.assertEqual(out, out_t)
579 self.assertEqual(out, out_t)
573
580
574 def test_syntax_multiline(self):
581 def test_syntax_multiline(self):
575 isp = self.isp
582 isp = self.isp
576 for example in syntax_ml.itervalues():
583 for example in syntax_ml.itervalues():
577 out_t_parts = []
584 out_t_parts = []
578 for line_pairs in example:
585 for line_pairs in example:
579 for raw, out_t_part in line_pairs:
586 for raw, out_t_part in line_pairs:
580 isp.push(raw)
587 isp.push(raw)
581 out_t_parts.append(out_t_part)
588 out_t_parts.append(out_t_part)
582
589
583 out = isp.source_reset().rstrip()
590 out = isp.source_reset().rstrip()
584 out_t = '\n'.join(out_t_parts).rstrip()
591 out_t = '\n'.join(out_t_parts).rstrip()
585 self.assertEqual(out, out_t)
592 self.assertEqual(out, out_t)
586
593
587
594
588 class BlockIPythonInputTestCase(IPythonInputTestCase):
595 class BlockIPythonInputTestCase(IPythonInputTestCase):
589
596
590 # Deactivate tests that don't make sense for the block mode
597 # Deactivate tests that don't make sense for the block mode
591 test_push3 = test_split = lambda s: None
598 test_push3 = test_split = lambda s: None
592
599
593 def setUp(self):
600 def setUp(self):
594 self.isp = isp.IPythonInputSplitter(input_mode='cell')
601 self.isp = isp.IPythonInputSplitter(input_mode='cell')
595
602
596 def test_syntax_multiline(self):
603 def test_syntax_multiline(self):
597 isp = self.isp
604 isp = self.isp
598 for example in syntax_ml.itervalues():
605 for example in syntax_ml.itervalues():
599 raw_parts = []
606 raw_parts = []
600 out_t_parts = []
607 out_t_parts = []
601 for line_pairs in example:
608 for line_pairs in example:
602 for raw, out_t_part in line_pairs:
609 for raw, out_t_part in line_pairs:
603 raw_parts.append(raw)
610 raw_parts.append(raw)
604 out_t_parts.append(out_t_part)
611 out_t_parts.append(out_t_part)
605
612
606 raw = '\n'.join(raw_parts)
613 raw = '\n'.join(raw_parts)
607 out_t = '\n'.join(out_t_parts)
614 out_t = '\n'.join(out_t_parts)
608
615
609 isp.push(raw)
616 isp.push(raw)
610 out = isp.source_reset()
617 out = isp.source_reset()
611 # Match ignoring trailing whitespace
618 # Match ignoring trailing whitespace
612 self.assertEqual(out.rstrip(), out_t.rstrip())
619 self.assertEqual(out.rstrip(), out_t.rstrip())
613
620
614
621
615 #-----------------------------------------------------------------------------
622 #-----------------------------------------------------------------------------
616 # Main - use as a script, mostly for developer experiments
623 # Main - use as a script, mostly for developer experiments
617 #-----------------------------------------------------------------------------
624 #-----------------------------------------------------------------------------
618
625
619 if __name__ == '__main__':
626 if __name__ == '__main__':
620 # A simple demo for interactive experimentation. This code will not get
627 # A simple demo for interactive experimentation. This code will not get
621 # picked up by any test suite.
628 # picked up by any test suite.
622 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
629 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
623
630
624 # configure here the syntax to use, prompt and whether to autoindent
631 # configure here the syntax to use, prompt and whether to autoindent
625 #isp, start_prompt = InputSplitter(), '>>> '
632 #isp, start_prompt = InputSplitter(), '>>> '
626 isp, start_prompt = IPythonInputSplitter(), 'In> '
633 isp, start_prompt = IPythonInputSplitter(), 'In> '
627
634
628 autoindent = True
635 autoindent = True
629 #autoindent = False
636 #autoindent = False
630
637
631 try:
638 try:
632 while True:
639 while True:
633 prompt = start_prompt
640 prompt = start_prompt
634 while isp.push_accepts_more():
641 while isp.push_accepts_more():
635 indent = ' '*isp.indent_spaces
642 indent = ' '*isp.indent_spaces
636 if autoindent:
643 if autoindent:
637 line = indent + raw_input(prompt+indent)
644 line = indent + raw_input(prompt+indent)
638 else:
645 else:
639 line = raw_input(prompt)
646 line = raw_input(prompt)
640 isp.push(line)
647 isp.push(line)
641 prompt = '... '
648 prompt = '... '
642
649
643 # Here we just return input so we can use it in a test suite, but a
650 # Here we just return input so we can use it in a test suite, but a
644 # real interpreter would instead send it for execution somewhere.
651 # real interpreter would instead send it for execution somewhere.
645 #src = isp.source; raise EOFError # dbg
652 #src = isp.source; raise EOFError # dbg
646 src = isp.source_reset()
653 src = isp.source_reset()
647 print 'Input source was:\n', src
654 print 'Input source was:\n', src
648 except EOFError:
655 except EOFError:
649 print 'Bye'
656 print 'Bye'
General Comments 0
You need to be logged in to leave comments. Login now