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