##// END OF EJS Templates
Expose IPython machinery for testing code completeness
Thomas Kluyver -
Show More
@@ -1,638 +1,654 b''
1 """Input handling and transformation machinery.
1 """Input handling and transformation machinery.
2
2
3 The first class in this module, :class:`InputSplitter`, is designed to tell when
3 The first class in this module, :class:`InputSplitter`, is designed to tell when
4 input from a line-oriented frontend is complete and should be executed, and when
4 input from a line-oriented frontend is complete and should be executed, and when
5 the user should be prompted for another line of code instead. The name 'input
5 the user should be prompted for another line of code instead. The name 'input
6 splitter' is largely for historical reasons.
6 splitter' is largely for historical reasons.
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 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
10 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
11 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
12 and stores the results.
12 and stores the results.
13
13
14 For more details, see the class docstrings below.
14 For more details, see the class docstrings below.
15
15
16 Authors
16 Authors
17 -------
17 -------
18
18
19 * Fernando Perez
19 * Fernando Perez
20 * Brian Granger
20 * Brian Granger
21 * Thomas Kluyver
21 * Thomas Kluyver
22 """
22 """
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Copyright (C) 2010 The IPython Development Team
24 # Copyright (C) 2010 The IPython Development Team
25 #
25 #
26 # Distributed under the terms of the BSD License. The full license is in
26 # Distributed under the terms of the BSD License. The full license is in
27 # the file COPYING, distributed as part of this software.
27 # the file COPYING, distributed as part of this software.
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Imports
31 # Imports
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # stdlib
33 # stdlib
34 import ast
34 import ast
35 import codeop
35 import codeop
36 import re
36 import re
37 import sys
37 import sys
38
38
39 # IPython modules
39 # IPython modules
40 from IPython.utils.py3compat import cast_unicode
40 from IPython.utils.py3compat import cast_unicode
41 from IPython.core.inputtransformer import (leading_indent,
41 from IPython.core.inputtransformer import (leading_indent,
42 classic_prompt,
42 classic_prompt,
43 ipy_prompt,
43 ipy_prompt,
44 strip_encoding_cookie,
44 strip_encoding_cookie,
45 cellmagic,
45 cellmagic,
46 assemble_logical_lines,
46 assemble_logical_lines,
47 help_end,
47 help_end,
48 escaped_commands,
48 escaped_commands,
49 assign_from_magic,
49 assign_from_magic,
50 assign_from_system,
50 assign_from_system,
51 assemble_python_lines,
51 assemble_python_lines,
52 )
52 )
53
53
54 # These are available in this module for backwards compatibility.
54 # These are available in this module for backwards compatibility.
55 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
55 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
56 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
56 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
57 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
57 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Utilities
60 # Utilities
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63 # FIXME: These are general-purpose utilities that later can be moved to the
63 # FIXME: These are general-purpose utilities that later can be moved to the
64 # general ward. Kept here for now because we're being very strict about test
64 # general ward. Kept here for now because we're being very strict about test
65 # coverage with this code, and this lets us ensure that we keep 100% coverage
65 # coverage with this code, and this lets us ensure that we keep 100% coverage
66 # while developing.
66 # while developing.
67
67
68 # compiled regexps for autoindent management
68 # compiled regexps for autoindent management
69 dedent_re = re.compile('|'.join([
69 dedent_re = re.compile('|'.join([
70 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
70 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
71 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
71 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
72 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
72 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
73 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
73 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
74 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
74 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
75 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
75 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
76 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
76 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
77 ]))
77 ]))
78 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
78 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
79
79
80 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
80 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
81 # before pure comments
81 # before pure comments
82 comment_line_re = re.compile('^\s*\#')
82 comment_line_re = re.compile('^\s*\#')
83
83
84
84
85 def num_ini_spaces(s):
85 def num_ini_spaces(s):
86 """Return the number of initial spaces in a string.
86 """Return the number of initial spaces in a string.
87
87
88 Note that tabs are counted as a single space. For now, we do *not* support
88 Note that tabs are counted as a single space. For now, we do *not* support
89 mixing of tabs and spaces in the user's input.
89 mixing of tabs and spaces in the user's input.
90
90
91 Parameters
91 Parameters
92 ----------
92 ----------
93 s : string
93 s : string
94
94
95 Returns
95 Returns
96 -------
96 -------
97 n : int
97 n : int
98 """
98 """
99
99
100 ini_spaces = ini_spaces_re.match(s)
100 ini_spaces = ini_spaces_re.match(s)
101 if ini_spaces:
101 if ini_spaces:
102 return ini_spaces.end()
102 return ini_spaces.end()
103 else:
103 else:
104 return 0
104 return 0
105
105
106 def last_blank(src):
106 def last_blank(src):
107 """Determine if the input source ends in a blank.
107 """Determine if the input source ends in a blank.
108
108
109 A blank is either a newline or a line consisting of whitespace.
109 A blank is either a newline or a line consisting of whitespace.
110
110
111 Parameters
111 Parameters
112 ----------
112 ----------
113 src : string
113 src : string
114 A single or multiline string.
114 A single or multiline string.
115 """
115 """
116 if not src: return False
116 if not src: return False
117 ll = src.splitlines()[-1]
117 ll = src.splitlines()[-1]
118 return (ll == '') or ll.isspace()
118 return (ll == '') or ll.isspace()
119
119
120
120
121 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
121 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
122 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
122 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
123
123
124 def last_two_blanks(src):
124 def last_two_blanks(src):
125 """Determine if the input source ends in two blanks.
125 """Determine if the input source ends in two blanks.
126
126
127 A blank is either a newline or a line consisting of whitespace.
127 A blank is either a newline or a line consisting of whitespace.
128
128
129 Parameters
129 Parameters
130 ----------
130 ----------
131 src : string
131 src : string
132 A single or multiline string.
132 A single or multiline string.
133 """
133 """
134 if not src: return False
134 if not src: return False
135 # The logic here is tricky: I couldn't get a regexp to work and pass all
135 # The logic here is tricky: I couldn't get a regexp to work and pass all
136 # the tests, so I took a different approach: split the source by lines,
136 # the tests, so I took a different approach: split the source by lines,
137 # grab the last two and prepend '###\n' as a stand-in for whatever was in
137 # grab the last two and prepend '###\n' as a stand-in for whatever was in
138 # the body before the last two lines. Then, with that structure, it's
138 # the body before the last two lines. Then, with that structure, it's
139 # possible to analyze with two regexps. Not the most elegant solution, but
139 # possible to analyze with two regexps. Not the most elegant solution, but
140 # it works. If anyone tries to change this logic, make sure to validate
140 # it works. If anyone tries to change this logic, make sure to validate
141 # the whole test suite first!
141 # the whole test suite first!
142 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
142 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
143 return (bool(last_two_blanks_re.match(new_src)) or
143 return (bool(last_two_blanks_re.match(new_src)) or
144 bool(last_two_blanks_re2.match(new_src)) )
144 bool(last_two_blanks_re2.match(new_src)) )
145
145
146
146
147 def remove_comments(src):
147 def remove_comments(src):
148 """Remove all comments from input source.
148 """Remove all comments from input source.
149
149
150 Note: comments are NOT recognized inside of strings!
150 Note: comments are NOT recognized inside of strings!
151
151
152 Parameters
152 Parameters
153 ----------
153 ----------
154 src : string
154 src : string
155 A single or multiline input string.
155 A single or multiline input string.
156
156
157 Returns
157 Returns
158 -------
158 -------
159 String with all Python comments removed.
159 String with all Python comments removed.
160 """
160 """
161
161
162 return re.sub('#.*', '', src)
162 return re.sub('#.*', '', src)
163
163
164
164
165 def get_input_encoding():
165 def get_input_encoding():
166 """Return the default standard input encoding.
166 """Return the default standard input encoding.
167
167
168 If sys.stdin has no encoding, 'ascii' is returned."""
168 If sys.stdin has no encoding, 'ascii' is returned."""
169 # There are strange environments for which sys.stdin.encoding is None. We
169 # There are strange environments for which sys.stdin.encoding is None. We
170 # ensure that a valid encoding is returned.
170 # ensure that a valid encoding is returned.
171 encoding = getattr(sys.stdin, 'encoding', None)
171 encoding = getattr(sys.stdin, 'encoding', None)
172 if encoding is None:
172 if encoding is None:
173 encoding = 'ascii'
173 encoding = 'ascii'
174 return encoding
174 return encoding
175
175
176 #-----------------------------------------------------------------------------
176 #-----------------------------------------------------------------------------
177 # Classes and functions for normal Python syntax handling
177 # Classes and functions for normal Python syntax handling
178 #-----------------------------------------------------------------------------
178 #-----------------------------------------------------------------------------
179
179
180 class InputSplitter(object):
180 class InputSplitter(object):
181 r"""An object that can accumulate lines of Python source before execution.
181 r"""An object that can accumulate lines of Python source before execution.
182
182
183 This object is designed to be fed python source line-by-line, using
183 This object is designed to be fed python source line-by-line, using
184 :meth:`push`. It will return on each push whether the currently pushed
184 :meth:`push`. It will return on each push whether the currently pushed
185 code could be executed already. In addition, it provides a method called
185 code could be executed already. In addition, it provides a method called
186 :meth:`push_accepts_more` that can be used to query whether more input
186 :meth:`push_accepts_more` that can be used to query whether more input
187 can be pushed into a single interactive block.
187 can be pushed into a single interactive block.
188
188
189 This is a simple example of how an interactive terminal-based client can use
189 This is a simple example of how an interactive terminal-based client can use
190 this tool::
190 this tool::
191
191
192 isp = InputSplitter()
192 isp = InputSplitter()
193 while isp.push_accepts_more():
193 while isp.push_accepts_more():
194 indent = ' '*isp.indent_spaces
194 indent = ' '*isp.indent_spaces
195 prompt = '>>> ' + indent
195 prompt = '>>> ' + indent
196 line = indent + raw_input(prompt)
196 line = indent + raw_input(prompt)
197 isp.push(line)
197 isp.push(line)
198 print 'Input source was:\n', isp.source_reset(),
198 print 'Input source was:\n', isp.source_reset(),
199 """
199 """
200 # Number of spaces of indentation computed from input that has been pushed
200 # Number of spaces of indentation computed from input that has been pushed
201 # so far. This is the attributes callers should query to get the current
201 # so far. This is the attributes callers should query to get the current
202 # indentation level, in order to provide auto-indent facilities.
202 # indentation level, in order to provide auto-indent facilities.
203 indent_spaces = 0
203 indent_spaces = 0
204 # String, indicating the default input encoding. It is computed by default
204 # String, indicating the default input encoding. It is computed by default
205 # at initialization time via get_input_encoding(), but it can be reset by a
205 # at initialization time via get_input_encoding(), but it can be reset by a
206 # client with specific knowledge of the encoding.
206 # client with specific knowledge of the encoding.
207 encoding = ''
207 encoding = ''
208 # String where the current full source input is stored, properly encoded.
208 # String where the current full source input is stored, properly encoded.
209 # Reading this attribute is the normal way of querying the currently pushed
209 # Reading this attribute is the normal way of querying the currently pushed
210 # source code, that has been properly encoded.
210 # source code, that has been properly encoded.
211 source = ''
211 source = ''
212 # Code object corresponding to the current source. It is automatically
212 # Code object corresponding to the current source. It is automatically
213 # synced to the source, so it can be queried at any time to obtain the code
213 # synced to the source, so it can be queried at any time to obtain the code
214 # object; it will be None if the source doesn't compile to valid Python.
214 # object; it will be None if the source doesn't compile to valid Python.
215 code = None
215 code = None
216
216
217 # Private attributes
217 # Private attributes
218
218
219 # List with lines of input accumulated so far
219 # List with lines of input accumulated so far
220 _buffer = None
220 _buffer = None
221 # Command compiler
221 # Command compiler
222 _compile = None
222 _compile = None
223 # Mark when input has changed indentation all the way back to flush-left
223 # Mark when input has changed indentation all the way back to flush-left
224 _full_dedent = False
224 _full_dedent = False
225 # Boolean indicating whether the current block is complete
225 # Boolean indicating whether the current block is complete
226 _is_complete = None
226 _is_complete = None
227
227
228 def __init__(self):
228 def __init__(self):
229 """Create a new InputSplitter instance.
229 """Create a new InputSplitter instance.
230 """
230 """
231 self._buffer = []
231 self._buffer = []
232 self._compile = codeop.CommandCompiler()
232 self._compile = codeop.CommandCompiler()
233 self.encoding = get_input_encoding()
233 self.encoding = get_input_encoding()
234
234
235 def reset(self):
235 def reset(self):
236 """Reset the input buffer and associated state."""
236 """Reset the input buffer and associated state."""
237 self.indent_spaces = 0
237 self.indent_spaces = 0
238 self._buffer[:] = []
238 self._buffer[:] = []
239 self.source = ''
239 self.source = ''
240 self.code = None
240 self.code = None
241 self._is_complete = False
241 self._is_complete = False
242 self._full_dedent = False
242 self._full_dedent = False
243
243
244 def source_reset(self):
244 def source_reset(self):
245 """Return the input source and perform a full reset.
245 """Return the input source and perform a full reset.
246 """
246 """
247 out = self.source
247 out = self.source
248 self.reset()
248 self.reset()
249 return out
249 return out
250
250
251 def is_complete(self, source):
252 """Return whether a block of code is ready to execute, or should be continued
253
254 This is a non-stateful API, and will reset the state of this InputSplitter.
255 """
256 self.reset()
257 try:
258 self.push(source)
259 return not self.push_accepts_more()
260 except SyntaxError:
261 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 # which push() will not catch.
263 return True
264 finally:
265 self.reset()
266
251 def push(self, lines):
267 def push(self, lines):
252 """Push one or more lines of input.
268 """Push one or more lines of input.
253
269
254 This stores the given lines and returns a status code indicating
270 This stores the given lines and returns a status code indicating
255 whether the code forms a complete Python block or not.
271 whether the code forms a complete Python block or not.
256
272
257 Any exceptions generated in compilation are swallowed, but if an
273 Any exceptions generated in compilation are swallowed, but if an
258 exception was produced, the method returns True.
274 exception was produced, the method returns True.
259
275
260 Parameters
276 Parameters
261 ----------
277 ----------
262 lines : string
278 lines : string
263 One or more lines of Python input.
279 One or more lines of Python input.
264
280
265 Returns
281 Returns
266 -------
282 -------
267 is_complete : boolean
283 is_complete : boolean
268 True if the current input source (the result of the current input
284 True if the current input source (the result of the current input
269 plus prior inputs) forms a complete Python execution block. Note that
285 plus prior inputs) forms a complete Python execution block. Note that
270 this value is also stored as a private attribute (``_is_complete``), so it
286 this value is also stored as a private attribute (``_is_complete``), so it
271 can be queried at any time.
287 can be queried at any time.
272 """
288 """
273 self._store(lines)
289 self._store(lines)
274 source = self.source
290 source = self.source
275
291
276 # Before calling _compile(), reset the code object to None so that if an
292 # Before calling _compile(), reset the code object to None so that if an
277 # exception is raised in compilation, we don't mislead by having
293 # exception is raised in compilation, we don't mislead by having
278 # inconsistent code/source attributes.
294 # inconsistent code/source attributes.
279 self.code, self._is_complete = None, None
295 self.code, self._is_complete = None, None
280
296
281 # Honor termination lines properly
297 # Honor termination lines properly
282 if source.endswith('\\\n'):
298 if source.endswith('\\\n'):
283 return False
299 return False
284
300
285 self._update_indent(lines)
301 self._update_indent(lines)
286 try:
302 try:
287 self.code = self._compile(source, symbol="exec")
303 self.code = self._compile(source, symbol="exec")
288 # Invalid syntax can produce any of a number of different errors from
304 # Invalid syntax can produce any of a number of different errors from
289 # inside the compiler, so we have to catch them all. Syntax errors
305 # inside the compiler, so we have to catch them all. Syntax errors
290 # immediately produce a 'ready' block, so the invalid Python can be
306 # immediately produce a 'ready' block, so the invalid Python can be
291 # sent to the kernel for evaluation with possible ipython
307 # sent to the kernel for evaluation with possible ipython
292 # special-syntax conversion.
308 # special-syntax conversion.
293 except (SyntaxError, OverflowError, ValueError, TypeError,
309 except (SyntaxError, OverflowError, ValueError, TypeError,
294 MemoryError):
310 MemoryError):
295 self._is_complete = True
311 self._is_complete = True
296 else:
312 else:
297 # Compilation didn't produce any exceptions (though it may not have
313 # Compilation didn't produce any exceptions (though it may not have
298 # given a complete code object)
314 # given a complete code object)
299 self._is_complete = self.code is not None
315 self._is_complete = self.code is not None
300
316
301 return self._is_complete
317 return self._is_complete
302
318
303 def push_accepts_more(self):
319 def push_accepts_more(self):
304 """Return whether a block of interactive input can accept more input.
320 """Return whether a block of interactive input can accept more input.
305
321
306 This method is meant to be used by line-oriented frontends, who need to
322 This method is meant to be used by line-oriented frontends, who need to
307 guess whether a block is complete or not based solely on prior and
323 guess whether a block is complete or not based solely on prior and
308 current input lines. The InputSplitter considers it has a complete
324 current input lines. The InputSplitter considers it has a complete
309 interactive block and will not accept more input when either:
325 interactive block and will not accept more input when either:
310
326
311 * A SyntaxError is raised
327 * A SyntaxError is raised
312
328
313 * The code is complete and consists of a single line or a single
329 * The code is complete and consists of a single line or a single
314 non-compound statement
330 non-compound statement
315
331
316 * The code is complete and has a blank line at the end
332 * The code is complete and has a blank line at the end
317
333
318 If the current input produces a syntax error, this method immediately
334 If the current input produces a syntax error, this method immediately
319 returns False but does *not* raise the syntax error exception, as
335 returns False but does *not* raise the syntax error exception, as
320 typically clients will want to send invalid syntax to an execution
336 typically clients will want to send invalid syntax to an execution
321 backend which might convert the invalid syntax into valid Python via
337 backend which might convert the invalid syntax into valid Python via
322 one of the dynamic IPython mechanisms.
338 one of the dynamic IPython mechanisms.
323 """
339 """
324
340
325 # With incomplete input, unconditionally accept more
341 # With incomplete input, unconditionally accept more
326 # A syntax error also sets _is_complete to True - see push()
342 # A syntax error also sets _is_complete to True - see push()
327 if not self._is_complete:
343 if not self._is_complete:
328 #print("Not complete") # debug
344 #print("Not complete") # debug
329 return True
345 return True
330
346
331 # The user can make any (complete) input execute by leaving a blank line
347 # The user can make any (complete) input execute by leaving a blank line
332 last_line = self.source.splitlines()[-1]
348 last_line = self.source.splitlines()[-1]
333 if (not last_line) or last_line.isspace():
349 if (not last_line) or last_line.isspace():
334 #print("Blank line") # debug
350 #print("Blank line") # debug
335 return False
351 return False
336
352
337 # If there's just a single line or AST node, and we're flush left, as is
353 # If there's just a single line or AST node, and we're flush left, as is
338 # the case after a simple statement such as 'a=1', we want to execute it
354 # the case after a simple statement such as 'a=1', we want to execute it
339 # straight away.
355 # straight away.
340 if self.indent_spaces==0:
356 if self.indent_spaces==0:
341 if len(self.source.splitlines()) <= 1:
357 if len(self.source.splitlines()) <= 1:
342 return False
358 return False
343
359
344 try:
360 try:
345 code_ast = ast.parse(u''.join(self._buffer))
361 code_ast = ast.parse(u''.join(self._buffer))
346 except Exception:
362 except Exception:
347 #print("Can't parse AST") # debug
363 #print("Can't parse AST") # debug
348 return False
364 return False
349 else:
365 else:
350 if len(code_ast.body) == 1 and \
366 if len(code_ast.body) == 1 and \
351 not hasattr(code_ast.body[0], 'body'):
367 not hasattr(code_ast.body[0], 'body'):
352 #print("Simple statement") # debug
368 #print("Simple statement") # debug
353 return False
369 return False
354
370
355 # General fallback - accept more code
371 # General fallback - accept more code
356 return True
372 return True
357
373
358 #------------------------------------------------------------------------
374 #------------------------------------------------------------------------
359 # Private interface
375 # Private interface
360 #------------------------------------------------------------------------
376 #------------------------------------------------------------------------
361
377
362 def _find_indent(self, line):
378 def _find_indent(self, line):
363 """Compute the new indentation level for a single line.
379 """Compute the new indentation level for a single line.
364
380
365 Parameters
381 Parameters
366 ----------
382 ----------
367 line : str
383 line : str
368 A single new line of non-whitespace, non-comment Python input.
384 A single new line of non-whitespace, non-comment Python input.
369
385
370 Returns
386 Returns
371 -------
387 -------
372 indent_spaces : int
388 indent_spaces : int
373 New value for the indent level (it may be equal to self.indent_spaces
389 New value for the indent level (it may be equal to self.indent_spaces
374 if indentation doesn't change.
390 if indentation doesn't change.
375
391
376 full_dedent : boolean
392 full_dedent : boolean
377 Whether the new line causes a full flush-left dedent.
393 Whether the new line causes a full flush-left dedent.
378 """
394 """
379 indent_spaces = self.indent_spaces
395 indent_spaces = self.indent_spaces
380 full_dedent = self._full_dedent
396 full_dedent = self._full_dedent
381
397
382 inisp = num_ini_spaces(line)
398 inisp = num_ini_spaces(line)
383 if inisp < indent_spaces:
399 if inisp < indent_spaces:
384 indent_spaces = inisp
400 indent_spaces = inisp
385 if indent_spaces <= 0:
401 if indent_spaces <= 0:
386 #print 'Full dedent in text',self.source # dbg
402 #print 'Full dedent in text',self.source # dbg
387 full_dedent = True
403 full_dedent = True
388
404
389 if line.rstrip()[-1] == ':':
405 if line.rstrip()[-1] == ':':
390 indent_spaces += 4
406 indent_spaces += 4
391 elif dedent_re.match(line):
407 elif dedent_re.match(line):
392 indent_spaces -= 4
408 indent_spaces -= 4
393 if indent_spaces <= 0:
409 if indent_spaces <= 0:
394 full_dedent = True
410 full_dedent = True
395
411
396 # Safety
412 # Safety
397 if indent_spaces < 0:
413 if indent_spaces < 0:
398 indent_spaces = 0
414 indent_spaces = 0
399 #print 'safety' # dbg
415 #print 'safety' # dbg
400
416
401 return indent_spaces, full_dedent
417 return indent_spaces, full_dedent
402
418
403 def _update_indent(self, lines):
419 def _update_indent(self, lines):
404 for line in remove_comments(lines).splitlines():
420 for line in remove_comments(lines).splitlines():
405 if line and not line.isspace():
421 if line and not line.isspace():
406 self.indent_spaces, self._full_dedent = self._find_indent(line)
422 self.indent_spaces, self._full_dedent = self._find_indent(line)
407
423
408 def _store(self, lines, buffer=None, store='source'):
424 def _store(self, lines, buffer=None, store='source'):
409 """Store one or more lines of input.
425 """Store one or more lines of input.
410
426
411 If input lines are not newline-terminated, a newline is automatically
427 If input lines are not newline-terminated, a newline is automatically
412 appended."""
428 appended."""
413
429
414 if buffer is None:
430 if buffer is None:
415 buffer = self._buffer
431 buffer = self._buffer
416
432
417 if lines.endswith('\n'):
433 if lines.endswith('\n'):
418 buffer.append(lines)
434 buffer.append(lines)
419 else:
435 else:
420 buffer.append(lines+'\n')
436 buffer.append(lines+'\n')
421 setattr(self, store, self._set_source(buffer))
437 setattr(self, store, self._set_source(buffer))
422
438
423 def _set_source(self, buffer):
439 def _set_source(self, buffer):
424 return u''.join(buffer)
440 return u''.join(buffer)
425
441
426
442
427 class IPythonInputSplitter(InputSplitter):
443 class IPythonInputSplitter(InputSplitter):
428 """An input splitter that recognizes all of IPython's special syntax."""
444 """An input splitter that recognizes all of IPython's special syntax."""
429
445
430 # String with raw, untransformed input.
446 # String with raw, untransformed input.
431 source_raw = ''
447 source_raw = ''
432
448
433 # Flag to track when a transformer has stored input that it hasn't given
449 # Flag to track when a transformer has stored input that it hasn't given
434 # back yet.
450 # back yet.
435 transformer_accumulating = False
451 transformer_accumulating = False
436
452
437 # Flag to track when assemble_python_lines has stored input that it hasn't
453 # Flag to track when assemble_python_lines has stored input that it hasn't
438 # given back yet.
454 # given back yet.
439 within_python_line = False
455 within_python_line = False
440
456
441 # Private attributes
457 # Private attributes
442
458
443 # List with lines of raw input accumulated so far.
459 # List with lines of raw input accumulated so far.
444 _buffer_raw = None
460 _buffer_raw = None
445
461
446 def __init__(self, line_input_checker=True, physical_line_transforms=None,
462 def __init__(self, line_input_checker=True, physical_line_transforms=None,
447 logical_line_transforms=None, python_line_transforms=None):
463 logical_line_transforms=None, python_line_transforms=None):
448 super(IPythonInputSplitter, self).__init__()
464 super(IPythonInputSplitter, self).__init__()
449 self._buffer_raw = []
465 self._buffer_raw = []
450 self._validate = True
466 self._validate = True
451
467
452 if physical_line_transforms is not None:
468 if physical_line_transforms is not None:
453 self.physical_line_transforms = physical_line_transforms
469 self.physical_line_transforms = physical_line_transforms
454 else:
470 else:
455 self.physical_line_transforms = [
471 self.physical_line_transforms = [
456 leading_indent(),
472 leading_indent(),
457 classic_prompt(),
473 classic_prompt(),
458 ipy_prompt(),
474 ipy_prompt(),
459 strip_encoding_cookie(),
475 strip_encoding_cookie(),
460 cellmagic(end_on_blank_line=line_input_checker),
476 cellmagic(end_on_blank_line=line_input_checker),
461 ]
477 ]
462
478
463 self.assemble_logical_lines = assemble_logical_lines()
479 self.assemble_logical_lines = assemble_logical_lines()
464 if logical_line_transforms is not None:
480 if logical_line_transforms is not None:
465 self.logical_line_transforms = logical_line_transforms
481 self.logical_line_transforms = logical_line_transforms
466 else:
482 else:
467 self.logical_line_transforms = [
483 self.logical_line_transforms = [
468 help_end(),
484 help_end(),
469 escaped_commands(),
485 escaped_commands(),
470 assign_from_magic(),
486 assign_from_magic(),
471 assign_from_system(),
487 assign_from_system(),
472 ]
488 ]
473
489
474 self.assemble_python_lines = assemble_python_lines()
490 self.assemble_python_lines = assemble_python_lines()
475 if python_line_transforms is not None:
491 if python_line_transforms is not None:
476 self.python_line_transforms = python_line_transforms
492 self.python_line_transforms = python_line_transforms
477 else:
493 else:
478 # We don't use any of these at present
494 # We don't use any of these at present
479 self.python_line_transforms = []
495 self.python_line_transforms = []
480
496
481 @property
497 @property
482 def transforms(self):
498 def transforms(self):
483 "Quick access to all transformers."
499 "Quick access to all transformers."
484 return self.physical_line_transforms + \
500 return self.physical_line_transforms + \
485 [self.assemble_logical_lines] + self.logical_line_transforms + \
501 [self.assemble_logical_lines] + self.logical_line_transforms + \
486 [self.assemble_python_lines] + self.python_line_transforms
502 [self.assemble_python_lines] + self.python_line_transforms
487
503
488 @property
504 @property
489 def transforms_in_use(self):
505 def transforms_in_use(self):
490 """Transformers, excluding logical line transformers if we're in a
506 """Transformers, excluding logical line transformers if we're in a
491 Python line."""
507 Python line."""
492 t = self.physical_line_transforms[:]
508 t = self.physical_line_transforms[:]
493 if not self.within_python_line:
509 if not self.within_python_line:
494 t += [self.assemble_logical_lines] + self.logical_line_transforms
510 t += [self.assemble_logical_lines] + self.logical_line_transforms
495 return t + [self.assemble_python_lines] + self.python_line_transforms
511 return t + [self.assemble_python_lines] + self.python_line_transforms
496
512
497 def reset(self):
513 def reset(self):
498 """Reset the input buffer and associated state."""
514 """Reset the input buffer and associated state."""
499 super(IPythonInputSplitter, self).reset()
515 super(IPythonInputSplitter, self).reset()
500 self._buffer_raw[:] = []
516 self._buffer_raw[:] = []
501 self.source_raw = ''
517 self.source_raw = ''
502 self.transformer_accumulating = False
518 self.transformer_accumulating = False
503 self.within_python_line = False
519 self.within_python_line = False
504
520
505 for t in self.transforms:
521 for t in self.transforms:
506 try:
522 try:
507 t.reset()
523 t.reset()
508 except SyntaxError:
524 except SyntaxError:
509 # Nothing that calls reset() expects to handle transformer
525 # Nothing that calls reset() expects to handle transformer
510 # errors
526 # errors
511 pass
527 pass
512
528
513 def flush_transformers(self):
529 def flush_transformers(self):
514 def _flush(transform, out):
530 def _flush(transform, out):
515 if out is not None:
531 if out is not None:
516 tmp = transform.push(out)
532 tmp = transform.push(out)
517 return tmp or transform.reset() or None
533 return tmp or transform.reset() or None
518 else:
534 else:
519 return transform.reset() or None
535 return transform.reset() or None
520
536
521 out = None
537 out = None
522 for t in self.transforms_in_use:
538 for t in self.transforms_in_use:
523 out = _flush(t, out)
539 out = _flush(t, out)
524
540
525 if out is not None:
541 if out is not None:
526 self._store(out)
542 self._store(out)
527
543
528 def raw_reset(self):
544 def raw_reset(self):
529 """Return raw input only and perform a full reset.
545 """Return raw input only and perform a full reset.
530 """
546 """
531 out = self.source_raw
547 out = self.source_raw
532 self.reset()
548 self.reset()
533 return out
549 return out
534
550
535 def source_reset(self):
551 def source_reset(self):
536 try:
552 try:
537 self.flush_transformers()
553 self.flush_transformers()
538 return self.source
554 return self.source
539 finally:
555 finally:
540 self.reset()
556 self.reset()
541
557
542 def push_accepts_more(self):
558 def push_accepts_more(self):
543 if self.transformer_accumulating:
559 if self.transformer_accumulating:
544 return True
560 return True
545 else:
561 else:
546 return super(IPythonInputSplitter, self).push_accepts_more()
562 return super(IPythonInputSplitter, self).push_accepts_more()
547
563
548 def transform_cell(self, cell):
564 def transform_cell(self, cell):
549 """Process and translate a cell of input.
565 """Process and translate a cell of input.
550 """
566 """
551 self.reset()
567 self.reset()
552 try:
568 try:
553 self.push(cell)
569 self.push(cell)
554 self.flush_transformers()
570 self.flush_transformers()
555 return self.source
571 return self.source
556 finally:
572 finally:
557 self.reset()
573 self.reset()
558
574
559 def push(self, lines):
575 def push(self, lines):
560 """Push one or more lines of IPython input.
576 """Push one or more lines of IPython input.
561
577
562 This stores the given lines and returns a status code indicating
578 This stores the given lines and returns a status code indicating
563 whether the code forms a complete Python block or not, after processing
579 whether the code forms a complete Python block or not, after processing
564 all input lines for special IPython syntax.
580 all input lines for special IPython syntax.
565
581
566 Any exceptions generated in compilation are swallowed, but if an
582 Any exceptions generated in compilation are swallowed, but if an
567 exception was produced, the method returns True.
583 exception was produced, the method returns True.
568
584
569 Parameters
585 Parameters
570 ----------
586 ----------
571 lines : string
587 lines : string
572 One or more lines of Python input.
588 One or more lines of Python input.
573
589
574 Returns
590 Returns
575 -------
591 -------
576 is_complete : boolean
592 is_complete : boolean
577 True if the current input source (the result of the current input
593 True if the current input source (the result of the current input
578 plus prior inputs) forms a complete Python execution block. Note that
594 plus prior inputs) forms a complete Python execution block. Note that
579 this value is also stored as a private attribute (_is_complete), so it
595 this value is also stored as a private attribute (_is_complete), so it
580 can be queried at any time.
596 can be queried at any time.
581 """
597 """
582
598
583 # We must ensure all input is pure unicode
599 # We must ensure all input is pure unicode
584 lines = cast_unicode(lines, self.encoding)
600 lines = cast_unicode(lines, self.encoding)
585
601
586 # ''.splitlines() --> [], but we need to push the empty line to transformers
602 # ''.splitlines() --> [], but we need to push the empty line to transformers
587 lines_list = lines.splitlines()
603 lines_list = lines.splitlines()
588 if not lines_list:
604 if not lines_list:
589 lines_list = ['']
605 lines_list = ['']
590
606
591 # Store raw source before applying any transformations to it. Note
607 # Store raw source before applying any transformations to it. Note
592 # that this must be done *after* the reset() call that would otherwise
608 # that this must be done *after* the reset() call that would otherwise
593 # flush the buffer.
609 # flush the buffer.
594 self._store(lines, self._buffer_raw, 'source_raw')
610 self._store(lines, self._buffer_raw, 'source_raw')
595
611
596 for line in lines_list:
612 for line in lines_list:
597 out = self.push_line(line)
613 out = self.push_line(line)
598
614
599 return out
615 return out
600
616
601 def push_line(self, line):
617 def push_line(self, line):
602 buf = self._buffer
618 buf = self._buffer
603
619
604 def _accumulating(dbg):
620 def _accumulating(dbg):
605 #print(dbg)
621 #print(dbg)
606 self.transformer_accumulating = True
622 self.transformer_accumulating = True
607 return False
623 return False
608
624
609 for transformer in self.physical_line_transforms:
625 for transformer in self.physical_line_transforms:
610 line = transformer.push(line)
626 line = transformer.push(line)
611 if line is None:
627 if line is None:
612 return _accumulating(transformer)
628 return _accumulating(transformer)
613
629
614 if not self.within_python_line:
630 if not self.within_python_line:
615 line = self.assemble_logical_lines.push(line)
631 line = self.assemble_logical_lines.push(line)
616 if line is None:
632 if line is None:
617 return _accumulating('acc logical line')
633 return _accumulating('acc logical line')
618
634
619 for transformer in self.logical_line_transforms:
635 for transformer in self.logical_line_transforms:
620 line = transformer.push(line)
636 line = transformer.push(line)
621 if line is None:
637 if line is None:
622 return _accumulating(transformer)
638 return _accumulating(transformer)
623
639
624 line = self.assemble_python_lines.push(line)
640 line = self.assemble_python_lines.push(line)
625 if line is None:
641 if line is None:
626 self.within_python_line = True
642 self.within_python_line = True
627 return _accumulating('acc python line')
643 return _accumulating('acc python line')
628 else:
644 else:
629 self.within_python_line = False
645 self.within_python_line = False
630
646
631 for transformer in self.python_line_transforms:
647 for transformer in self.python_line_transforms:
632 line = transformer.push(line)
648 line = transformer.push(line)
633 if line is None:
649 if line is None:
634 return _accumulating(transformer)
650 return _accumulating(transformer)
635
651
636 #print("transformers clear") #debug
652 #print("transformers clear") #debug
637 self.transformer_accumulating = False
653 self.transformer_accumulating = False
638 return super(IPythonInputSplitter, self).push(line)
654 return super(IPythonInputSplitter, self).push(line)
@@ -1,585 +1,592 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Fernando Perez
6 * Fernando Perez
7 * Robert Kern
7 * Robert Kern
8 """
8 """
9 from __future__ import print_function
9 from __future__ import print_function
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010-2011 The IPython Development Team
11 # Copyright (C) 2010-2011 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # stdlib
20 # stdlib
21 import unittest
21 import unittest
22 import sys
22 import sys
23
23
24 # Third party
24 # Third party
25 import nose.tools as nt
25 import nose.tools as nt
26
26
27 # Our own
27 # Our own
28 from IPython.core import inputsplitter as isp
28 from IPython.core import inputsplitter as isp
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
30 from IPython.testing import tools as tt
30 from IPython.testing import tools as tt
31 from IPython.utils import py3compat
31 from IPython.utils import py3compat
32 from IPython.utils.py3compat import string_types, input
32 from IPython.utils.py3compat import string_types, input
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Semi-complete examples (also used as tests)
35 # Semi-complete examples (also used as tests)
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 # Note: at the bottom, there's a slightly more complete version of this that
38 # Note: at the bottom, there's a slightly more complete version of this that
39 # can be useful during development of code here.
39 # can be useful during development of code here.
40
40
41 def mini_interactive_loop(input_func):
41 def mini_interactive_loop(input_func):
42 """Minimal example of the logic of an interactive interpreter loop.
42 """Minimal example of the logic of an interactive interpreter loop.
43
43
44 This serves as an example, and it is used by the test system with a fake
44 This serves as an example, and it is used by the test system with a fake
45 raw_input that simulates interactive input."""
45 raw_input that simulates interactive input."""
46
46
47 from IPython.core.inputsplitter import InputSplitter
47 from IPython.core.inputsplitter import InputSplitter
48
48
49 isp = InputSplitter()
49 isp = InputSplitter()
50 # In practice, this input loop would be wrapped in an outside loop to read
50 # In practice, this input loop would be wrapped in an outside loop to read
51 # input indefinitely, until some exit/quit command was issued. Here we
51 # input indefinitely, until some exit/quit command was issued. Here we
52 # only illustrate the basic inner loop.
52 # only illustrate the basic inner loop.
53 while isp.push_accepts_more():
53 while isp.push_accepts_more():
54 indent = ' '*isp.indent_spaces
54 indent = ' '*isp.indent_spaces
55 prompt = '>>> ' + indent
55 prompt = '>>> ' + indent
56 line = indent + input_func(prompt)
56 line = indent + input_func(prompt)
57 isp.push(line)
57 isp.push(line)
58
58
59 # Here we just return input so we can use it in a test suite, but a real
59 # Here we just return input so we can use it in a test suite, but a real
60 # interpreter would instead send it for execution somewhere.
60 # interpreter would instead send it for execution somewhere.
61 src = isp.source_reset()
61 src = isp.source_reset()
62 #print 'Input source was:\n', src # dbg
62 #print 'Input source was:\n', src # dbg
63 return src
63 return src
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Test utilities, just for local use
66 # Test utilities, just for local use
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 def assemble(block):
69 def assemble(block):
70 """Assemble a block into multi-line sub-blocks."""
70 """Assemble a block into multi-line sub-blocks."""
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
72
72
73
73
74 def pseudo_input(lines):
74 def pseudo_input(lines):
75 """Return a function that acts like raw_input but feeds the input list."""
75 """Return a function that acts like raw_input but feeds the input list."""
76 ilines = iter(lines)
76 ilines = iter(lines)
77 def raw_in(prompt):
77 def raw_in(prompt):
78 try:
78 try:
79 return next(ilines)
79 return next(ilines)
80 except StopIteration:
80 except StopIteration:
81 return ''
81 return ''
82 return raw_in
82 return raw_in
83
83
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85 # Tests
85 # Tests
86 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
87 def test_spaces():
87 def test_spaces():
88 tests = [('', 0),
88 tests = [('', 0),
89 (' ', 1),
89 (' ', 1),
90 ('\n', 0),
90 ('\n', 0),
91 (' \n', 1),
91 (' \n', 1),
92 ('x', 0),
92 ('x', 0),
93 (' x', 1),
93 (' x', 1),
94 (' x',2),
94 (' x',2),
95 (' x',4),
95 (' x',4),
96 # Note: tabs are counted as a single whitespace!
96 # Note: tabs are counted as a single whitespace!
97 ('\tx', 1),
97 ('\tx', 1),
98 ('\t x', 2),
98 ('\t x', 2),
99 ]
99 ]
100 tt.check_pairs(isp.num_ini_spaces, tests)
100 tt.check_pairs(isp.num_ini_spaces, tests)
101
101
102
102
103 def test_remove_comments():
103 def test_remove_comments():
104 tests = [('text', 'text'),
104 tests = [('text', 'text'),
105 ('text # comment', 'text '),
105 ('text # comment', 'text '),
106 ('text # comment\n', 'text \n'),
106 ('text # comment\n', 'text \n'),
107 ('text # comment \n', 'text \n'),
107 ('text # comment \n', 'text \n'),
108 ('line # c \nline\n','line \nline\n'),
108 ('line # c \nline\n','line \nline\n'),
109 ('line # c \nline#c2 \nline\nline #c\n\n',
109 ('line # c \nline#c2 \nline\nline #c\n\n',
110 'line \nline\nline\nline \n\n'),
110 'line \nline\nline\nline \n\n'),
111 ]
111 ]
112 tt.check_pairs(isp.remove_comments, tests)
112 tt.check_pairs(isp.remove_comments, tests)
113
113
114
114
115 def test_get_input_encoding():
115 def test_get_input_encoding():
116 encoding = isp.get_input_encoding()
116 encoding = isp.get_input_encoding()
117 nt.assert_true(isinstance(encoding, string_types))
117 nt.assert_true(isinstance(encoding, string_types))
118 # simple-minded check that at least encoding a simple string works with the
118 # simple-minded check that at least encoding a simple string works with the
119 # encoding we got.
119 # encoding we got.
120 nt.assert_equal(u'test'.encode(encoding), b'test')
120 nt.assert_equal(u'test'.encode(encoding), b'test')
121
121
122
122
123 class NoInputEncodingTestCase(unittest.TestCase):
123 class NoInputEncodingTestCase(unittest.TestCase):
124 def setUp(self):
124 def setUp(self):
125 self.old_stdin = sys.stdin
125 self.old_stdin = sys.stdin
126 class X: pass
126 class X: pass
127 fake_stdin = X()
127 fake_stdin = X()
128 sys.stdin = fake_stdin
128 sys.stdin = fake_stdin
129
129
130 def test(self):
130 def test(self):
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
132 # thing
132 # thing
133 enc = isp.get_input_encoding()
133 enc = isp.get_input_encoding()
134 self.assertEqual(enc, 'ascii')
134 self.assertEqual(enc, 'ascii')
135
135
136 def tearDown(self):
136 def tearDown(self):
137 sys.stdin = self.old_stdin
137 sys.stdin = self.old_stdin
138
138
139
139
140 class InputSplitterTestCase(unittest.TestCase):
140 class InputSplitterTestCase(unittest.TestCase):
141 def setUp(self):
141 def setUp(self):
142 self.isp = isp.InputSplitter()
142 self.isp = isp.InputSplitter()
143
143
144 def test_reset(self):
144 def test_reset(self):
145 isp = self.isp
145 isp = self.isp
146 isp.push('x=1')
146 isp.push('x=1')
147 isp.reset()
147 isp.reset()
148 self.assertEqual(isp._buffer, [])
148 self.assertEqual(isp._buffer, [])
149 self.assertEqual(isp.indent_spaces, 0)
149 self.assertEqual(isp.indent_spaces, 0)
150 self.assertEqual(isp.source, '')
150 self.assertEqual(isp.source, '')
151 self.assertEqual(isp.code, None)
151 self.assertEqual(isp.code, None)
152 self.assertEqual(isp._is_complete, False)
152 self.assertEqual(isp._is_complete, False)
153
153
154 def test_source(self):
154 def test_source(self):
155 self.isp._store('1')
155 self.isp._store('1')
156 self.isp._store('2')
156 self.isp._store('2')
157 self.assertEqual(self.isp.source, '1\n2\n')
157 self.assertEqual(self.isp.source, '1\n2\n')
158 self.assertTrue(len(self.isp._buffer)>0)
158 self.assertTrue(len(self.isp._buffer)>0)
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
160 self.assertEqual(self.isp._buffer, [])
160 self.assertEqual(self.isp._buffer, [])
161 self.assertEqual(self.isp.source, '')
161 self.assertEqual(self.isp.source, '')
162
162
163 def test_indent(self):
163 def test_indent(self):
164 isp = self.isp # shorthand
164 isp = self.isp # shorthand
165 isp.push('x=1')
165 isp.push('x=1')
166 self.assertEqual(isp.indent_spaces, 0)
166 self.assertEqual(isp.indent_spaces, 0)
167 isp.push('if 1:\n x=1')
167 isp.push('if 1:\n x=1')
168 self.assertEqual(isp.indent_spaces, 4)
168 self.assertEqual(isp.indent_spaces, 4)
169 isp.push('y=2\n')
169 isp.push('y=2\n')
170 self.assertEqual(isp.indent_spaces, 0)
170 self.assertEqual(isp.indent_spaces, 0)
171
171
172 def test_indent2(self):
172 def test_indent2(self):
173 isp = self.isp
173 isp = self.isp
174 isp.push('if 1:')
174 isp.push('if 1:')
175 self.assertEqual(isp.indent_spaces, 4)
175 self.assertEqual(isp.indent_spaces, 4)
176 isp.push(' x=1')
176 isp.push(' x=1')
177 self.assertEqual(isp.indent_spaces, 4)
177 self.assertEqual(isp.indent_spaces, 4)
178 # Blank lines shouldn't change the indent level
178 # Blank lines shouldn't change the indent level
179 isp.push(' '*2)
179 isp.push(' '*2)
180 self.assertEqual(isp.indent_spaces, 4)
180 self.assertEqual(isp.indent_spaces, 4)
181
181
182 def test_indent3(self):
182 def test_indent3(self):
183 isp = self.isp
183 isp = self.isp
184 # When a multiline statement contains parens or multiline strings, we
184 # When a multiline statement contains parens or multiline strings, we
185 # shouldn't get confused.
185 # shouldn't get confused.
186 isp.push("if 1:")
186 isp.push("if 1:")
187 isp.push(" x = (1+\n 2)")
187 isp.push(" x = (1+\n 2)")
188 self.assertEqual(isp.indent_spaces, 4)
188 self.assertEqual(isp.indent_spaces, 4)
189
189
190 def test_indent4(self):
190 def test_indent4(self):
191 isp = self.isp
191 isp = self.isp
192 # whitespace after ':' should not screw up indent level
192 # whitespace after ':' should not screw up indent level
193 isp.push('if 1: \n x=1')
193 isp.push('if 1: \n x=1')
194 self.assertEqual(isp.indent_spaces, 4)
194 self.assertEqual(isp.indent_spaces, 4)
195 isp.push('y=2\n')
195 isp.push('y=2\n')
196 self.assertEqual(isp.indent_spaces, 0)
196 self.assertEqual(isp.indent_spaces, 0)
197 isp.push('if 1:\t\n x=1')
197 isp.push('if 1:\t\n x=1')
198 self.assertEqual(isp.indent_spaces, 4)
198 self.assertEqual(isp.indent_spaces, 4)
199 isp.push('y=2\n')
199 isp.push('y=2\n')
200 self.assertEqual(isp.indent_spaces, 0)
200 self.assertEqual(isp.indent_spaces, 0)
201
201
202 def test_dedent_pass(self):
202 def test_dedent_pass(self):
203 isp = self.isp # shorthand
203 isp = self.isp # shorthand
204 # should NOT cause dedent
204 # should NOT cause dedent
205 isp.push('if 1:\n passes = 5')
205 isp.push('if 1:\n passes = 5')
206 self.assertEqual(isp.indent_spaces, 4)
206 self.assertEqual(isp.indent_spaces, 4)
207 isp.push('if 1:\n pass')
207 isp.push('if 1:\n pass')
208 self.assertEqual(isp.indent_spaces, 0)
208 self.assertEqual(isp.indent_spaces, 0)
209 isp.push('if 1:\n pass ')
209 isp.push('if 1:\n pass ')
210 self.assertEqual(isp.indent_spaces, 0)
210 self.assertEqual(isp.indent_spaces, 0)
211
211
212 def test_dedent_break(self):
212 def test_dedent_break(self):
213 isp = self.isp # shorthand
213 isp = self.isp # shorthand
214 # should NOT cause dedent
214 # should NOT cause dedent
215 isp.push('while 1:\n breaks = 5')
215 isp.push('while 1:\n breaks = 5')
216 self.assertEqual(isp.indent_spaces, 4)
216 self.assertEqual(isp.indent_spaces, 4)
217 isp.push('while 1:\n break')
217 isp.push('while 1:\n break')
218 self.assertEqual(isp.indent_spaces, 0)
218 self.assertEqual(isp.indent_spaces, 0)
219 isp.push('while 1:\n break ')
219 isp.push('while 1:\n break ')
220 self.assertEqual(isp.indent_spaces, 0)
220 self.assertEqual(isp.indent_spaces, 0)
221
221
222 def test_dedent_continue(self):
222 def test_dedent_continue(self):
223 isp = self.isp # shorthand
223 isp = self.isp # shorthand
224 # should NOT cause dedent
224 # should NOT cause dedent
225 isp.push('while 1:\n continues = 5')
225 isp.push('while 1:\n continues = 5')
226 self.assertEqual(isp.indent_spaces, 4)
226 self.assertEqual(isp.indent_spaces, 4)
227 isp.push('while 1:\n continue')
227 isp.push('while 1:\n continue')
228 self.assertEqual(isp.indent_spaces, 0)
228 self.assertEqual(isp.indent_spaces, 0)
229 isp.push('while 1:\n continue ')
229 isp.push('while 1:\n continue ')
230 self.assertEqual(isp.indent_spaces, 0)
230 self.assertEqual(isp.indent_spaces, 0)
231
231
232 def test_dedent_raise(self):
232 def test_dedent_raise(self):
233 isp = self.isp # shorthand
233 isp = self.isp # shorthand
234 # should NOT cause dedent
234 # should NOT cause dedent
235 isp.push('if 1:\n raised = 4')
235 isp.push('if 1:\n raised = 4')
236 self.assertEqual(isp.indent_spaces, 4)
236 self.assertEqual(isp.indent_spaces, 4)
237 isp.push('if 1:\n raise TypeError()')
237 isp.push('if 1:\n raise TypeError()')
238 self.assertEqual(isp.indent_spaces, 0)
238 self.assertEqual(isp.indent_spaces, 0)
239 isp.push('if 1:\n raise')
239 isp.push('if 1:\n raise')
240 self.assertEqual(isp.indent_spaces, 0)
240 self.assertEqual(isp.indent_spaces, 0)
241 isp.push('if 1:\n raise ')
241 isp.push('if 1:\n raise ')
242 self.assertEqual(isp.indent_spaces, 0)
242 self.assertEqual(isp.indent_spaces, 0)
243
243
244 def test_dedent_return(self):
244 def test_dedent_return(self):
245 isp = self.isp # shorthand
245 isp = self.isp # shorthand
246 # should NOT cause dedent
246 # should NOT cause dedent
247 isp.push('if 1:\n returning = 4')
247 isp.push('if 1:\n returning = 4')
248 self.assertEqual(isp.indent_spaces, 4)
248 self.assertEqual(isp.indent_spaces, 4)
249 isp.push('if 1:\n return 5 + 493')
249 isp.push('if 1:\n return 5 + 493')
250 self.assertEqual(isp.indent_spaces, 0)
250 self.assertEqual(isp.indent_spaces, 0)
251 isp.push('if 1:\n return')
251 isp.push('if 1:\n return')
252 self.assertEqual(isp.indent_spaces, 0)
252 self.assertEqual(isp.indent_spaces, 0)
253 isp.push('if 1:\n return ')
253 isp.push('if 1:\n return ')
254 self.assertEqual(isp.indent_spaces, 0)
254 self.assertEqual(isp.indent_spaces, 0)
255 isp.push('if 1:\n return(0)')
255 isp.push('if 1:\n return(0)')
256 self.assertEqual(isp.indent_spaces, 0)
256 self.assertEqual(isp.indent_spaces, 0)
257
257
258 def test_push(self):
258 def test_push(self):
259 isp = self.isp
259 isp = self.isp
260 self.assertTrue(isp.push('x=1'))
260 self.assertTrue(isp.push('x=1'))
261
261
262 def test_push2(self):
262 def test_push2(self):
263 isp = self.isp
263 isp = self.isp
264 self.assertFalse(isp.push('if 1:'))
264 self.assertFalse(isp.push('if 1:'))
265 for line in [' x=1', '# a comment', ' y=2']:
265 for line in [' x=1', '# a comment', ' y=2']:
266 print(line)
266 print(line)
267 self.assertTrue(isp.push(line))
267 self.assertTrue(isp.push(line))
268
268
269 def test_push3(self):
269 def test_push3(self):
270 isp = self.isp
270 isp = self.isp
271 isp.push('if True:')
271 isp.push('if True:')
272 isp.push(' a = 1')
272 isp.push(' a = 1')
273 self.assertFalse(isp.push('b = [1,'))
273 self.assertFalse(isp.push('b = [1,'))
274
274
275 def test_push_accepts_more(self):
275 def test_push_accepts_more(self):
276 isp = self.isp
276 isp = self.isp
277 isp.push('x=1')
277 isp.push('x=1')
278 self.assertFalse(isp.push_accepts_more())
278 self.assertFalse(isp.push_accepts_more())
279
279
280 def test_push_accepts_more2(self):
280 def test_push_accepts_more2(self):
281 isp = self.isp
281 isp = self.isp
282 isp.push('if 1:')
282 isp.push('if 1:')
283 self.assertTrue(isp.push_accepts_more())
283 self.assertTrue(isp.push_accepts_more())
284 isp.push(' x=1')
284 isp.push(' x=1')
285 self.assertTrue(isp.push_accepts_more())
285 self.assertTrue(isp.push_accepts_more())
286 isp.push('')
286 isp.push('')
287 self.assertFalse(isp.push_accepts_more())
287 self.assertFalse(isp.push_accepts_more())
288
288
289 def test_push_accepts_more3(self):
289 def test_push_accepts_more3(self):
290 isp = self.isp
290 isp = self.isp
291 isp.push("x = (2+\n3)")
291 isp.push("x = (2+\n3)")
292 self.assertFalse(isp.push_accepts_more())
292 self.assertFalse(isp.push_accepts_more())
293
293
294 def test_push_accepts_more4(self):
294 def test_push_accepts_more4(self):
295 isp = self.isp
295 isp = self.isp
296 # When a multiline statement contains parens or multiline strings, we
296 # When a multiline statement contains parens or multiline strings, we
297 # shouldn't get confused.
297 # shouldn't get confused.
298 # FIXME: we should be able to better handle de-dents in statements like
298 # FIXME: we should be able to better handle de-dents in statements like
299 # multiline strings and multiline expressions (continued with \ or
299 # multiline strings and multiline expressions (continued with \ or
300 # parens). Right now we aren't handling the indentation tracking quite
300 # parens). Right now we aren't handling the indentation tracking quite
301 # correctly with this, though in practice it may not be too much of a
301 # correctly with this, though in practice it may not be too much of a
302 # problem. We'll need to see.
302 # problem. We'll need to see.
303 isp.push("if 1:")
303 isp.push("if 1:")
304 isp.push(" x = (2+")
304 isp.push(" x = (2+")
305 isp.push(" 3)")
305 isp.push(" 3)")
306 self.assertTrue(isp.push_accepts_more())
306 self.assertTrue(isp.push_accepts_more())
307 isp.push(" y = 3")
307 isp.push(" y = 3")
308 self.assertTrue(isp.push_accepts_more())
308 self.assertTrue(isp.push_accepts_more())
309 isp.push('')
309 isp.push('')
310 self.assertFalse(isp.push_accepts_more())
310 self.assertFalse(isp.push_accepts_more())
311
311
312 def test_push_accepts_more5(self):
312 def test_push_accepts_more5(self):
313 isp = self.isp
313 isp = self.isp
314 isp.push('try:')
314 isp.push('try:')
315 isp.push(' a = 5')
315 isp.push(' a = 5')
316 isp.push('except:')
316 isp.push('except:')
317 isp.push(' raise')
317 isp.push(' raise')
318 # We want to be able to add an else: block at this point, so it should
318 # We want to be able to add an else: block at this point, so it should
319 # wait for a blank line.
319 # wait for a blank line.
320 self.assertTrue(isp.push_accepts_more())
320 self.assertTrue(isp.push_accepts_more())
321
321
322 def test_continuation(self):
322 def test_continuation(self):
323 isp = self.isp
323 isp = self.isp
324 isp.push("import os, \\")
324 isp.push("import os, \\")
325 self.assertTrue(isp.push_accepts_more())
325 self.assertTrue(isp.push_accepts_more())
326 isp.push("sys")
326 isp.push("sys")
327 self.assertFalse(isp.push_accepts_more())
327 self.assertFalse(isp.push_accepts_more())
328
328
329 def test_syntax_error(self):
329 def test_syntax_error(self):
330 isp = self.isp
330 isp = self.isp
331 # Syntax errors immediately produce a 'ready' block, so the invalid
331 # Syntax errors immediately produce a 'ready' block, so the invalid
332 # Python can be sent to the kernel for evaluation with possible ipython
332 # Python can be sent to the kernel for evaluation with possible ipython
333 # special-syntax conversion.
333 # special-syntax conversion.
334 isp.push('run foo')
334 isp.push('run foo')
335 self.assertFalse(isp.push_accepts_more())
335 self.assertFalse(isp.push_accepts_more())
336
336
337 def test_unicode(self):
337 def test_unicode(self):
338 self.isp.push(u"PΓ©rez")
338 self.isp.push(u"PΓ©rez")
339 self.isp.push(u'\xc3\xa9')
339 self.isp.push(u'\xc3\xa9')
340 self.isp.push(u"u'\xc3\xa9'")
340 self.isp.push(u"u'\xc3\xa9'")
341
341
342 def test_line_continuation(self):
342 def test_line_continuation(self):
343 """ Test issue #2108."""
343 """ Test issue #2108."""
344 isp = self.isp
344 isp = self.isp
345 # A blank line after a line continuation should not accept more
345 # A blank line after a line continuation should not accept more
346 isp.push("1 \\\n\n")
346 isp.push("1 \\\n\n")
347 self.assertFalse(isp.push_accepts_more())
347 self.assertFalse(isp.push_accepts_more())
348 # Whitespace after a \ is a SyntaxError. The only way to test that
348 # Whitespace after a \ is a SyntaxError. The only way to test that
349 # here is to test that push doesn't accept more (as with
349 # here is to test that push doesn't accept more (as with
350 # test_syntax_error() above).
350 # test_syntax_error() above).
351 isp.push(r"1 \ ")
351 isp.push(r"1 \ ")
352 self.assertFalse(isp.push_accepts_more())
352 self.assertFalse(isp.push_accepts_more())
353 # Even if the line is continuable (c.f. the regular Python
353 # Even if the line is continuable (c.f. the regular Python
354 # interpreter)
354 # interpreter)
355 isp.push(r"(1 \ ")
355 isp.push(r"(1 \ ")
356 self.assertFalse(isp.push_accepts_more())
356 self.assertFalse(isp.push_accepts_more())
357
357
358 def test_is_complete(self):
359 isp = self.isp
360 assert isp.is_complete("a = 1")
361 assert not isp.is_complete("for a in range(5):")
362 assert isp.is_complete("raise = 2") # SyntaxError should mean complete
363 assert not isp.is_complete("a = [1,\n2,")
364
358 class InteractiveLoopTestCase(unittest.TestCase):
365 class InteractiveLoopTestCase(unittest.TestCase):
359 """Tests for an interactive loop like a python shell.
366 """Tests for an interactive loop like a python shell.
360 """
367 """
361 def check_ns(self, lines, ns):
368 def check_ns(self, lines, ns):
362 """Validate that the given input lines produce the resulting namespace.
369 """Validate that the given input lines produce the resulting namespace.
363
370
364 Note: the input lines are given exactly as they would be typed in an
371 Note: the input lines are given exactly as they would be typed in an
365 auto-indenting environment, as mini_interactive_loop above already does
372 auto-indenting environment, as mini_interactive_loop above already does
366 auto-indenting and prepends spaces to the input.
373 auto-indenting and prepends spaces to the input.
367 """
374 """
368 src = mini_interactive_loop(pseudo_input(lines))
375 src = mini_interactive_loop(pseudo_input(lines))
369 test_ns = {}
376 test_ns = {}
370 exec(src, test_ns)
377 exec(src, test_ns)
371 # We can't check that the provided ns is identical to the test_ns,
378 # We can't check that the provided ns is identical to the test_ns,
372 # because Python fills test_ns with extra keys (copyright, etc). But
379 # because Python fills test_ns with extra keys (copyright, etc). But
373 # we can check that the given dict is *contained* in test_ns
380 # we can check that the given dict is *contained* in test_ns
374 for k,v in ns.items():
381 for k,v in ns.items():
375 self.assertEqual(test_ns[k], v)
382 self.assertEqual(test_ns[k], v)
376
383
377 def test_simple(self):
384 def test_simple(self):
378 self.check_ns(['x=1'], dict(x=1))
385 self.check_ns(['x=1'], dict(x=1))
379
386
380 def test_simple2(self):
387 def test_simple2(self):
381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
388 self.check_ns(['if 1:', 'x=2'], dict(x=2))
382
389
383 def test_xy(self):
390 def test_xy(self):
384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
391 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
385
392
386 def test_abc(self):
393 def test_abc(self):
387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
394 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
388
395
389 def test_multi(self):
396 def test_multi(self):
390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
397 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
391
398
392
399
393 class IPythonInputTestCase(InputSplitterTestCase):
400 class IPythonInputTestCase(InputSplitterTestCase):
394 """By just creating a new class whose .isp is a different instance, we
401 """By just creating a new class whose .isp is a different instance, we
395 re-run the same test battery on the new input splitter.
402 re-run the same test battery on the new input splitter.
396
403
397 In addition, this runs the tests over the syntax and syntax_ml dicts that
404 In addition, this runs the tests over the syntax and syntax_ml dicts that
398 were tested by individual functions, as part of the OO interface.
405 were tested by individual functions, as part of the OO interface.
399
406
400 It also makes some checks on the raw buffer storage.
407 It also makes some checks on the raw buffer storage.
401 """
408 """
402
409
403 def setUp(self):
410 def setUp(self):
404 self.isp = isp.IPythonInputSplitter()
411 self.isp = isp.IPythonInputSplitter()
405
412
406 def test_syntax(self):
413 def test_syntax(self):
407 """Call all single-line syntax tests from the main object"""
414 """Call all single-line syntax tests from the main object"""
408 isp = self.isp
415 isp = self.isp
409 for example in syntax.values():
416 for example in syntax.values():
410 for raw, out_t in example:
417 for raw, out_t in example:
411 if raw.startswith(' '):
418 if raw.startswith(' '):
412 continue
419 continue
413
420
414 isp.push(raw+'\n')
421 isp.push(raw+'\n')
415 out_raw = isp.source_raw
422 out_raw = isp.source_raw
416 out = isp.source_reset()
423 out = isp.source_reset()
417 self.assertEqual(out.rstrip(), out_t,
424 self.assertEqual(out.rstrip(), out_t,
418 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
425 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
419 self.assertEqual(out_raw.rstrip(), raw.rstrip())
426 self.assertEqual(out_raw.rstrip(), raw.rstrip())
420
427
421 def test_syntax_multiline(self):
428 def test_syntax_multiline(self):
422 isp = self.isp
429 isp = self.isp
423 for example in syntax_ml.values():
430 for example in syntax_ml.values():
424 for line_pairs in example:
431 for line_pairs in example:
425 out_t_parts = []
432 out_t_parts = []
426 raw_parts = []
433 raw_parts = []
427 for lraw, out_t_part in line_pairs:
434 for lraw, out_t_part in line_pairs:
428 if out_t_part is not None:
435 if out_t_part is not None:
429 out_t_parts.append(out_t_part)
436 out_t_parts.append(out_t_part)
430
437
431 if lraw is not None:
438 if lraw is not None:
432 isp.push(lraw)
439 isp.push(lraw)
433 raw_parts.append(lraw)
440 raw_parts.append(lraw)
434
441
435 out_raw = isp.source_raw
442 out_raw = isp.source_raw
436 out = isp.source_reset()
443 out = isp.source_reset()
437 out_t = '\n'.join(out_t_parts).rstrip()
444 out_t = '\n'.join(out_t_parts).rstrip()
438 raw = '\n'.join(raw_parts).rstrip()
445 raw = '\n'.join(raw_parts).rstrip()
439 self.assertEqual(out.rstrip(), out_t)
446 self.assertEqual(out.rstrip(), out_t)
440 self.assertEqual(out_raw.rstrip(), raw)
447 self.assertEqual(out_raw.rstrip(), raw)
441
448
442 def test_syntax_multiline_cell(self):
449 def test_syntax_multiline_cell(self):
443 isp = self.isp
450 isp = self.isp
444 for example in syntax_ml.values():
451 for example in syntax_ml.values():
445
452
446 out_t_parts = []
453 out_t_parts = []
447 for line_pairs in example:
454 for line_pairs in example:
448 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
455 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
449 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
456 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
450 out = isp.transform_cell(raw)
457 out = isp.transform_cell(raw)
451 # Match ignoring trailing whitespace
458 # Match ignoring trailing whitespace
452 self.assertEqual(out.rstrip(), out_t.rstrip())
459 self.assertEqual(out.rstrip(), out_t.rstrip())
453
460
454 def test_cellmagic_preempt(self):
461 def test_cellmagic_preempt(self):
455 isp = self.isp
462 isp = self.isp
456 for raw, name, line, cell in [
463 for raw, name, line, cell in [
457 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
464 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
458 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
465 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
459 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
466 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
460 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
467 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
461 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
468 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
462 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
469 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
463 ]:
470 ]:
464 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
471 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
465 name, line, cell
472 name, line, cell
466 )
473 )
467 out = isp.transform_cell(raw)
474 out = isp.transform_cell(raw)
468 self.assertEqual(out.rstrip(), expected.rstrip())
475 self.assertEqual(out.rstrip(), expected.rstrip())
469
476
470
477
471
478
472 #-----------------------------------------------------------------------------
479 #-----------------------------------------------------------------------------
473 # Main - use as a script, mostly for developer experiments
480 # Main - use as a script, mostly for developer experiments
474 #-----------------------------------------------------------------------------
481 #-----------------------------------------------------------------------------
475
482
476 if __name__ == '__main__':
483 if __name__ == '__main__':
477 # A simple demo for interactive experimentation. This code will not get
484 # A simple demo for interactive experimentation. This code will not get
478 # picked up by any test suite.
485 # picked up by any test suite.
479 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
486 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
480
487
481 # configure here the syntax to use, prompt and whether to autoindent
488 # configure here the syntax to use, prompt and whether to autoindent
482 #isp, start_prompt = InputSplitter(), '>>> '
489 #isp, start_prompt = InputSplitter(), '>>> '
483 isp, start_prompt = IPythonInputSplitter(), 'In> '
490 isp, start_prompt = IPythonInputSplitter(), 'In> '
484
491
485 autoindent = True
492 autoindent = True
486 #autoindent = False
493 #autoindent = False
487
494
488 try:
495 try:
489 while True:
496 while True:
490 prompt = start_prompt
497 prompt = start_prompt
491 while isp.push_accepts_more():
498 while isp.push_accepts_more():
492 indent = ' '*isp.indent_spaces
499 indent = ' '*isp.indent_spaces
493 if autoindent:
500 if autoindent:
494 line = indent + input(prompt+indent)
501 line = indent + input(prompt+indent)
495 else:
502 else:
496 line = input(prompt)
503 line = input(prompt)
497 isp.push(line)
504 isp.push(line)
498 prompt = '... '
505 prompt = '... '
499
506
500 # Here we just return input so we can use it in a test suite, but a
507 # Here we just return input so we can use it in a test suite, but a
501 # real interpreter would instead send it for execution somewhere.
508 # real interpreter would instead send it for execution somewhere.
502 #src = isp.source; raise EOFError # dbg
509 #src = isp.source; raise EOFError # dbg
503 raw = isp.source_raw
510 raw = isp.source_raw
504 src = isp.source_reset()
511 src = isp.source_reset()
505 print('Input source was:\n', src)
512 print('Input source was:\n', src)
506 print('Raw source was:\n', raw)
513 print('Raw source was:\n', raw)
507 except EOFError:
514 except EOFError:
508 print('Bye')
515 print('Bye')
509
516
510 # Tests for cell magics support
517 # Tests for cell magics support
511
518
512 def test_last_blank():
519 def test_last_blank():
513 nt.assert_false(isp.last_blank(''))
520 nt.assert_false(isp.last_blank(''))
514 nt.assert_false(isp.last_blank('abc'))
521 nt.assert_false(isp.last_blank('abc'))
515 nt.assert_false(isp.last_blank('abc\n'))
522 nt.assert_false(isp.last_blank('abc\n'))
516 nt.assert_false(isp.last_blank('abc\na'))
523 nt.assert_false(isp.last_blank('abc\na'))
517
524
518 nt.assert_true(isp.last_blank('\n'))
525 nt.assert_true(isp.last_blank('\n'))
519 nt.assert_true(isp.last_blank('\n '))
526 nt.assert_true(isp.last_blank('\n '))
520 nt.assert_true(isp.last_blank('abc\n '))
527 nt.assert_true(isp.last_blank('abc\n '))
521 nt.assert_true(isp.last_blank('abc\n\n'))
528 nt.assert_true(isp.last_blank('abc\n\n'))
522 nt.assert_true(isp.last_blank('abc\nd\n\n'))
529 nt.assert_true(isp.last_blank('abc\nd\n\n'))
523 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
530 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
524 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
531 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
525
532
526
533
527 def test_last_two_blanks():
534 def test_last_two_blanks():
528 nt.assert_false(isp.last_two_blanks(''))
535 nt.assert_false(isp.last_two_blanks(''))
529 nt.assert_false(isp.last_two_blanks('abc'))
536 nt.assert_false(isp.last_two_blanks('abc'))
530 nt.assert_false(isp.last_two_blanks('abc\n'))
537 nt.assert_false(isp.last_two_blanks('abc\n'))
531 nt.assert_false(isp.last_two_blanks('abc\n\na'))
538 nt.assert_false(isp.last_two_blanks('abc\n\na'))
532 nt.assert_false(isp.last_two_blanks('abc\n \n'))
539 nt.assert_false(isp.last_two_blanks('abc\n \n'))
533 nt.assert_false(isp.last_two_blanks('abc\n\n'))
540 nt.assert_false(isp.last_two_blanks('abc\n\n'))
534
541
535 nt.assert_true(isp.last_two_blanks('\n\n'))
542 nt.assert_true(isp.last_two_blanks('\n\n'))
536 nt.assert_true(isp.last_two_blanks('\n\n '))
543 nt.assert_true(isp.last_two_blanks('\n\n '))
537 nt.assert_true(isp.last_two_blanks('\n \n'))
544 nt.assert_true(isp.last_two_blanks('\n \n'))
538 nt.assert_true(isp.last_two_blanks('abc\n\n '))
545 nt.assert_true(isp.last_two_blanks('abc\n\n '))
539 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
546 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
540 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
547 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
541 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
548 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
542 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
549 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
543 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
550 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
544 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
551 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
545
552
546
553
547 class CellMagicsCommon(object):
554 class CellMagicsCommon(object):
548
555
549 def test_whole_cell(self):
556 def test_whole_cell(self):
550 src = "%%cellm line\nbody\n"
557 src = "%%cellm line\nbody\n"
551 out = self.sp.transform_cell(src)
558 out = self.sp.transform_cell(src)
552 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
559 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
553 nt.assert_equal(out, py3compat.u_format(ref))
560 nt.assert_equal(out, py3compat.u_format(ref))
554
561
555 def test_cellmagic_help(self):
562 def test_cellmagic_help(self):
556 self.sp.push('%%cellm?')
563 self.sp.push('%%cellm?')
557 nt.assert_false(self.sp.push_accepts_more())
564 nt.assert_false(self.sp.push_accepts_more())
558
565
559 def tearDown(self):
566 def tearDown(self):
560 self.sp.reset()
567 self.sp.reset()
561
568
562
569
563 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
570 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
564 sp = isp.IPythonInputSplitter(line_input_checker=False)
571 sp = isp.IPythonInputSplitter(line_input_checker=False)
565
572
566 def test_incremental(self):
573 def test_incremental(self):
567 sp = self.sp
574 sp = self.sp
568 sp.push('%%cellm firstline\n')
575 sp.push('%%cellm firstline\n')
569 nt.assert_true(sp.push_accepts_more()) #1
576 nt.assert_true(sp.push_accepts_more()) #1
570 sp.push('line2\n')
577 sp.push('line2\n')
571 nt.assert_true(sp.push_accepts_more()) #2
578 nt.assert_true(sp.push_accepts_more()) #2
572 sp.push('\n')
579 sp.push('\n')
573 # This should accept a blank line and carry on until the cell is reset
580 # This should accept a blank line and carry on until the cell is reset
574 nt.assert_true(sp.push_accepts_more()) #3
581 nt.assert_true(sp.push_accepts_more()) #3
575
582
576 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
583 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
577 sp = isp.IPythonInputSplitter(line_input_checker=True)
584 sp = isp.IPythonInputSplitter(line_input_checker=True)
578
585
579 def test_incremental(self):
586 def test_incremental(self):
580 sp = self.sp
587 sp = self.sp
581 sp.push('%%cellm line2\n')
588 sp.push('%%cellm line2\n')
582 nt.assert_true(sp.push_accepts_more()) #1
589 nt.assert_true(sp.push_accepts_more()) #1
583 sp.push('\n')
590 sp.push('\n')
584 # In this case, a blank line should end the cell magic
591 # In this case, a blank line should end the cell magic
585 nt.assert_false(sp.push_accepts_more()) #2
592 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,207 +1,223 b''
1 # coding: utf-8
1 # coding: utf-8
2 """test the IPython Kernel"""
2 """test the IPython Kernel"""
3
3
4 #-------------------------------------------------------------------------------
4 #-------------------------------------------------------------------------------
5 # Copyright (C) 2013 The IPython Development Team
5 # Copyright (C) 2013 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
14
15 import io
15 import io
16 import os.path
16 import os.path
17 import sys
17 import sys
18
18
19 import nose.tools as nt
19 import nose.tools as nt
20
20
21 from IPython.testing import decorators as dec, tools as tt
21 from IPython.testing import decorators as dec, tools as tt
22 from IPython.utils import py3compat
22 from IPython.utils import py3compat
23 from IPython.utils.path import locate_profile
23 from IPython.utils.path import locate_profile
24 from IPython.utils.tempdir import TemporaryDirectory
24 from IPython.utils.tempdir import TemporaryDirectory
25
25
26 from .utils import (new_kernel, kernel, TIMEOUT, assemble_output, execute,
26 from .utils import (new_kernel, kernel, TIMEOUT, assemble_output, execute,
27 flush_channels, wait_for_idle)
27 flush_channels, wait_for_idle)
28
28
29 #-------------------------------------------------------------------------------
29 #-------------------------------------------------------------------------------
30 # Tests
30 # Tests
31 #-------------------------------------------------------------------------------
31 #-------------------------------------------------------------------------------
32
32
33
33
34 def _check_mp_mode(kc, expected=False, stream="stdout"):
34 def _check_mp_mode(kc, expected=False, stream="stdout"):
35 execute(kc=kc, code="import sys")
35 execute(kc=kc, code="import sys")
36 flush_channels(kc)
36 flush_channels(kc)
37 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
37 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
38 stdout, stderr = assemble_output(kc.iopub_channel)
38 stdout, stderr = assemble_output(kc.iopub_channel)
39 nt.assert_equal(eval(stdout.strip()), expected)
39 nt.assert_equal(eval(stdout.strip()), expected)
40
40
41
41
42 # printing tests
42 # printing tests
43
43
44 def test_simple_print():
44 def test_simple_print():
45 """simple print statement in kernel"""
45 """simple print statement in kernel"""
46 with kernel() as kc:
46 with kernel() as kc:
47 iopub = kc.iopub_channel
47 iopub = kc.iopub_channel
48 msg_id, content = execute(kc=kc, code="print ('hi')")
48 msg_id, content = execute(kc=kc, code="print ('hi')")
49 stdout, stderr = assemble_output(iopub)
49 stdout, stderr = assemble_output(iopub)
50 nt.assert_equal(stdout, 'hi\n')
50 nt.assert_equal(stdout, 'hi\n')
51 nt.assert_equal(stderr, '')
51 nt.assert_equal(stderr, '')
52 _check_mp_mode(kc, expected=False)
52 _check_mp_mode(kc, expected=False)
53
53
54
54
55 def test_sys_path():
55 def test_sys_path():
56 """test that sys.path doesn't get messed up by default"""
56 """test that sys.path doesn't get messed up by default"""
57 with kernel() as kc:
57 with kernel() as kc:
58 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
58 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
59 stdout, stderr = assemble_output(kc.iopub_channel)
59 stdout, stderr = assemble_output(kc.iopub_channel)
60 nt.assert_equal(stdout, "''\n")
60 nt.assert_equal(stdout, "''\n")
61
61
62 def test_sys_path_profile_dir():
62 def test_sys_path_profile_dir():
63 """test that sys.path doesn't get messed up when `--profile-dir` is specified"""
63 """test that sys.path doesn't get messed up when `--profile-dir` is specified"""
64
64
65 with new_kernel(['--profile-dir', locate_profile('default')]) as kc:
65 with new_kernel(['--profile-dir', locate_profile('default')]) as kc:
66 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
66 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
67 stdout, stderr = assemble_output(kc.iopub_channel)
67 stdout, stderr = assemble_output(kc.iopub_channel)
68 nt.assert_equal(stdout, "''\n")
68 nt.assert_equal(stdout, "''\n")
69
69
70 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
70 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
71 def test_subprocess_print():
71 def test_subprocess_print():
72 """printing from forked mp.Process"""
72 """printing from forked mp.Process"""
73 with new_kernel() as kc:
73 with new_kernel() as kc:
74 iopub = kc.iopub_channel
74 iopub = kc.iopub_channel
75
75
76 _check_mp_mode(kc, expected=False)
76 _check_mp_mode(kc, expected=False)
77 flush_channels(kc)
77 flush_channels(kc)
78 np = 5
78 np = 5
79 code = '\n'.join([
79 code = '\n'.join([
80 "from __future__ import print_function",
80 "from __future__ import print_function",
81 "import multiprocessing as mp",
81 "import multiprocessing as mp",
82 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
82 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
83 "for p in pool: p.start()",
83 "for p in pool: p.start()",
84 "for p in pool: p.join()"
84 "for p in pool: p.join()"
85 ])
85 ])
86
86
87 expected = '\n'.join([
87 expected = '\n'.join([
88 "hello %s" % i for i in range(np)
88 "hello %s" % i for i in range(np)
89 ]) + '\n'
89 ]) + '\n'
90
90
91 msg_id, content = execute(kc=kc, code=code)
91 msg_id, content = execute(kc=kc, code=code)
92 stdout, stderr = assemble_output(iopub)
92 stdout, stderr = assemble_output(iopub)
93 nt.assert_equal(stdout.count("hello"), np, stdout)
93 nt.assert_equal(stdout.count("hello"), np, stdout)
94 for n in range(np):
94 for n in range(np):
95 nt.assert_equal(stdout.count(str(n)), 1, stdout)
95 nt.assert_equal(stdout.count(str(n)), 1, stdout)
96 nt.assert_equal(stderr, '')
96 nt.assert_equal(stderr, '')
97 _check_mp_mode(kc, expected=False)
97 _check_mp_mode(kc, expected=False)
98 _check_mp_mode(kc, expected=False, stream="stderr")
98 _check_mp_mode(kc, expected=False, stream="stderr")
99
99
100
100
101 def test_subprocess_noprint():
101 def test_subprocess_noprint():
102 """mp.Process without print doesn't trigger iostream mp_mode"""
102 """mp.Process without print doesn't trigger iostream mp_mode"""
103 with kernel() as kc:
103 with kernel() as kc:
104 iopub = kc.iopub_channel
104 iopub = kc.iopub_channel
105
105
106 np = 5
106 np = 5
107 code = '\n'.join([
107 code = '\n'.join([
108 "import multiprocessing as mp",
108 "import multiprocessing as mp",
109 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
109 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
110 "for p in pool: p.start()",
110 "for p in pool: p.start()",
111 "for p in pool: p.join()"
111 "for p in pool: p.join()"
112 ])
112 ])
113
113
114 msg_id, content = execute(kc=kc, code=code)
114 msg_id, content = execute(kc=kc, code=code)
115 stdout, stderr = assemble_output(iopub)
115 stdout, stderr = assemble_output(iopub)
116 nt.assert_equal(stdout, '')
116 nt.assert_equal(stdout, '')
117 nt.assert_equal(stderr, '')
117 nt.assert_equal(stderr, '')
118
118
119 _check_mp_mode(kc, expected=False)
119 _check_mp_mode(kc, expected=False)
120 _check_mp_mode(kc, expected=False, stream="stderr")
120 _check_mp_mode(kc, expected=False, stream="stderr")
121
121
122
122
123 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
123 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
124 def test_subprocess_error():
124 def test_subprocess_error():
125 """error in mp.Process doesn't crash"""
125 """error in mp.Process doesn't crash"""
126 with new_kernel() as kc:
126 with new_kernel() as kc:
127 iopub = kc.iopub_channel
127 iopub = kc.iopub_channel
128
128
129 code = '\n'.join([
129 code = '\n'.join([
130 "import multiprocessing as mp",
130 "import multiprocessing as mp",
131 "p = mp.Process(target=int, args=('hi',))",
131 "p = mp.Process(target=int, args=('hi',))",
132 "p.start()",
132 "p.start()",
133 "p.join()",
133 "p.join()",
134 ])
134 ])
135
135
136 msg_id, content = execute(kc=kc, code=code)
136 msg_id, content = execute(kc=kc, code=code)
137 stdout, stderr = assemble_output(iopub)
137 stdout, stderr = assemble_output(iopub)
138 nt.assert_equal(stdout, '')
138 nt.assert_equal(stdout, '')
139 nt.assert_true("ValueError" in stderr, stderr)
139 nt.assert_true("ValueError" in stderr, stderr)
140
140
141 _check_mp_mode(kc, expected=False)
141 _check_mp_mode(kc, expected=False)
142 _check_mp_mode(kc, expected=False, stream="stderr")
142 _check_mp_mode(kc, expected=False, stream="stderr")
143
143
144 # raw_input tests
144 # raw_input tests
145
145
146 def test_raw_input():
146 def test_raw_input():
147 """test [raw_]input"""
147 """test [raw_]input"""
148 with kernel() as kc:
148 with kernel() as kc:
149 iopub = kc.iopub_channel
149 iopub = kc.iopub_channel
150
150
151 input_f = "input" if py3compat.PY3 else "raw_input"
151 input_f = "input" if py3compat.PY3 else "raw_input"
152 theprompt = "prompt> "
152 theprompt = "prompt> "
153 code = 'print({input_f}("{theprompt}"))'.format(**locals())
153 code = 'print({input_f}("{theprompt}"))'.format(**locals())
154 msg_id = kc.execute(code, allow_stdin=True)
154 msg_id = kc.execute(code, allow_stdin=True)
155 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
155 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
156 nt.assert_equal(msg['header']['msg_type'], u'input_request')
156 nt.assert_equal(msg['header']['msg_type'], u'input_request')
157 content = msg['content']
157 content = msg['content']
158 nt.assert_equal(content['prompt'], theprompt)
158 nt.assert_equal(content['prompt'], theprompt)
159 text = "some text"
159 text = "some text"
160 kc.input(text)
160 kc.input(text)
161 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
161 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
162 nt.assert_equal(reply['content']['status'], 'ok')
162 nt.assert_equal(reply['content']['status'], 'ok')
163 stdout, stderr = assemble_output(iopub)
163 stdout, stderr = assemble_output(iopub)
164 nt.assert_equal(stdout, text + "\n")
164 nt.assert_equal(stdout, text + "\n")
165
165
166
166
167 @dec.skipif(py3compat.PY3)
167 @dec.skipif(py3compat.PY3)
168 def test_eval_input():
168 def test_eval_input():
169 """test input() on Python 2"""
169 """test input() on Python 2"""
170 with kernel() as kc:
170 with kernel() as kc:
171 iopub = kc.iopub_channel
171 iopub = kc.iopub_channel
172
172
173 input_f = "input" if py3compat.PY3 else "raw_input"
173 input_f = "input" if py3compat.PY3 else "raw_input"
174 theprompt = "prompt> "
174 theprompt = "prompt> "
175 code = 'print(input("{theprompt}"))'.format(**locals())
175 code = 'print(input("{theprompt}"))'.format(**locals())
176 msg_id = kc.execute(code, allow_stdin=True)
176 msg_id = kc.execute(code, allow_stdin=True)
177 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
177 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
178 nt.assert_equal(msg['header']['msg_type'], u'input_request')
178 nt.assert_equal(msg['header']['msg_type'], u'input_request')
179 content = msg['content']
179 content = msg['content']
180 nt.assert_equal(content['prompt'], theprompt)
180 nt.assert_equal(content['prompt'], theprompt)
181 kc.input("1+1")
181 kc.input("1+1")
182 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
182 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
183 nt.assert_equal(reply['content']['status'], 'ok')
183 nt.assert_equal(reply['content']['status'], 'ok')
184 stdout, stderr = assemble_output(iopub)
184 stdout, stderr = assemble_output(iopub)
185 nt.assert_equal(stdout, "2\n")
185 nt.assert_equal(stdout, "2\n")
186
186
187
187
188 def test_save_history():
188 def test_save_history():
189 # Saving history from the kernel with %hist -f was failing because of
189 # Saving history from the kernel with %hist -f was failing because of
190 # unicode problems on Python 2.
190 # unicode problems on Python 2.
191 with kernel() as kc, TemporaryDirectory() as td:
191 with kernel() as kc, TemporaryDirectory() as td:
192 file = os.path.join(td, 'hist.out')
192 file = os.path.join(td, 'hist.out')
193 execute(u'a=1', kc=kc)
193 execute(u'a=1', kc=kc)
194 wait_for_idle(kc)
194 wait_for_idle(kc)
195 execute(u'b=u"abcΓΎ"', kc=kc)
195 execute(u'b=u"abcΓΎ"', kc=kc)
196 wait_for_idle(kc)
196 wait_for_idle(kc)
197 _, reply = execute("%hist -f " + file, kc=kc)
197 _, reply = execute("%hist -f " + file, kc=kc)
198 nt.assert_equal(reply['status'], 'ok')
198 nt.assert_equal(reply['status'], 'ok')
199 with io.open(file, encoding='utf-8') as f:
199 with io.open(file, encoding='utf-8') as f:
200 content = f.read()
200 content = f.read()
201 nt.assert_in(u'a=1', content)
201 nt.assert_in(u'a=1', content)
202 nt.assert_in(u'b=u"abcΓΎ"', content)
202 nt.assert_in(u'b=u"abcΓΎ"', content)
203
203
204 def test_help_output():
204 def test_help_output():
205 """ipython kernel --help-all works"""
205 """ipython kernel --help-all works"""
206 tt.help_all_output_test('kernel')
206 tt.help_all_output_test('kernel')
207
207
208 def test_is_complete():
209 with kernel() as kc:
210 # There are more test cases for this in core - here we just check
211 # that the kernel exposes the interface correctly.
212 kc.is_complete('2+2')
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['complete']
215
216 # SyntaxError should mean it's complete
217 kc.is_complete('raise = 2')
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['complete']
220
221 kc.is_complete('a = [1,\n2,')
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert not reply['content']['complete']
@@ -1,302 +1,306 b''
1 """The IPython kernel implementation"""
1 """The IPython kernel implementation"""
2
2
3 import getpass
3 import getpass
4 import sys
4 import sys
5 import traceback
5 import traceback
6
6
7 from IPython.core import release
7 from IPython.core import release
8 from IPython.utils.py3compat import builtin_mod, PY3
8 from IPython.utils.py3compat import builtin_mod, PY3
9 from IPython.utils.tokenutil import token_at_cursor
9 from IPython.utils.tokenutil import token_at_cursor
10 from IPython.utils.traitlets import Instance, Type, Any
10 from IPython.utils.traitlets import Instance, Type, Any
11 from IPython.utils.decorators import undoc
11 from IPython.utils.decorators import undoc
12
12
13 from .kernelbase import Kernel as KernelBase
13 from .kernelbase import Kernel as KernelBase
14 from .serialize import serialize_object, unpack_apply_message
14 from .serialize import serialize_object, unpack_apply_message
15 from .zmqshell import ZMQInteractiveShell
15 from .zmqshell import ZMQInteractiveShell
16
16
17 class IPythonKernel(KernelBase):
17 class IPythonKernel(KernelBase):
18 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
18 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
19 shell_class = Type(ZMQInteractiveShell)
19 shell_class = Type(ZMQInteractiveShell)
20
20
21 user_module = Any()
21 user_module = Any()
22 def _user_module_changed(self, name, old, new):
22 def _user_module_changed(self, name, old, new):
23 if self.shell is not None:
23 if self.shell is not None:
24 self.shell.user_module = new
24 self.shell.user_module = new
25
25
26 user_ns = Instance(dict, args=None, allow_none=True)
26 user_ns = Instance(dict, args=None, allow_none=True)
27 def _user_ns_changed(self, name, old, new):
27 def _user_ns_changed(self, name, old, new):
28 if self.shell is not None:
28 if self.shell is not None:
29 self.shell.user_ns = new
29 self.shell.user_ns = new
30 self.shell.init_user_ns()
30 self.shell.init_user_ns()
31
31
32 # A reference to the Python builtin 'raw_input' function.
32 # A reference to the Python builtin 'raw_input' function.
33 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
33 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
34 _sys_raw_input = Any()
34 _sys_raw_input = Any()
35 _sys_eval_input = Any()
35 _sys_eval_input = Any()
36
36
37 def __init__(self, **kwargs):
37 def __init__(self, **kwargs):
38 super(IPythonKernel, self).__init__(**kwargs)
38 super(IPythonKernel, self).__init__(**kwargs)
39
39
40 # Initialize the InteractiveShell subclass
40 # Initialize the InteractiveShell subclass
41 self.shell = self.shell_class.instance(parent=self,
41 self.shell = self.shell_class.instance(parent=self,
42 profile_dir = self.profile_dir,
42 profile_dir = self.profile_dir,
43 user_module = self.user_module,
43 user_module = self.user_module,
44 user_ns = self.user_ns,
44 user_ns = self.user_ns,
45 kernel = self,
45 kernel = self,
46 )
46 )
47 self.shell.displayhook.session = self.session
47 self.shell.displayhook.session = self.session
48 self.shell.displayhook.pub_socket = self.iopub_socket
48 self.shell.displayhook.pub_socket = self.iopub_socket
49 self.shell.displayhook.topic = self._topic('execute_result')
49 self.shell.displayhook.topic = self._topic('execute_result')
50 self.shell.display_pub.session = self.session
50 self.shell.display_pub.session = self.session
51 self.shell.display_pub.pub_socket = self.iopub_socket
51 self.shell.display_pub.pub_socket = self.iopub_socket
52 self.shell.data_pub.session = self.session
52 self.shell.data_pub.session = self.session
53 self.shell.data_pub.pub_socket = self.iopub_socket
53 self.shell.data_pub.pub_socket = self.iopub_socket
54
54
55 # TMP - hack while developing
55 # TMP - hack while developing
56 self.shell._reply_content = None
56 self.shell._reply_content = None
57
57
58 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
58 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
59 comm_manager = self.shell.comm_manager
59 comm_manager = self.shell.comm_manager
60 for msg_type in comm_msg_types:
60 for msg_type in comm_msg_types:
61 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
61 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
62
62
63 # Kernel info fields
63 # Kernel info fields
64 implementation = 'ipython'
64 implementation = 'ipython'
65 implementation_version = release.version
65 implementation_version = release.version
66 language = 'python'
66 language = 'python'
67 language_version = sys.version.split()[0]
67 language_version = sys.version.split()[0]
68 @property
68 @property
69 def banner(self):
69 def banner(self):
70 return self.shell.banner
70 return self.shell.banner
71
71
72 def start(self):
72 def start(self):
73 self.shell.exit_now = False
73 self.shell.exit_now = False
74 super(IPythonKernel, self).start()
74 super(IPythonKernel, self).start()
75
75
76 def set_parent(self, ident, parent):
76 def set_parent(self, ident, parent):
77 """Overridden from parent to tell the display hook and output streams
77 """Overridden from parent to tell the display hook and output streams
78 about the parent message.
78 about the parent message.
79 """
79 """
80 super(IPythonKernel, self).set_parent(ident, parent)
80 super(IPythonKernel, self).set_parent(ident, parent)
81 self.shell.set_parent(parent)
81 self.shell.set_parent(parent)
82
82
83 def _forward_input(self, allow_stdin=False):
83 def _forward_input(self, allow_stdin=False):
84 """Forward raw_input and getpass to the current frontend.
84 """Forward raw_input and getpass to the current frontend.
85
85
86 via input_request
86 via input_request
87 """
87 """
88 self._allow_stdin = allow_stdin
88 self._allow_stdin = allow_stdin
89
89
90 if PY3:
90 if PY3:
91 self._sys_raw_input = builtin_mod.input
91 self._sys_raw_input = builtin_mod.input
92 builtin_mod.input = self.raw_input
92 builtin_mod.input = self.raw_input
93 else:
93 else:
94 self._sys_raw_input = builtin_mod.raw_input
94 self._sys_raw_input = builtin_mod.raw_input
95 self._sys_eval_input = builtin_mod.input
95 self._sys_eval_input = builtin_mod.input
96 builtin_mod.raw_input = self.raw_input
96 builtin_mod.raw_input = self.raw_input
97 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
97 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
98 self._save_getpass = getpass.getpass
98 self._save_getpass = getpass.getpass
99 getpass.getpass = self.getpass
99 getpass.getpass = self.getpass
100
100
101 def _restore_input(self):
101 def _restore_input(self):
102 """Restore raw_input, getpass"""
102 """Restore raw_input, getpass"""
103 if PY3:
103 if PY3:
104 builtin_mod.input = self._sys_raw_input
104 builtin_mod.input = self._sys_raw_input
105 else:
105 else:
106 builtin_mod.raw_input = self._sys_raw_input
106 builtin_mod.raw_input = self._sys_raw_input
107 builtin_mod.input = self._sys_eval_input
107 builtin_mod.input = self._sys_eval_input
108
108
109 getpass.getpass = self._save_getpass
109 getpass.getpass = self._save_getpass
110
110
111 @property
111 @property
112 def execution_count(self):
112 def execution_count(self):
113 return self.shell.execution_count
113 return self.shell.execution_count
114
114
115 @execution_count.setter
115 @execution_count.setter
116 def execution_count(self, value):
116 def execution_count(self, value):
117 # Ignore the incrememnting done by KernelBase, in favour of our shell's
117 # Ignore the incrememnting done by KernelBase, in favour of our shell's
118 # execution counter.
118 # execution counter.
119 pass
119 pass
120
120
121 def do_execute(self, code, silent, store_history=True,
121 def do_execute(self, code, silent, store_history=True,
122 user_expressions=None, allow_stdin=False):
122 user_expressions=None, allow_stdin=False):
123 shell = self.shell # we'll need this a lot here
123 shell = self.shell # we'll need this a lot here
124
124
125 self._forward_input(allow_stdin)
125 self._forward_input(allow_stdin)
126
126
127 reply_content = {}
127 reply_content = {}
128 # FIXME: the shell calls the exception handler itself.
128 # FIXME: the shell calls the exception handler itself.
129 shell._reply_content = None
129 shell._reply_content = None
130 try:
130 try:
131 shell.run_cell(code, store_history=store_history, silent=silent)
131 shell.run_cell(code, store_history=store_history, silent=silent)
132 except:
132 except:
133 status = u'error'
133 status = u'error'
134 # FIXME: this code right now isn't being used yet by default,
134 # FIXME: this code right now isn't being used yet by default,
135 # because the run_cell() call above directly fires off exception
135 # because the run_cell() call above directly fires off exception
136 # reporting. This code, therefore, is only active in the scenario
136 # reporting. This code, therefore, is only active in the scenario
137 # where runlines itself has an unhandled exception. We need to
137 # where runlines itself has an unhandled exception. We need to
138 # uniformize this, for all exception construction to come from a
138 # uniformize this, for all exception construction to come from a
139 # single location in the codbase.
139 # single location in the codbase.
140 etype, evalue, tb = sys.exc_info()
140 etype, evalue, tb = sys.exc_info()
141 tb_list = traceback.format_exception(etype, evalue, tb)
141 tb_list = traceback.format_exception(etype, evalue, tb)
142 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
142 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
143 else:
143 else:
144 status = u'ok'
144 status = u'ok'
145 finally:
145 finally:
146 self._restore_input()
146 self._restore_input()
147
147
148 reply_content[u'status'] = status
148 reply_content[u'status'] = status
149
149
150 # Return the execution counter so clients can display prompts
150 # Return the execution counter so clients can display prompts
151 reply_content['execution_count'] = shell.execution_count - 1
151 reply_content['execution_count'] = shell.execution_count - 1
152
152
153 # FIXME - fish exception info out of shell, possibly left there by
153 # FIXME - fish exception info out of shell, possibly left there by
154 # runlines. We'll need to clean up this logic later.
154 # runlines. We'll need to clean up this logic later.
155 if shell._reply_content is not None:
155 if shell._reply_content is not None:
156 reply_content.update(shell._reply_content)
156 reply_content.update(shell._reply_content)
157 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
157 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
158 reply_content['engine_info'] = e_info
158 reply_content['engine_info'] = e_info
159 # reset after use
159 # reset after use
160 shell._reply_content = None
160 shell._reply_content = None
161
161
162 if 'traceback' in reply_content:
162 if 'traceback' in reply_content:
163 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
163 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
164
164
165
165
166 # At this point, we can tell whether the main code execution succeeded
166 # At this point, we can tell whether the main code execution succeeded
167 # or not. If it did, we proceed to evaluate user_expressions
167 # or not. If it did, we proceed to evaluate user_expressions
168 if reply_content['status'] == 'ok':
168 if reply_content['status'] == 'ok':
169 reply_content[u'user_expressions'] = \
169 reply_content[u'user_expressions'] = \
170 shell.user_expressions(user_expressions or {})
170 shell.user_expressions(user_expressions or {})
171 else:
171 else:
172 # If there was an error, don't even try to compute expressions
172 # If there was an error, don't even try to compute expressions
173 reply_content[u'user_expressions'] = {}
173 reply_content[u'user_expressions'] = {}
174
174
175 # Payloads should be retrieved regardless of outcome, so we can both
175 # Payloads should be retrieved regardless of outcome, so we can both
176 # recover partial output (that could have been generated early in a
176 # recover partial output (that could have been generated early in a
177 # block, before an error) and clear the payload system always.
177 # block, before an error) and clear the payload system always.
178 reply_content[u'payload'] = shell.payload_manager.read_payload()
178 reply_content[u'payload'] = shell.payload_manager.read_payload()
179 # Be agressive about clearing the payload because we don't want
179 # Be agressive about clearing the payload because we don't want
180 # it to sit in memory until the next execute_request comes in.
180 # it to sit in memory until the next execute_request comes in.
181 shell.payload_manager.clear_payload()
181 shell.payload_manager.clear_payload()
182
182
183 return reply_content
183 return reply_content
184
184
185 def do_complete(self, code, cursor_pos):
185 def do_complete(self, code, cursor_pos):
186 txt, matches = self.shell.complete('', code, cursor_pos)
186 txt, matches = self.shell.complete('', code, cursor_pos)
187 return {'matches' : matches,
187 return {'matches' : matches,
188 'cursor_end' : cursor_pos,
188 'cursor_end' : cursor_pos,
189 'cursor_start' : cursor_pos - len(txt),
189 'cursor_start' : cursor_pos - len(txt),
190 'metadata' : {},
190 'metadata' : {},
191 'status' : 'ok'}
191 'status' : 'ok'}
192
192
193 def do_inspect(self, code, cursor_pos, detail_level=0):
193 def do_inspect(self, code, cursor_pos, detail_level=0):
194 name = token_at_cursor(code, cursor_pos)
194 name = token_at_cursor(code, cursor_pos)
195 info = self.shell.object_inspect(name)
195 info = self.shell.object_inspect(name)
196
196
197 reply_content = {'status' : 'ok'}
197 reply_content = {'status' : 'ok'}
198 reply_content['data'] = data = {}
198 reply_content['data'] = data = {}
199 reply_content['metadata'] = {}
199 reply_content['metadata'] = {}
200 reply_content['found'] = info['found']
200 reply_content['found'] = info['found']
201 if info['found']:
201 if info['found']:
202 info_text = self.shell.object_inspect_text(
202 info_text = self.shell.object_inspect_text(
203 name,
203 name,
204 detail_level=detail_level,
204 detail_level=detail_level,
205 )
205 )
206 data['text/plain'] = info_text
206 data['text/plain'] = info_text
207
207
208 return reply_content
208 return reply_content
209
209
210 def do_history(self, hist_access_type, output, raw, session=None, start=None,
210 def do_history(self, hist_access_type, output, raw, session=None, start=None,
211 stop=None, n=None, pattern=None, unique=False):
211 stop=None, n=None, pattern=None, unique=False):
212 if hist_access_type == 'tail':
212 if hist_access_type == 'tail':
213 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
213 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
214 include_latest=True)
214 include_latest=True)
215
215
216 elif hist_access_type == 'range':
216 elif hist_access_type == 'range':
217 hist = self.shell.history_manager.get_range(session, start, stop,
217 hist = self.shell.history_manager.get_range(session, start, stop,
218 raw=raw, output=output)
218 raw=raw, output=output)
219
219
220 elif hist_access_type == 'search':
220 elif hist_access_type == 'search':
221 hist = self.shell.history_manager.search(
221 hist = self.shell.history_manager.search(
222 pattern, raw=raw, output=output, n=n, unique=unique)
222 pattern, raw=raw, output=output, n=n, unique=unique)
223 else:
223 else:
224 hist = []
224 hist = []
225
225
226 return {'history' : list(hist)}
226 return {'history' : list(hist)}
227
227
228 def do_shutdown(self, restart):
228 def do_shutdown(self, restart):
229 self.shell.exit_now = True
229 self.shell.exit_now = True
230 return dict(status='ok', restart=restart)
230 return dict(status='ok', restart=restart)
231
231
232 def do_is_complete(self, code):
233 complete = self.shell.input_transformer_manager.is_complete(code)
234 return {'complete': complete}
235
232 def do_apply(self, content, bufs, msg_id, reply_metadata):
236 def do_apply(self, content, bufs, msg_id, reply_metadata):
233 shell = self.shell
237 shell = self.shell
234 try:
238 try:
235 working = shell.user_ns
239 working = shell.user_ns
236
240
237 prefix = "_"+str(msg_id).replace("-","")+"_"
241 prefix = "_"+str(msg_id).replace("-","")+"_"
238
242
239 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
243 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
240
244
241 fname = getattr(f, '__name__', 'f')
245 fname = getattr(f, '__name__', 'f')
242
246
243 fname = prefix+"f"
247 fname = prefix+"f"
244 argname = prefix+"args"
248 argname = prefix+"args"
245 kwargname = prefix+"kwargs"
249 kwargname = prefix+"kwargs"
246 resultname = prefix+"result"
250 resultname = prefix+"result"
247
251
248 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
252 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
249 # print ns
253 # print ns
250 working.update(ns)
254 working.update(ns)
251 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
255 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
252 try:
256 try:
253 exec(code, shell.user_global_ns, shell.user_ns)
257 exec(code, shell.user_global_ns, shell.user_ns)
254 result = working.get(resultname)
258 result = working.get(resultname)
255 finally:
259 finally:
256 for key in ns:
260 for key in ns:
257 working.pop(key)
261 working.pop(key)
258
262
259 result_buf = serialize_object(result,
263 result_buf = serialize_object(result,
260 buffer_threshold=self.session.buffer_threshold,
264 buffer_threshold=self.session.buffer_threshold,
261 item_threshold=self.session.item_threshold,
265 item_threshold=self.session.item_threshold,
262 )
266 )
263
267
264 except:
268 except:
265 # invoke IPython traceback formatting
269 # invoke IPython traceback formatting
266 shell.showtraceback()
270 shell.showtraceback()
267 # FIXME - fish exception info out of shell, possibly left there by
271 # FIXME - fish exception info out of shell, possibly left there by
268 # run_code. We'll need to clean up this logic later.
272 # run_code. We'll need to clean up this logic later.
269 reply_content = {}
273 reply_content = {}
270 if shell._reply_content is not None:
274 if shell._reply_content is not None:
271 reply_content.update(shell._reply_content)
275 reply_content.update(shell._reply_content)
272 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
276 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
273 reply_content['engine_info'] = e_info
277 reply_content['engine_info'] = e_info
274 # reset after use
278 # reset after use
275 shell._reply_content = None
279 shell._reply_content = None
276
280
277 self.send_response(self.iopub_socket, u'error', reply_content,
281 self.send_response(self.iopub_socket, u'error', reply_content,
278 ident=self._topic('error'))
282 ident=self._topic('error'))
279 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
283 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
280 result_buf = []
284 result_buf = []
281
285
282 if reply_content['ename'] == 'UnmetDependency':
286 if reply_content['ename'] == 'UnmetDependency':
283 reply_metadata['dependencies_met'] = False
287 reply_metadata['dependencies_met'] = False
284 else:
288 else:
285 reply_content = {'status' : 'ok'}
289 reply_content = {'status' : 'ok'}
286
290
287 return reply_content, result_buf
291 return reply_content, result_buf
288
292
289 def do_clear(self):
293 def do_clear(self):
290 self.shell.reset(False)
294 self.shell.reset(False)
291 return dict(status='ok')
295 return dict(status='ok')
292
296
293
297
294 # This exists only for backwards compatibility - use IPythonKernel instead
298 # This exists only for backwards compatibility - use IPythonKernel instead
295
299
296 @undoc
300 @undoc
297 class Kernel(IPythonKernel):
301 class Kernel(IPythonKernel):
298 def __init__(self, *args, **kwargs):
302 def __init__(self, *args, **kwargs):
299 import warnings
303 import warnings
300 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
304 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
301 DeprecationWarning)
305 DeprecationWarning)
302 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
306 super(Kernel, self).__init__(*args, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now