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