##// END OF EJS Templates
Four possible states for completion reply, & indent hint
Thomas Kluyver -
Show More
@@ -1,654 +1,679 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 # Boolean indicating whether the current block has an unrecoverable syntax error
228 _is_invalid = False
227
229
228 def __init__(self):
230 def __init__(self):
229 """Create a new InputSplitter instance.
231 """Create a new InputSplitter instance.
230 """
232 """
231 self._buffer = []
233 self._buffer = []
232 self._compile = codeop.CommandCompiler()
234 self._compile = codeop.CommandCompiler()
233 self.encoding = get_input_encoding()
235 self.encoding = get_input_encoding()
234
236
235 def reset(self):
237 def reset(self):
236 """Reset the input buffer and associated state."""
238 """Reset the input buffer and associated state."""
237 self.indent_spaces = 0
239 self.indent_spaces = 0
238 self._buffer[:] = []
240 self._buffer[:] = []
239 self.source = ''
241 self.source = ''
240 self.code = None
242 self.code = None
241 self._is_complete = False
243 self._is_complete = False
244 self._is_invalid = False
242 self._full_dedent = False
245 self._full_dedent = False
243
246
244 def source_reset(self):
247 def source_reset(self):
245 """Return the input source and perform a full reset.
248 """Return the input source and perform a full reset.
246 """
249 """
247 out = self.source
250 out = self.source
248 self.reset()
251 self.reset()
249 return out
252 return out
250
253
251 def is_complete(self, source):
254 def check_complete(self, source):
252 """Return whether a block of code is ready to execute, or should be continued
255 """Return whether a block of code is ready to execute, or should be continued
253
256
254 This is a non-stateful API, and will reset the state of this InputSplitter.
257 This is a non-stateful API, and will reset the state of this InputSplitter.
258
259 Parameters
260 ----------
261 source : string
262 Python input code, which can be multiline.
263
264 Returns
265 -------
266 status : str
267 One of 'complete', 'incomplete', or 'invalid' if source is not a
268 prefix of valid code.
269 indent_spaces : int or None
270 The number of spaces by which to indent the next line of code. If
271 status is not 'incomplete', this is None.
255 """
272 """
256 self.reset()
273 self.reset()
257 try:
274 try:
258 self.push(source)
275 self.push(source)
259 return not self.push_accepts_more()
260 except SyntaxError:
276 except SyntaxError:
261 # Transformers in IPythonInputSplitter can raise SyntaxError,
277 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 # which push() will not catch.
278 # which push() will not catch.
263 return True
279 return 'invalid', None
280 else:
281 if self._is_invalid:
282 return 'invalid', None
283 elif self.push_accepts_more():
284 return 'incomplete', self.indent_spaces
285 else:
286 return 'complete', None
264 finally:
287 finally:
265 self.reset()
288 self.reset()
266
289
267 def push(self, lines):
290 def push(self, lines):
268 """Push one or more lines of input.
291 """Push one or more lines of input.
269
292
270 This stores the given lines and returns a status code indicating
293 This stores the given lines and returns a status code indicating
271 whether the code forms a complete Python block or not.
294 whether the code forms a complete Python block or not.
272
295
273 Any exceptions generated in compilation are swallowed, but if an
296 Any exceptions generated in compilation are swallowed, but if an
274 exception was produced, the method returns True.
297 exception was produced, the method returns True.
275
298
276 Parameters
299 Parameters
277 ----------
300 ----------
278 lines : string
301 lines : string
279 One or more lines of Python input.
302 One or more lines of Python input.
280
303
281 Returns
304 Returns
282 -------
305 -------
283 is_complete : boolean
306 is_complete : boolean
284 True if the current input source (the result of the current input
307 True if the current input source (the result of the current input
285 plus prior inputs) forms a complete Python execution block. Note that
308 plus prior inputs) forms a complete Python execution block. Note that
286 this value is also stored as a private attribute (``_is_complete``), so it
309 this value is also stored as a private attribute (``_is_complete``), so it
287 can be queried at any time.
310 can be queried at any time.
288 """
311 """
289 self._store(lines)
312 self._store(lines)
290 source = self.source
313 source = self.source
291
314
292 # Before calling _compile(), reset the code object to None so that if an
315 # Before calling _compile(), reset the code object to None so that if an
293 # exception is raised in compilation, we don't mislead by having
316 # exception is raised in compilation, we don't mislead by having
294 # inconsistent code/source attributes.
317 # inconsistent code/source attributes.
295 self.code, self._is_complete = None, None
318 self.code, self._is_complete = None, None
319 self._is_invalid = False
296
320
297 # Honor termination lines properly
321 # Honor termination lines properly
298 if source.endswith('\\\n'):
322 if source.endswith('\\\n'):
299 return False
323 return False
300
324
301 self._update_indent(lines)
325 self._update_indent(lines)
302 try:
326 try:
303 self.code = self._compile(source, symbol="exec")
327 self.code = self._compile(source, symbol="exec")
304 # Invalid syntax can produce any of a number of different errors from
328 # Invalid syntax can produce any of a number of different errors from
305 # inside the compiler, so we have to catch them all. Syntax errors
329 # inside the compiler, so we have to catch them all. Syntax errors
306 # immediately produce a 'ready' block, so the invalid Python can be
330 # immediately produce a 'ready' block, so the invalid Python can be
307 # sent to the kernel for evaluation with possible ipython
331 # sent to the kernel for evaluation with possible ipython
308 # special-syntax conversion.
332 # special-syntax conversion.
309 except (SyntaxError, OverflowError, ValueError, TypeError,
333 except (SyntaxError, OverflowError, ValueError, TypeError,
310 MemoryError):
334 MemoryError):
311 self._is_complete = True
335 self._is_complete = True
336 self._is_invalid = True
312 else:
337 else:
313 # Compilation didn't produce any exceptions (though it may not have
338 # Compilation didn't produce any exceptions (though it may not have
314 # given a complete code object)
339 # given a complete code object)
315 self._is_complete = self.code is not None
340 self._is_complete = self.code is not None
316
341
317 return self._is_complete
342 return self._is_complete
318
343
319 def push_accepts_more(self):
344 def push_accepts_more(self):
320 """Return whether a block of interactive input can accept more input.
345 """Return whether a block of interactive input can accept more input.
321
346
322 This method is meant to be used by line-oriented frontends, who need to
347 This method is meant to be used by line-oriented frontends, who need to
323 guess whether a block is complete or not based solely on prior and
348 guess whether a block is complete or not based solely on prior and
324 current input lines. The InputSplitter considers it has a complete
349 current input lines. The InputSplitter considers it has a complete
325 interactive block and will not accept more input when either:
350 interactive block and will not accept more input when either:
326
351
327 * A SyntaxError is raised
352 * A SyntaxError is raised
328
353
329 * The code is complete and consists of a single line or a single
354 * The code is complete and consists of a single line or a single
330 non-compound statement
355 non-compound statement
331
356
332 * The code is complete and has a blank line at the end
357 * The code is complete and has a blank line at the end
333
358
334 If the current input produces a syntax error, this method immediately
359 If the current input produces a syntax error, this method immediately
335 returns False but does *not* raise the syntax error exception, as
360 returns False but does *not* raise the syntax error exception, as
336 typically clients will want to send invalid syntax to an execution
361 typically clients will want to send invalid syntax to an execution
337 backend which might convert the invalid syntax into valid Python via
362 backend which might convert the invalid syntax into valid Python via
338 one of the dynamic IPython mechanisms.
363 one of the dynamic IPython mechanisms.
339 """
364 """
340
365
341 # With incomplete input, unconditionally accept more
366 # With incomplete input, unconditionally accept more
342 # A syntax error also sets _is_complete to True - see push()
367 # A syntax error also sets _is_complete to True - see push()
343 if not self._is_complete:
368 if not self._is_complete:
344 #print("Not complete") # debug
369 #print("Not complete") # debug
345 return True
370 return True
346
371
347 # The user can make any (complete) input execute by leaving a blank line
372 # The user can make any (complete) input execute by leaving a blank line
348 last_line = self.source.splitlines()[-1]
373 last_line = self.source.splitlines()[-1]
349 if (not last_line) or last_line.isspace():
374 if (not last_line) or last_line.isspace():
350 #print("Blank line") # debug
375 #print("Blank line") # debug
351 return False
376 return False
352
377
353 # If there's just a single line or AST node, and we're flush left, as is
378 # If there's just a single line or AST node, and we're flush left, as is
354 # the case after a simple statement such as 'a=1', we want to execute it
379 # the case after a simple statement such as 'a=1', we want to execute it
355 # straight away.
380 # straight away.
356 if self.indent_spaces==0:
381 if self.indent_spaces==0:
357 if len(self.source.splitlines()) <= 1:
382 if len(self.source.splitlines()) <= 1:
358 return False
383 return False
359
384
360 try:
385 try:
361 code_ast = ast.parse(u''.join(self._buffer))
386 code_ast = ast.parse(u''.join(self._buffer))
362 except Exception:
387 except Exception:
363 #print("Can't parse AST") # debug
388 #print("Can't parse AST") # debug
364 return False
389 return False
365 else:
390 else:
366 if len(code_ast.body) == 1 and \
391 if len(code_ast.body) == 1 and \
367 not hasattr(code_ast.body[0], 'body'):
392 not hasattr(code_ast.body[0], 'body'):
368 #print("Simple statement") # debug
393 #print("Simple statement") # debug
369 return False
394 return False
370
395
371 # General fallback - accept more code
396 # General fallback - accept more code
372 return True
397 return True
373
398
374 #------------------------------------------------------------------------
399 #------------------------------------------------------------------------
375 # Private interface
400 # Private interface
376 #------------------------------------------------------------------------
401 #------------------------------------------------------------------------
377
402
378 def _find_indent(self, line):
403 def _find_indent(self, line):
379 """Compute the new indentation level for a single line.
404 """Compute the new indentation level for a single line.
380
405
381 Parameters
406 Parameters
382 ----------
407 ----------
383 line : str
408 line : str
384 A single new line of non-whitespace, non-comment Python input.
409 A single new line of non-whitespace, non-comment Python input.
385
410
386 Returns
411 Returns
387 -------
412 -------
388 indent_spaces : int
413 indent_spaces : int
389 New value for the indent level (it may be equal to self.indent_spaces
414 New value for the indent level (it may be equal to self.indent_spaces
390 if indentation doesn't change.
415 if indentation doesn't change.
391
416
392 full_dedent : boolean
417 full_dedent : boolean
393 Whether the new line causes a full flush-left dedent.
418 Whether the new line causes a full flush-left dedent.
394 """
419 """
395 indent_spaces = self.indent_spaces
420 indent_spaces = self.indent_spaces
396 full_dedent = self._full_dedent
421 full_dedent = self._full_dedent
397
422
398 inisp = num_ini_spaces(line)
423 inisp = num_ini_spaces(line)
399 if inisp < indent_spaces:
424 if inisp < indent_spaces:
400 indent_spaces = inisp
425 indent_spaces = inisp
401 if indent_spaces <= 0:
426 if indent_spaces <= 0:
402 #print 'Full dedent in text',self.source # dbg
427 #print 'Full dedent in text',self.source # dbg
403 full_dedent = True
428 full_dedent = True
404
429
405 if line.rstrip()[-1] == ':':
430 if line.rstrip()[-1] == ':':
406 indent_spaces += 4
431 indent_spaces += 4
407 elif dedent_re.match(line):
432 elif dedent_re.match(line):
408 indent_spaces -= 4
433 indent_spaces -= 4
409 if indent_spaces <= 0:
434 if indent_spaces <= 0:
410 full_dedent = True
435 full_dedent = True
411
436
412 # Safety
437 # Safety
413 if indent_spaces < 0:
438 if indent_spaces < 0:
414 indent_spaces = 0
439 indent_spaces = 0
415 #print 'safety' # dbg
440 #print 'safety' # dbg
416
441
417 return indent_spaces, full_dedent
442 return indent_spaces, full_dedent
418
443
419 def _update_indent(self, lines):
444 def _update_indent(self, lines):
420 for line in remove_comments(lines).splitlines():
445 for line in remove_comments(lines).splitlines():
421 if line and not line.isspace():
446 if line and not line.isspace():
422 self.indent_spaces, self._full_dedent = self._find_indent(line)
447 self.indent_spaces, self._full_dedent = self._find_indent(line)
423
448
424 def _store(self, lines, buffer=None, store='source'):
449 def _store(self, lines, buffer=None, store='source'):
425 """Store one or more lines of input.
450 """Store one or more lines of input.
426
451
427 If input lines are not newline-terminated, a newline is automatically
452 If input lines are not newline-terminated, a newline is automatically
428 appended."""
453 appended."""
429
454
430 if buffer is None:
455 if buffer is None:
431 buffer = self._buffer
456 buffer = self._buffer
432
457
433 if lines.endswith('\n'):
458 if lines.endswith('\n'):
434 buffer.append(lines)
459 buffer.append(lines)
435 else:
460 else:
436 buffer.append(lines+'\n')
461 buffer.append(lines+'\n')
437 setattr(self, store, self._set_source(buffer))
462 setattr(self, store, self._set_source(buffer))
438
463
439 def _set_source(self, buffer):
464 def _set_source(self, buffer):
440 return u''.join(buffer)
465 return u''.join(buffer)
441
466
442
467
443 class IPythonInputSplitter(InputSplitter):
468 class IPythonInputSplitter(InputSplitter):
444 """An input splitter that recognizes all of IPython's special syntax."""
469 """An input splitter that recognizes all of IPython's special syntax."""
445
470
446 # String with raw, untransformed input.
471 # String with raw, untransformed input.
447 source_raw = ''
472 source_raw = ''
448
473
449 # Flag to track when a transformer has stored input that it hasn't given
474 # Flag to track when a transformer has stored input that it hasn't given
450 # back yet.
475 # back yet.
451 transformer_accumulating = False
476 transformer_accumulating = False
452
477
453 # Flag to track when assemble_python_lines has stored input that it hasn't
478 # Flag to track when assemble_python_lines has stored input that it hasn't
454 # given back yet.
479 # given back yet.
455 within_python_line = False
480 within_python_line = False
456
481
457 # Private attributes
482 # Private attributes
458
483
459 # List with lines of raw input accumulated so far.
484 # List with lines of raw input accumulated so far.
460 _buffer_raw = None
485 _buffer_raw = None
461
486
462 def __init__(self, line_input_checker=True, physical_line_transforms=None,
487 def __init__(self, line_input_checker=True, physical_line_transforms=None,
463 logical_line_transforms=None, python_line_transforms=None):
488 logical_line_transforms=None, python_line_transforms=None):
464 super(IPythonInputSplitter, self).__init__()
489 super(IPythonInputSplitter, self).__init__()
465 self._buffer_raw = []
490 self._buffer_raw = []
466 self._validate = True
491 self._validate = True
467
492
468 if physical_line_transforms is not None:
493 if physical_line_transforms is not None:
469 self.physical_line_transforms = physical_line_transforms
494 self.physical_line_transforms = physical_line_transforms
470 else:
495 else:
471 self.physical_line_transforms = [
496 self.physical_line_transforms = [
472 leading_indent(),
497 leading_indent(),
473 classic_prompt(),
498 classic_prompt(),
474 ipy_prompt(),
499 ipy_prompt(),
475 strip_encoding_cookie(),
500 strip_encoding_cookie(),
476 cellmagic(end_on_blank_line=line_input_checker),
501 cellmagic(end_on_blank_line=line_input_checker),
477 ]
502 ]
478
503
479 self.assemble_logical_lines = assemble_logical_lines()
504 self.assemble_logical_lines = assemble_logical_lines()
480 if logical_line_transforms is not None:
505 if logical_line_transforms is not None:
481 self.logical_line_transforms = logical_line_transforms
506 self.logical_line_transforms = logical_line_transforms
482 else:
507 else:
483 self.logical_line_transforms = [
508 self.logical_line_transforms = [
484 help_end(),
509 help_end(),
485 escaped_commands(),
510 escaped_commands(),
486 assign_from_magic(),
511 assign_from_magic(),
487 assign_from_system(),
512 assign_from_system(),
488 ]
513 ]
489
514
490 self.assemble_python_lines = assemble_python_lines()
515 self.assemble_python_lines = assemble_python_lines()
491 if python_line_transforms is not None:
516 if python_line_transforms is not None:
492 self.python_line_transforms = python_line_transforms
517 self.python_line_transforms = python_line_transforms
493 else:
518 else:
494 # We don't use any of these at present
519 # We don't use any of these at present
495 self.python_line_transforms = []
520 self.python_line_transforms = []
496
521
497 @property
522 @property
498 def transforms(self):
523 def transforms(self):
499 "Quick access to all transformers."
524 "Quick access to all transformers."
500 return self.physical_line_transforms + \
525 return self.physical_line_transforms + \
501 [self.assemble_logical_lines] + self.logical_line_transforms + \
526 [self.assemble_logical_lines] + self.logical_line_transforms + \
502 [self.assemble_python_lines] + self.python_line_transforms
527 [self.assemble_python_lines] + self.python_line_transforms
503
528
504 @property
529 @property
505 def transforms_in_use(self):
530 def transforms_in_use(self):
506 """Transformers, excluding logical line transformers if we're in a
531 """Transformers, excluding logical line transformers if we're in a
507 Python line."""
532 Python line."""
508 t = self.physical_line_transforms[:]
533 t = self.physical_line_transforms[:]
509 if not self.within_python_line:
534 if not self.within_python_line:
510 t += [self.assemble_logical_lines] + self.logical_line_transforms
535 t += [self.assemble_logical_lines] + self.logical_line_transforms
511 return t + [self.assemble_python_lines] + self.python_line_transforms
536 return t + [self.assemble_python_lines] + self.python_line_transforms
512
537
513 def reset(self):
538 def reset(self):
514 """Reset the input buffer and associated state."""
539 """Reset the input buffer and associated state."""
515 super(IPythonInputSplitter, self).reset()
540 super(IPythonInputSplitter, self).reset()
516 self._buffer_raw[:] = []
541 self._buffer_raw[:] = []
517 self.source_raw = ''
542 self.source_raw = ''
518 self.transformer_accumulating = False
543 self.transformer_accumulating = False
519 self.within_python_line = False
544 self.within_python_line = False
520
545
521 for t in self.transforms:
546 for t in self.transforms:
522 try:
547 try:
523 t.reset()
548 t.reset()
524 except SyntaxError:
549 except SyntaxError:
525 # Nothing that calls reset() expects to handle transformer
550 # Nothing that calls reset() expects to handle transformer
526 # errors
551 # errors
527 pass
552 pass
528
553
529 def flush_transformers(self):
554 def flush_transformers(self):
530 def _flush(transform, out):
555 def _flush(transform, out):
531 if out is not None:
556 if out is not None:
532 tmp = transform.push(out)
557 tmp = transform.push(out)
533 return tmp or transform.reset() or None
558 return tmp or transform.reset() or None
534 else:
559 else:
535 return transform.reset() or None
560 return transform.reset() or None
536
561
537 out = None
562 out = None
538 for t in self.transforms_in_use:
563 for t in self.transforms_in_use:
539 out = _flush(t, out)
564 out = _flush(t, out)
540
565
541 if out is not None:
566 if out is not None:
542 self._store(out)
567 self._store(out)
543
568
544 def raw_reset(self):
569 def raw_reset(self):
545 """Return raw input only and perform a full reset.
570 """Return raw input only and perform a full reset.
546 """
571 """
547 out = self.source_raw
572 out = self.source_raw
548 self.reset()
573 self.reset()
549 return out
574 return out
550
575
551 def source_reset(self):
576 def source_reset(self):
552 try:
577 try:
553 self.flush_transformers()
578 self.flush_transformers()
554 return self.source
579 return self.source
555 finally:
580 finally:
556 self.reset()
581 self.reset()
557
582
558 def push_accepts_more(self):
583 def push_accepts_more(self):
559 if self.transformer_accumulating:
584 if self.transformer_accumulating:
560 return True
585 return True
561 else:
586 else:
562 return super(IPythonInputSplitter, self).push_accepts_more()
587 return super(IPythonInputSplitter, self).push_accepts_more()
563
588
564 def transform_cell(self, cell):
589 def transform_cell(self, cell):
565 """Process and translate a cell of input.
590 """Process and translate a cell of input.
566 """
591 """
567 self.reset()
592 self.reset()
568 try:
593 try:
569 self.push(cell)
594 self.push(cell)
570 self.flush_transformers()
595 self.flush_transformers()
571 return self.source
596 return self.source
572 finally:
597 finally:
573 self.reset()
598 self.reset()
574
599
575 def push(self, lines):
600 def push(self, lines):
576 """Push one or more lines of IPython input.
601 """Push one or more lines of IPython input.
577
602
578 This stores the given lines and returns a status code indicating
603 This stores the given lines and returns a status code indicating
579 whether the code forms a complete Python block or not, after processing
604 whether the code forms a complete Python block or not, after processing
580 all input lines for special IPython syntax.
605 all input lines for special IPython syntax.
581
606
582 Any exceptions generated in compilation are swallowed, but if an
607 Any exceptions generated in compilation are swallowed, but if an
583 exception was produced, the method returns True.
608 exception was produced, the method returns True.
584
609
585 Parameters
610 Parameters
586 ----------
611 ----------
587 lines : string
612 lines : string
588 One or more lines of Python input.
613 One or more lines of Python input.
589
614
590 Returns
615 Returns
591 -------
616 -------
592 is_complete : boolean
617 is_complete : boolean
593 True if the current input source (the result of the current input
618 True if the current input source (the result of the current input
594 plus prior inputs) forms a complete Python execution block. Note that
619 plus prior inputs) forms a complete Python execution block. Note that
595 this value is also stored as a private attribute (_is_complete), so it
620 this value is also stored as a private attribute (_is_complete), so it
596 can be queried at any time.
621 can be queried at any time.
597 """
622 """
598
623
599 # We must ensure all input is pure unicode
624 # We must ensure all input is pure unicode
600 lines = cast_unicode(lines, self.encoding)
625 lines = cast_unicode(lines, self.encoding)
601
626
602 # ''.splitlines() --> [], but we need to push the empty line to transformers
627 # ''.splitlines() --> [], but we need to push the empty line to transformers
603 lines_list = lines.splitlines()
628 lines_list = lines.splitlines()
604 if not lines_list:
629 if not lines_list:
605 lines_list = ['']
630 lines_list = ['']
606
631
607 # Store raw source before applying any transformations to it. Note
632 # Store raw source before applying any transformations to it. Note
608 # that this must be done *after* the reset() call that would otherwise
633 # that this must be done *after* the reset() call that would otherwise
609 # flush the buffer.
634 # flush the buffer.
610 self._store(lines, self._buffer_raw, 'source_raw')
635 self._store(lines, self._buffer_raw, 'source_raw')
611
636
612 for line in lines_list:
637 for line in lines_list:
613 out = self.push_line(line)
638 out = self.push_line(line)
614
639
615 return out
640 return out
616
641
617 def push_line(self, line):
642 def push_line(self, line):
618 buf = self._buffer
643 buf = self._buffer
619
644
620 def _accumulating(dbg):
645 def _accumulating(dbg):
621 #print(dbg)
646 #print(dbg)
622 self.transformer_accumulating = True
647 self.transformer_accumulating = True
623 return False
648 return False
624
649
625 for transformer in self.physical_line_transforms:
650 for transformer in self.physical_line_transforms:
626 line = transformer.push(line)
651 line = transformer.push(line)
627 if line is None:
652 if line is None:
628 return _accumulating(transformer)
653 return _accumulating(transformer)
629
654
630 if not self.within_python_line:
655 if not self.within_python_line:
631 line = self.assemble_logical_lines.push(line)
656 line = self.assemble_logical_lines.push(line)
632 if line is None:
657 if line is None:
633 return _accumulating('acc logical line')
658 return _accumulating('acc logical line')
634
659
635 for transformer in self.logical_line_transforms:
660 for transformer in self.logical_line_transforms:
636 line = transformer.push(line)
661 line = transformer.push(line)
637 if line is None:
662 if line is None:
638 return _accumulating(transformer)
663 return _accumulating(transformer)
639
664
640 line = self.assemble_python_lines.push(line)
665 line = self.assemble_python_lines.push(line)
641 if line is None:
666 if line is None:
642 self.within_python_line = True
667 self.within_python_line = True
643 return _accumulating('acc python line')
668 return _accumulating('acc python line')
644 else:
669 else:
645 self.within_python_line = False
670 self.within_python_line = False
646
671
647 for transformer in self.python_line_transforms:
672 for transformer in self.python_line_transforms:
648 line = transformer.push(line)
673 line = transformer.push(line)
649 if line is None:
674 if line is None:
650 return _accumulating(transformer)
675 return _accumulating(transformer)
651
676
652 #print("transformers clear") #debug
677 #print("transformers clear") #debug
653 self.transformer_accumulating = False
678 self.transformer_accumulating = False
654 return super(IPythonInputSplitter, self).push(line)
679 return super(IPythonInputSplitter, self).push(line)
@@ -1,592 +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):
358 def test_check_complete(self):
359 isp = self.isp
359 isp = self.isp
360 assert isp.is_complete("a = 1")
360 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
361 assert not isp.is_complete("for a in range(5):")
361 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
362 assert isp.is_complete("raise = 2") # SyntaxError should mean complete
362 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
363 assert not isp.is_complete("a = [1,\n2,")
363 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
364
364
365 class InteractiveLoopTestCase(unittest.TestCase):
365 class InteractiveLoopTestCase(unittest.TestCase):
366 """Tests for an interactive loop like a python shell.
366 """Tests for an interactive loop like a python shell.
367 """
367 """
368 def check_ns(self, lines, ns):
368 def check_ns(self, lines, ns):
369 """Validate that the given input lines produce the resulting namespace.
369 """Validate that the given input lines produce the resulting namespace.
370
370
371 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
372 auto-indenting environment, as mini_interactive_loop above already does
372 auto-indenting environment, as mini_interactive_loop above already does
373 auto-indenting and prepends spaces to the input.
373 auto-indenting and prepends spaces to the input.
374 """
374 """
375 src = mini_interactive_loop(pseudo_input(lines))
375 src = mini_interactive_loop(pseudo_input(lines))
376 test_ns = {}
376 test_ns = {}
377 exec(src, test_ns)
377 exec(src, test_ns)
378 # 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,
379 # because Python fills test_ns with extra keys (copyright, etc). But
379 # because Python fills test_ns with extra keys (copyright, etc). But
380 # 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
381 for k,v in ns.items():
381 for k,v in ns.items():
382 self.assertEqual(test_ns[k], v)
382 self.assertEqual(test_ns[k], v)
383
383
384 def test_simple(self):
384 def test_simple(self):
385 self.check_ns(['x=1'], dict(x=1))
385 self.check_ns(['x=1'], dict(x=1))
386
386
387 def test_simple2(self):
387 def test_simple2(self):
388 self.check_ns(['if 1:', 'x=2'], dict(x=2))
388 self.check_ns(['if 1:', 'x=2'], dict(x=2))
389
389
390 def test_xy(self):
390 def test_xy(self):
391 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))
392
392
393 def test_abc(self):
393 def test_abc(self):
394 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))
395
395
396 def test_multi(self):
396 def test_multi(self):
397 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
397 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
398
398
399
399
400 class IPythonInputTestCase(InputSplitterTestCase):
400 class IPythonInputTestCase(InputSplitterTestCase):
401 """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
402 re-run the same test battery on the new input splitter.
402 re-run the same test battery on the new input splitter.
403
403
404 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
405 were tested by individual functions, as part of the OO interface.
405 were tested by individual functions, as part of the OO interface.
406
406
407 It also makes some checks on the raw buffer storage.
407 It also makes some checks on the raw buffer storage.
408 """
408 """
409
409
410 def setUp(self):
410 def setUp(self):
411 self.isp = isp.IPythonInputSplitter()
411 self.isp = isp.IPythonInputSplitter()
412
412
413 def test_syntax(self):
413 def test_syntax(self):
414 """Call all single-line syntax tests from the main object"""
414 """Call all single-line syntax tests from the main object"""
415 isp = self.isp
415 isp = self.isp
416 for example in syntax.values():
416 for example in syntax.values():
417 for raw, out_t in example:
417 for raw, out_t in example:
418 if raw.startswith(' '):
418 if raw.startswith(' '):
419 continue
419 continue
420
420
421 isp.push(raw+'\n')
421 isp.push(raw+'\n')
422 out_raw = isp.source_raw
422 out_raw = isp.source_raw
423 out = isp.source_reset()
423 out = isp.source_reset()
424 self.assertEqual(out.rstrip(), out_t,
424 self.assertEqual(out.rstrip(), out_t,
425 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
425 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
426 self.assertEqual(out_raw.rstrip(), raw.rstrip())
426 self.assertEqual(out_raw.rstrip(), raw.rstrip())
427
427
428 def test_syntax_multiline(self):
428 def test_syntax_multiline(self):
429 isp = self.isp
429 isp = self.isp
430 for example in syntax_ml.values():
430 for example in syntax_ml.values():
431 for line_pairs in example:
431 for line_pairs in example:
432 out_t_parts = []
432 out_t_parts = []
433 raw_parts = []
433 raw_parts = []
434 for lraw, out_t_part in line_pairs:
434 for lraw, out_t_part in line_pairs:
435 if out_t_part is not None:
435 if out_t_part is not None:
436 out_t_parts.append(out_t_part)
436 out_t_parts.append(out_t_part)
437
437
438 if lraw is not None:
438 if lraw is not None:
439 isp.push(lraw)
439 isp.push(lraw)
440 raw_parts.append(lraw)
440 raw_parts.append(lraw)
441
441
442 out_raw = isp.source_raw
442 out_raw = isp.source_raw
443 out = isp.source_reset()
443 out = isp.source_reset()
444 out_t = '\n'.join(out_t_parts).rstrip()
444 out_t = '\n'.join(out_t_parts).rstrip()
445 raw = '\n'.join(raw_parts).rstrip()
445 raw = '\n'.join(raw_parts).rstrip()
446 self.assertEqual(out.rstrip(), out_t)
446 self.assertEqual(out.rstrip(), out_t)
447 self.assertEqual(out_raw.rstrip(), raw)
447 self.assertEqual(out_raw.rstrip(), raw)
448
448
449 def test_syntax_multiline_cell(self):
449 def test_syntax_multiline_cell(self):
450 isp = self.isp
450 isp = self.isp
451 for example in syntax_ml.values():
451 for example in syntax_ml.values():
452
452
453 out_t_parts = []
453 out_t_parts = []
454 for line_pairs in example:
454 for line_pairs in example:
455 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)
456 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)
457 out = isp.transform_cell(raw)
457 out = isp.transform_cell(raw)
458 # Match ignoring trailing whitespace
458 # Match ignoring trailing whitespace
459 self.assertEqual(out.rstrip(), out_t.rstrip())
459 self.assertEqual(out.rstrip(), out_t.rstrip())
460
460
461 def test_cellmagic_preempt(self):
461 def test_cellmagic_preempt(self):
462 isp = self.isp
462 isp = self.isp
463 for raw, name, line, cell in [
463 for raw, name, line, cell in [
464 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
464 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
465 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
465 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
466 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
466 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
467 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
467 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
468 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
468 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
469 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
469 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
470 ]:
470 ]:
471 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
471 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
472 name, line, cell
472 name, line, cell
473 )
473 )
474 out = isp.transform_cell(raw)
474 out = isp.transform_cell(raw)
475 self.assertEqual(out.rstrip(), expected.rstrip())
475 self.assertEqual(out.rstrip(), expected.rstrip())
476
476
477
477
478
478
479 #-----------------------------------------------------------------------------
479 #-----------------------------------------------------------------------------
480 # Main - use as a script, mostly for developer experiments
480 # Main - use as a script, mostly for developer experiments
481 #-----------------------------------------------------------------------------
481 #-----------------------------------------------------------------------------
482
482
483 if __name__ == '__main__':
483 if __name__ == '__main__':
484 # A simple demo for interactive experimentation. This code will not get
484 # A simple demo for interactive experimentation. This code will not get
485 # picked up by any test suite.
485 # picked up by any test suite.
486 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
486 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
487
487
488 # configure here the syntax to use, prompt and whether to autoindent
488 # configure here the syntax to use, prompt and whether to autoindent
489 #isp, start_prompt = InputSplitter(), '>>> '
489 #isp, start_prompt = InputSplitter(), '>>> '
490 isp, start_prompt = IPythonInputSplitter(), 'In> '
490 isp, start_prompt = IPythonInputSplitter(), 'In> '
491
491
492 autoindent = True
492 autoindent = True
493 #autoindent = False
493 #autoindent = False
494
494
495 try:
495 try:
496 while True:
496 while True:
497 prompt = start_prompt
497 prompt = start_prompt
498 while isp.push_accepts_more():
498 while isp.push_accepts_more():
499 indent = ' '*isp.indent_spaces
499 indent = ' '*isp.indent_spaces
500 if autoindent:
500 if autoindent:
501 line = indent + input(prompt+indent)
501 line = indent + input(prompt+indent)
502 else:
502 else:
503 line = input(prompt)
503 line = input(prompt)
504 isp.push(line)
504 isp.push(line)
505 prompt = '... '
505 prompt = '... '
506
506
507 # 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
508 # real interpreter would instead send it for execution somewhere.
508 # real interpreter would instead send it for execution somewhere.
509 #src = isp.source; raise EOFError # dbg
509 #src = isp.source; raise EOFError # dbg
510 raw = isp.source_raw
510 raw = isp.source_raw
511 src = isp.source_reset()
511 src = isp.source_reset()
512 print('Input source was:\n', src)
512 print('Input source was:\n', src)
513 print('Raw source was:\n', raw)
513 print('Raw source was:\n', raw)
514 except EOFError:
514 except EOFError:
515 print('Bye')
515 print('Bye')
516
516
517 # Tests for cell magics support
517 # Tests for cell magics support
518
518
519 def test_last_blank():
519 def test_last_blank():
520 nt.assert_false(isp.last_blank(''))
520 nt.assert_false(isp.last_blank(''))
521 nt.assert_false(isp.last_blank('abc'))
521 nt.assert_false(isp.last_blank('abc'))
522 nt.assert_false(isp.last_blank('abc\n'))
522 nt.assert_false(isp.last_blank('abc\n'))
523 nt.assert_false(isp.last_blank('abc\na'))
523 nt.assert_false(isp.last_blank('abc\na'))
524
524
525 nt.assert_true(isp.last_blank('\n'))
525 nt.assert_true(isp.last_blank('\n'))
526 nt.assert_true(isp.last_blank('\n '))
526 nt.assert_true(isp.last_blank('\n '))
527 nt.assert_true(isp.last_blank('abc\n '))
527 nt.assert_true(isp.last_blank('abc\n '))
528 nt.assert_true(isp.last_blank('abc\n\n'))
528 nt.assert_true(isp.last_blank('abc\n\n'))
529 nt.assert_true(isp.last_blank('abc\nd\n\n'))
529 nt.assert_true(isp.last_blank('abc\nd\n\n'))
530 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
530 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
531 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
531 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
532
532
533
533
534 def test_last_two_blanks():
534 def test_last_two_blanks():
535 nt.assert_false(isp.last_two_blanks(''))
535 nt.assert_false(isp.last_two_blanks(''))
536 nt.assert_false(isp.last_two_blanks('abc'))
536 nt.assert_false(isp.last_two_blanks('abc'))
537 nt.assert_false(isp.last_two_blanks('abc\n'))
537 nt.assert_false(isp.last_two_blanks('abc\n'))
538 nt.assert_false(isp.last_two_blanks('abc\n\na'))
538 nt.assert_false(isp.last_two_blanks('abc\n\na'))
539 nt.assert_false(isp.last_two_blanks('abc\n \n'))
539 nt.assert_false(isp.last_two_blanks('abc\n \n'))
540 nt.assert_false(isp.last_two_blanks('abc\n\n'))
540 nt.assert_false(isp.last_two_blanks('abc\n\n'))
541
541
542 nt.assert_true(isp.last_two_blanks('\n\n'))
542 nt.assert_true(isp.last_two_blanks('\n\n'))
543 nt.assert_true(isp.last_two_blanks('\n\n '))
543 nt.assert_true(isp.last_two_blanks('\n\n '))
544 nt.assert_true(isp.last_two_blanks('\n \n'))
544 nt.assert_true(isp.last_two_blanks('\n \n'))
545 nt.assert_true(isp.last_two_blanks('abc\n\n '))
545 nt.assert_true(isp.last_two_blanks('abc\n\n '))
546 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
546 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
547 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
547 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
548 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
548 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
549 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'))
550 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'))
551 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'))
552
552
553
553
554 class CellMagicsCommon(object):
554 class CellMagicsCommon(object):
555
555
556 def test_whole_cell(self):
556 def test_whole_cell(self):
557 src = "%%cellm line\nbody\n"
557 src = "%%cellm line\nbody\n"
558 out = self.sp.transform_cell(src)
558 out = self.sp.transform_cell(src)
559 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"
560 nt.assert_equal(out, py3compat.u_format(ref))
560 nt.assert_equal(out, py3compat.u_format(ref))
561
561
562 def test_cellmagic_help(self):
562 def test_cellmagic_help(self):
563 self.sp.push('%%cellm?')
563 self.sp.push('%%cellm?')
564 nt.assert_false(self.sp.push_accepts_more())
564 nt.assert_false(self.sp.push_accepts_more())
565
565
566 def tearDown(self):
566 def tearDown(self):
567 self.sp.reset()
567 self.sp.reset()
568
568
569
569
570 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
570 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
571 sp = isp.IPythonInputSplitter(line_input_checker=False)
571 sp = isp.IPythonInputSplitter(line_input_checker=False)
572
572
573 def test_incremental(self):
573 def test_incremental(self):
574 sp = self.sp
574 sp = self.sp
575 sp.push('%%cellm firstline\n')
575 sp.push('%%cellm firstline\n')
576 nt.assert_true(sp.push_accepts_more()) #1
576 nt.assert_true(sp.push_accepts_more()) #1
577 sp.push('line2\n')
577 sp.push('line2\n')
578 nt.assert_true(sp.push_accepts_more()) #2
578 nt.assert_true(sp.push_accepts_more()) #2
579 sp.push('\n')
579 sp.push('\n')
580 # 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
581 nt.assert_true(sp.push_accepts_more()) #3
581 nt.assert_true(sp.push_accepts_more()) #3
582
582
583 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
583 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
584 sp = isp.IPythonInputSplitter(line_input_checker=True)
584 sp = isp.IPythonInputSplitter(line_input_checker=True)
585
585
586 def test_incremental(self):
586 def test_incremental(self):
587 sp = self.sp
587 sp = self.sp
588 sp.push('%%cellm line2\n')
588 sp.push('%%cellm line2\n')
589 nt.assert_true(sp.push_accepts_more()) #1
589 nt.assert_true(sp.push_accepts_more()) #1
590 sp.push('\n')
590 sp.push('\n')
591 # In this case, a blank line should end the cell magic
591 # In this case, a blank line should end the cell magic
592 nt.assert_false(sp.push_accepts_more()) #2
592 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,223 +1,224 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():
208 def test_is_complete():
209 with kernel() as kc:
209 with kernel() as kc:
210 # There are more test cases for this in core - here we just check
210 # There are more test cases for this in core - here we just check
211 # that the kernel exposes the interface correctly.
211 # that the kernel exposes the interface correctly.
212 kc.is_complete('2+2')
212 kc.is_complete('2+2')
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['complete']
214 assert reply['content']['status'] == 'complete'
215
215
216 # SyntaxError should mean it's complete
216 # SyntaxError should mean it's complete
217 kc.is_complete('raise = 2')
217 kc.is_complete('raise = 2')
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['complete']
219 assert reply['content']['status'] == 'invalid'
220
220
221 kc.is_complete('a = [1,\n2,')
221 kc.is_complete('a = [1,\n2,')
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert not reply['content']['complete']
223 assert reply['content']['status'] == 'incomplete'
224 assert reply['content']['indent'] == '' No newline at end of file
@@ -1,420 +1,428 b''
1 """Test suite for our zeromq-based message specification."""
1 """Test suite for our zeromq-based message specification."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import re
6 import re
7 from distutils.version import LooseVersion as V
7 from distutils.version import LooseVersion as V
8 from subprocess import PIPE
8 from subprocess import PIPE
9 try:
9 try:
10 from queue import Empty # Py 3
10 from queue import Empty # Py 3
11 except ImportError:
11 except ImportError:
12 from Queue import Empty # Py 2
12 from Queue import Empty # Py 2
13
13
14 import nose.tools as nt
14 import nose.tools as nt
15
15
16 from IPython.kernel import KernelManager
16 from IPython.kernel import KernelManager
17
17
18 from IPython.utils.traitlets import (
18 from IPython.utils.traitlets import (
19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
20 )
20 )
21 from IPython.utils.py3compat import string_types, iteritems
21 from IPython.utils.py3compat import string_types, iteritems
22
22
23 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
23 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Globals
26 # Globals
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 KC = None
28 KC = None
29
29
30 def setup():
30 def setup():
31 global KC
31 global KC
32 KC = start_global_kernel()
32 KC = start_global_kernel()
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Message Spec References
35 # Message Spec References
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class Reference(HasTraits):
38 class Reference(HasTraits):
39
39
40 """
40 """
41 Base class for message spec specification testing.
41 Base class for message spec specification testing.
42
42
43 This class is the core of the message specification test. The
43 This class is the core of the message specification test. The
44 idea is that child classes implement trait attributes for each
44 idea is that child classes implement trait attributes for each
45 message keys, so that message keys can be tested against these
45 message keys, so that message keys can be tested against these
46 traits using :meth:`check` method.
46 traits using :meth:`check` method.
47
47
48 """
48 """
49
49
50 def check(self, d):
50 def check(self, d):
51 """validate a dict against our traits"""
51 """validate a dict against our traits"""
52 for key in self.trait_names():
52 for key in self.trait_names():
53 nt.assert_in(key, d)
53 nt.assert_in(key, d)
54 # FIXME: always allow None, probably not a good idea
54 # FIXME: always allow None, probably not a good idea
55 if d[key] is None:
55 if d[key] is None:
56 continue
56 continue
57 try:
57 try:
58 setattr(self, key, d[key])
58 setattr(self, key, d[key])
59 except TraitError as e:
59 except TraitError as e:
60 assert False, str(e)
60 assert False, str(e)
61
61
62
62
63 class Version(Unicode):
63 class Version(Unicode):
64 def __init__(self, *args, **kwargs):
64 def __init__(self, *args, **kwargs):
65 self.min = kwargs.pop('min', None)
65 self.min = kwargs.pop('min', None)
66 self.max = kwargs.pop('max', None)
66 self.max = kwargs.pop('max', None)
67 kwargs['default_value'] = self.min
67 kwargs['default_value'] = self.min
68 super(Version, self).__init__(*args, **kwargs)
68 super(Version, self).__init__(*args, **kwargs)
69
69
70 def validate(self, obj, value):
70 def validate(self, obj, value):
71 if self.min and V(value) < V(self.min):
71 if self.min and V(value) < V(self.min):
72 raise TraitError("bad version: %s < %s" % (value, self.min))
72 raise TraitError("bad version: %s < %s" % (value, self.min))
73 if self.max and (V(value) > V(self.max)):
73 if self.max and (V(value) > V(self.max)):
74 raise TraitError("bad version: %s > %s" % (value, self.max))
74 raise TraitError("bad version: %s > %s" % (value, self.max))
75
75
76
76
77 class RMessage(Reference):
77 class RMessage(Reference):
78 msg_id = Unicode()
78 msg_id = Unicode()
79 msg_type = Unicode()
79 msg_type = Unicode()
80 header = Dict()
80 header = Dict()
81 parent_header = Dict()
81 parent_header = Dict()
82 content = Dict()
82 content = Dict()
83
83
84 def check(self, d):
84 def check(self, d):
85 super(RMessage, self).check(d)
85 super(RMessage, self).check(d)
86 RHeader().check(self.header)
86 RHeader().check(self.header)
87 if self.parent_header:
87 if self.parent_header:
88 RHeader().check(self.parent_header)
88 RHeader().check(self.parent_header)
89
89
90 class RHeader(Reference):
90 class RHeader(Reference):
91 msg_id = Unicode()
91 msg_id = Unicode()
92 msg_type = Unicode()
92 msg_type = Unicode()
93 session = Unicode()
93 session = Unicode()
94 username = Unicode()
94 username = Unicode()
95 version = Version(min='5.0')
95 version = Version(min='5.0')
96
96
97 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
97 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
98
98
99 class MimeBundle(Reference):
99 class MimeBundle(Reference):
100 metadata = Dict()
100 metadata = Dict()
101 data = Dict()
101 data = Dict()
102 def _data_changed(self, name, old, new):
102 def _data_changed(self, name, old, new):
103 for k,v in iteritems(new):
103 for k,v in iteritems(new):
104 assert mime_pat.match(k)
104 assert mime_pat.match(k)
105 nt.assert_is_instance(v, string_types)
105 nt.assert_is_instance(v, string_types)
106
106
107 # shell replies
107 # shell replies
108
108
109 class ExecuteReply(Reference):
109 class ExecuteReply(Reference):
110 execution_count = Integer()
110 execution_count = Integer()
111 status = Enum((u'ok', u'error'))
111 status = Enum((u'ok', u'error'))
112
112
113 def check(self, d):
113 def check(self, d):
114 Reference.check(self, d)
114 Reference.check(self, d)
115 if d['status'] == 'ok':
115 if d['status'] == 'ok':
116 ExecuteReplyOkay().check(d)
116 ExecuteReplyOkay().check(d)
117 elif d['status'] == 'error':
117 elif d['status'] == 'error':
118 ExecuteReplyError().check(d)
118 ExecuteReplyError().check(d)
119
119
120
120
121 class ExecuteReplyOkay(Reference):
121 class ExecuteReplyOkay(Reference):
122 payload = List(Dict)
122 payload = List(Dict)
123 user_expressions = Dict()
123 user_expressions = Dict()
124
124
125
125
126 class ExecuteReplyError(Reference):
126 class ExecuteReplyError(Reference):
127 ename = Unicode()
127 ename = Unicode()
128 evalue = Unicode()
128 evalue = Unicode()
129 traceback = List(Unicode)
129 traceback = List(Unicode)
130
130
131
131
132 class InspectReply(MimeBundle):
132 class InspectReply(MimeBundle):
133 found = Bool()
133 found = Bool()
134
134
135
135
136 class ArgSpec(Reference):
136 class ArgSpec(Reference):
137 args = List(Unicode)
137 args = List(Unicode)
138 varargs = Unicode()
138 varargs = Unicode()
139 varkw = Unicode()
139 varkw = Unicode()
140 defaults = List()
140 defaults = List()
141
141
142
142
143 class Status(Reference):
143 class Status(Reference):
144 execution_state = Enum((u'busy', u'idle', u'starting'))
144 execution_state = Enum((u'busy', u'idle', u'starting'))
145
145
146
146
147 class CompleteReply(Reference):
147 class CompleteReply(Reference):
148 matches = List(Unicode)
148 matches = List(Unicode)
149 cursor_start = Integer()
149 cursor_start = Integer()
150 cursor_end = Integer()
150 cursor_end = Integer()
151 status = Unicode()
151 status = Unicode()
152
152
153
153
154 class KernelInfoReply(Reference):
154 class KernelInfoReply(Reference):
155 protocol_version = Version(min='5.0')
155 protocol_version = Version(min='5.0')
156 implementation = Unicode('ipython')
156 implementation = Unicode('ipython')
157 implementation_version = Version(min='2.1')
157 implementation_version = Version(min='2.1')
158 language_version = Version(min='2.7')
158 language_version = Version(min='2.7')
159 language = Unicode('python')
159 language = Unicode('python')
160 banner = Unicode()
160 banner = Unicode()
161
161
162
162
163 class IsCompleteReply(Reference):
163 class IsCompleteReply(Reference):
164 complete = Bool()
164 status = Enum((u'complete', u'incomplete', u'invalid', u'unknown'))
165
166 def check(self, d):
167 Reference.check(self, d)
168 if d['status'] == 'incomplete':
169 IsCompleteReplyIncomplete().check(d)
170
171 class IsCompleteReplyIncomplete(Reference):
172 indent = Unicode()
165
173
166
174
167 # IOPub messages
175 # IOPub messages
168
176
169 class ExecuteInput(Reference):
177 class ExecuteInput(Reference):
170 code = Unicode()
178 code = Unicode()
171 execution_count = Integer()
179 execution_count = Integer()
172
180
173
181
174 Error = ExecuteReplyError
182 Error = ExecuteReplyError
175
183
176
184
177 class Stream(Reference):
185 class Stream(Reference):
178 name = Enum((u'stdout', u'stderr'))
186 name = Enum((u'stdout', u'stderr'))
179 data = Unicode()
187 data = Unicode()
180
188
181
189
182 class DisplayData(MimeBundle):
190 class DisplayData(MimeBundle):
183 pass
191 pass
184
192
185
193
186 class ExecuteResult(MimeBundle):
194 class ExecuteResult(MimeBundle):
187 execution_count = Integer()
195 execution_count = Integer()
188
196
189
197
190 references = {
198 references = {
191 'execute_reply' : ExecuteReply(),
199 'execute_reply' : ExecuteReply(),
192 'inspect_reply' : InspectReply(),
200 'inspect_reply' : InspectReply(),
193 'status' : Status(),
201 'status' : Status(),
194 'complete_reply' : CompleteReply(),
202 'complete_reply' : CompleteReply(),
195 'kernel_info_reply': KernelInfoReply(),
203 'kernel_info_reply': KernelInfoReply(),
196 'is_complete_reply': IsCompleteReply(),
204 'is_complete_reply': IsCompleteReply(),
197 'execute_input' : ExecuteInput(),
205 'execute_input' : ExecuteInput(),
198 'execute_result' : ExecuteResult(),
206 'execute_result' : ExecuteResult(),
199 'error' : Error(),
207 'error' : Error(),
200 'stream' : Stream(),
208 'stream' : Stream(),
201 'display_data' : DisplayData(),
209 'display_data' : DisplayData(),
202 'header' : RHeader(),
210 'header' : RHeader(),
203 }
211 }
204 """
212 """
205 Specifications of `content` part of the reply messages.
213 Specifications of `content` part of the reply messages.
206 """
214 """
207
215
208
216
209 def validate_message(msg, msg_type=None, parent=None):
217 def validate_message(msg, msg_type=None, parent=None):
210 """validate a message
218 """validate a message
211
219
212 This is a generator, and must be iterated through to actually
220 This is a generator, and must be iterated through to actually
213 trigger each test.
221 trigger each test.
214
222
215 If msg_type and/or parent are given, the msg_type and/or parent msg_id
223 If msg_type and/or parent are given, the msg_type and/or parent msg_id
216 are compared with the given values.
224 are compared with the given values.
217 """
225 """
218 RMessage().check(msg)
226 RMessage().check(msg)
219 if msg_type:
227 if msg_type:
220 nt.assert_equal(msg['msg_type'], msg_type)
228 nt.assert_equal(msg['msg_type'], msg_type)
221 if parent:
229 if parent:
222 nt.assert_equal(msg['parent_header']['msg_id'], parent)
230 nt.assert_equal(msg['parent_header']['msg_id'], parent)
223 content = msg['content']
231 content = msg['content']
224 ref = references[msg['msg_type']]
232 ref = references[msg['msg_type']]
225 ref.check(content)
233 ref.check(content)
226
234
227
235
228 #-----------------------------------------------------------------------------
236 #-----------------------------------------------------------------------------
229 # Tests
237 # Tests
230 #-----------------------------------------------------------------------------
238 #-----------------------------------------------------------------------------
231
239
232 # Shell channel
240 # Shell channel
233
241
234 def test_execute():
242 def test_execute():
235 flush_channels()
243 flush_channels()
236
244
237 msg_id = KC.execute(code='x=1')
245 msg_id = KC.execute(code='x=1')
238 reply = KC.get_shell_msg(timeout=TIMEOUT)
246 reply = KC.get_shell_msg(timeout=TIMEOUT)
239 validate_message(reply, 'execute_reply', msg_id)
247 validate_message(reply, 'execute_reply', msg_id)
240
248
241
249
242 def test_execute_silent():
250 def test_execute_silent():
243 flush_channels()
251 flush_channels()
244 msg_id, reply = execute(code='x=1', silent=True)
252 msg_id, reply = execute(code='x=1', silent=True)
245
253
246 # flush status=idle
254 # flush status=idle
247 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
255 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
248 validate_message(status, 'status', msg_id)
256 validate_message(status, 'status', msg_id)
249 nt.assert_equal(status['content']['execution_state'], 'idle')
257 nt.assert_equal(status['content']['execution_state'], 'idle')
250
258
251 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
259 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
252 count = reply['execution_count']
260 count = reply['execution_count']
253
261
254 msg_id, reply = execute(code='x=2', silent=True)
262 msg_id, reply = execute(code='x=2', silent=True)
255
263
256 # flush status=idle
264 # flush status=idle
257 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
265 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
258 validate_message(status, 'status', msg_id)
266 validate_message(status, 'status', msg_id)
259 nt.assert_equal(status['content']['execution_state'], 'idle')
267 nt.assert_equal(status['content']['execution_state'], 'idle')
260
268
261 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
269 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
262 count_2 = reply['execution_count']
270 count_2 = reply['execution_count']
263 nt.assert_equal(count_2, count)
271 nt.assert_equal(count_2, count)
264
272
265
273
266 def test_execute_error():
274 def test_execute_error():
267 flush_channels()
275 flush_channels()
268
276
269 msg_id, reply = execute(code='1/0')
277 msg_id, reply = execute(code='1/0')
270 nt.assert_equal(reply['status'], 'error')
278 nt.assert_equal(reply['status'], 'error')
271 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
279 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
272
280
273 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
281 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
274 validate_message(error, 'error', msg_id)
282 validate_message(error, 'error', msg_id)
275
283
276
284
277 def test_execute_inc():
285 def test_execute_inc():
278 """execute request should increment execution_count"""
286 """execute request should increment execution_count"""
279 flush_channels()
287 flush_channels()
280
288
281 msg_id, reply = execute(code='x=1')
289 msg_id, reply = execute(code='x=1')
282 count = reply['execution_count']
290 count = reply['execution_count']
283
291
284 flush_channels()
292 flush_channels()
285
293
286 msg_id, reply = execute(code='x=2')
294 msg_id, reply = execute(code='x=2')
287 count_2 = reply['execution_count']
295 count_2 = reply['execution_count']
288 nt.assert_equal(count_2, count+1)
296 nt.assert_equal(count_2, count+1)
289
297
290
298
291 def test_user_expressions():
299 def test_user_expressions():
292 flush_channels()
300 flush_channels()
293
301
294 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
302 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
295 user_expressions = reply['user_expressions']
303 user_expressions = reply['user_expressions']
296 nt.assert_equal(user_expressions, {u'foo': {
304 nt.assert_equal(user_expressions, {u'foo': {
297 u'status': u'ok',
305 u'status': u'ok',
298 u'data': {u'text/plain': u'2'},
306 u'data': {u'text/plain': u'2'},
299 u'metadata': {},
307 u'metadata': {},
300 }})
308 }})
301
309
302
310
303 def test_user_expressions_fail():
311 def test_user_expressions_fail():
304 flush_channels()
312 flush_channels()
305
313
306 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
314 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
307 user_expressions = reply['user_expressions']
315 user_expressions = reply['user_expressions']
308 foo = user_expressions['foo']
316 foo = user_expressions['foo']
309 nt.assert_equal(foo['status'], 'error')
317 nt.assert_equal(foo['status'], 'error')
310 nt.assert_equal(foo['ename'], 'NameError')
318 nt.assert_equal(foo['ename'], 'NameError')
311
319
312
320
313 def test_oinfo():
321 def test_oinfo():
314 flush_channels()
322 flush_channels()
315
323
316 msg_id = KC.inspect('a')
324 msg_id = KC.inspect('a')
317 reply = KC.get_shell_msg(timeout=TIMEOUT)
325 reply = KC.get_shell_msg(timeout=TIMEOUT)
318 validate_message(reply, 'inspect_reply', msg_id)
326 validate_message(reply, 'inspect_reply', msg_id)
319
327
320
328
321 def test_oinfo_found():
329 def test_oinfo_found():
322 flush_channels()
330 flush_channels()
323
331
324 msg_id, reply = execute(code='a=5')
332 msg_id, reply = execute(code='a=5')
325
333
326 msg_id = KC.inspect('a')
334 msg_id = KC.inspect('a')
327 reply = KC.get_shell_msg(timeout=TIMEOUT)
335 reply = KC.get_shell_msg(timeout=TIMEOUT)
328 validate_message(reply, 'inspect_reply', msg_id)
336 validate_message(reply, 'inspect_reply', msg_id)
329 content = reply['content']
337 content = reply['content']
330 assert content['found']
338 assert content['found']
331 text = content['data']['text/plain']
339 text = content['data']['text/plain']
332 nt.assert_in('Type:', text)
340 nt.assert_in('Type:', text)
333 nt.assert_in('Docstring:', text)
341 nt.assert_in('Docstring:', text)
334
342
335
343
336 def test_oinfo_detail():
344 def test_oinfo_detail():
337 flush_channels()
345 flush_channels()
338
346
339 msg_id, reply = execute(code='ip=get_ipython()')
347 msg_id, reply = execute(code='ip=get_ipython()')
340
348
341 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
349 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
342 reply = KC.get_shell_msg(timeout=TIMEOUT)
350 reply = KC.get_shell_msg(timeout=TIMEOUT)
343 validate_message(reply, 'inspect_reply', msg_id)
351 validate_message(reply, 'inspect_reply', msg_id)
344 content = reply['content']
352 content = reply['content']
345 assert content['found']
353 assert content['found']
346 text = content['data']['text/plain']
354 text = content['data']['text/plain']
347 nt.assert_in('Definition:', text)
355 nt.assert_in('Definition:', text)
348 nt.assert_in('Source:', text)
356 nt.assert_in('Source:', text)
349
357
350
358
351 def test_oinfo_not_found():
359 def test_oinfo_not_found():
352 flush_channels()
360 flush_channels()
353
361
354 msg_id = KC.inspect('dne')
362 msg_id = KC.inspect('dne')
355 reply = KC.get_shell_msg(timeout=TIMEOUT)
363 reply = KC.get_shell_msg(timeout=TIMEOUT)
356 validate_message(reply, 'inspect_reply', msg_id)
364 validate_message(reply, 'inspect_reply', msg_id)
357 content = reply['content']
365 content = reply['content']
358 nt.assert_false(content['found'])
366 nt.assert_false(content['found'])
359
367
360
368
361 def test_complete():
369 def test_complete():
362 flush_channels()
370 flush_channels()
363
371
364 msg_id, reply = execute(code="alpha = albert = 5")
372 msg_id, reply = execute(code="alpha = albert = 5")
365
373
366 msg_id = KC.complete('al', 2)
374 msg_id = KC.complete('al', 2)
367 reply = KC.get_shell_msg(timeout=TIMEOUT)
375 reply = KC.get_shell_msg(timeout=TIMEOUT)
368 validate_message(reply, 'complete_reply', msg_id)
376 validate_message(reply, 'complete_reply', msg_id)
369 matches = reply['content']['matches']
377 matches = reply['content']['matches']
370 for name in ('alpha', 'albert'):
378 for name in ('alpha', 'albert'):
371 nt.assert_in(name, matches)
379 nt.assert_in(name, matches)
372
380
373
381
374 def test_kernel_info_request():
382 def test_kernel_info_request():
375 flush_channels()
383 flush_channels()
376
384
377 msg_id = KC.kernel_info()
385 msg_id = KC.kernel_info()
378 reply = KC.get_shell_msg(timeout=TIMEOUT)
386 reply = KC.get_shell_msg(timeout=TIMEOUT)
379 validate_message(reply, 'kernel_info_reply', msg_id)
387 validate_message(reply, 'kernel_info_reply', msg_id)
380
388
381
389
382 def test_single_payload():
390 def test_single_payload():
383 flush_channels()
391 flush_channels()
384 msg_id, reply = execute(code="for i in range(3):\n"+
392 msg_id, reply = execute(code="for i in range(3):\n"+
385 " x=range?\n")
393 " x=range?\n")
386 payload = reply['payload']
394 payload = reply['payload']
387 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
395 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
388 nt.assert_equal(len(next_input_pls), 1)
396 nt.assert_equal(len(next_input_pls), 1)
389
397
390 def test_is_complete():
398 def test_is_complete():
391 flush_channels()
399 flush_channels()
392
400
393 msg_id = KC.is_complete("a = 1")
401 msg_id = KC.is_complete("a = 1")
394 reply = KC.get_shell_msg(timeout=TIMEOUT)
402 reply = KC.get_shell_msg(timeout=TIMEOUT)
395 validate_message(reply, 'is_complete_reply', msg_id)
403 validate_message(reply, 'is_complete_reply', msg_id)
396
404
397 # IOPub channel
405 # IOPub channel
398
406
399
407
400 def test_stream():
408 def test_stream():
401 flush_channels()
409 flush_channels()
402
410
403 msg_id, reply = execute("print('hi')")
411 msg_id, reply = execute("print('hi')")
404
412
405 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
413 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
406 validate_message(stdout, 'stream', msg_id)
414 validate_message(stdout, 'stream', msg_id)
407 content = stdout['content']
415 content = stdout['content']
408 nt.assert_equal(content['data'], u'hi\n')
416 nt.assert_equal(content['data'], u'hi\n')
409
417
410
418
411 def test_display_data():
419 def test_display_data():
412 flush_channels()
420 flush_channels()
413
421
414 msg_id, reply = execute("from IPython.core.display import display; display(1)")
422 msg_id, reply = execute("from IPython.core.display import display; display(1)")
415
423
416 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
424 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
417 validate_message(display, 'display_data', parent=msg_id)
425 validate_message(display, 'display_data', parent=msg_id)
418 data = display['content']['data']
426 data = display['content']['data']
419 nt.assert_equal(data['text/plain'], u'1')
427 nt.assert_equal(data['text/plain'], u'1')
420
428
@@ -1,306 +1,309 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):
232 def do_is_complete(self, code):
233 complete = self.shell.input_transformer_manager.is_complete(code)
233 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
234 return {'complete': complete}
234 r = {'status': status}
235 if status == 'incomplete':
236 r['indent'] = ' ' * indent_spaces
237 return r
235
238
236 def do_apply(self, content, bufs, msg_id, reply_metadata):
239 def do_apply(self, content, bufs, msg_id, reply_metadata):
237 shell = self.shell
240 shell = self.shell
238 try:
241 try:
239 working = shell.user_ns
242 working = shell.user_ns
240
243
241 prefix = "_"+str(msg_id).replace("-","")+"_"
244 prefix = "_"+str(msg_id).replace("-","")+"_"
242
245
243 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
246 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
244
247
245 fname = getattr(f, '__name__', 'f')
248 fname = getattr(f, '__name__', 'f')
246
249
247 fname = prefix+"f"
250 fname = prefix+"f"
248 argname = prefix+"args"
251 argname = prefix+"args"
249 kwargname = prefix+"kwargs"
252 kwargname = prefix+"kwargs"
250 resultname = prefix+"result"
253 resultname = prefix+"result"
251
254
252 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
255 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
253 # print ns
256 # print ns
254 working.update(ns)
257 working.update(ns)
255 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
258 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
256 try:
259 try:
257 exec(code, shell.user_global_ns, shell.user_ns)
260 exec(code, shell.user_global_ns, shell.user_ns)
258 result = working.get(resultname)
261 result = working.get(resultname)
259 finally:
262 finally:
260 for key in ns:
263 for key in ns:
261 working.pop(key)
264 working.pop(key)
262
265
263 result_buf = serialize_object(result,
266 result_buf = serialize_object(result,
264 buffer_threshold=self.session.buffer_threshold,
267 buffer_threshold=self.session.buffer_threshold,
265 item_threshold=self.session.item_threshold,
268 item_threshold=self.session.item_threshold,
266 )
269 )
267
270
268 except:
271 except:
269 # invoke IPython traceback formatting
272 # invoke IPython traceback formatting
270 shell.showtraceback()
273 shell.showtraceback()
271 # FIXME - fish exception info out of shell, possibly left there by
274 # FIXME - fish exception info out of shell, possibly left there by
272 # run_code. We'll need to clean up this logic later.
275 # run_code. We'll need to clean up this logic later.
273 reply_content = {}
276 reply_content = {}
274 if shell._reply_content is not None:
277 if shell._reply_content is not None:
275 reply_content.update(shell._reply_content)
278 reply_content.update(shell._reply_content)
276 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
279 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
277 reply_content['engine_info'] = e_info
280 reply_content['engine_info'] = e_info
278 # reset after use
281 # reset after use
279 shell._reply_content = None
282 shell._reply_content = None
280
283
281 self.send_response(self.iopub_socket, u'error', reply_content,
284 self.send_response(self.iopub_socket, u'error', reply_content,
282 ident=self._topic('error'))
285 ident=self._topic('error'))
283 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
286 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
284 result_buf = []
287 result_buf = []
285
288
286 if reply_content['ename'] == 'UnmetDependency':
289 if reply_content['ename'] == 'UnmetDependency':
287 reply_metadata['dependencies_met'] = False
290 reply_metadata['dependencies_met'] = False
288 else:
291 else:
289 reply_content = {'status' : 'ok'}
292 reply_content = {'status' : 'ok'}
290
293
291 return reply_content, result_buf
294 return reply_content, result_buf
292
295
293 def do_clear(self):
296 def do_clear(self):
294 self.shell.reset(False)
297 self.shell.reset(False)
295 return dict(status='ok')
298 return dict(status='ok')
296
299
297
300
298 # This exists only for backwards compatibility - use IPythonKernel instead
301 # This exists only for backwards compatibility - use IPythonKernel instead
299
302
300 @undoc
303 @undoc
301 class Kernel(IPythonKernel):
304 class Kernel(IPythonKernel):
302 def __init__(self, *args, **kwargs):
305 def __init__(self, *args, **kwargs):
303 import warnings
306 import warnings
304 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
307 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
305 DeprecationWarning)
308 DeprecationWarning)
306 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
309 super(Kernel, self).__init__(*args, **kwargs)
@@ -1,693 +1,693 b''
1 """Base class for a kernel that talks to frontends over 0MQ."""
1 """Base class for a kernel that talks to frontends over 0MQ."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 import sys
8 import sys
9 import time
9 import time
10 import logging
10 import logging
11 import uuid
11 import uuid
12
12
13 from datetime import datetime
13 from datetime import datetime
14 from signal import (
14 from signal import (
15 signal, default_int_handler, SIGINT
15 signal, default_int_handler, SIGINT
16 )
16 )
17
17
18 import zmq
18 import zmq
19 from zmq.eventloop import ioloop
19 from zmq.eventloop import ioloop
20 from zmq.eventloop.zmqstream import ZMQStream
20 from zmq.eventloop.zmqstream import ZMQStream
21
21
22 from IPython.config.configurable import Configurable
22 from IPython.config.configurable import Configurable
23 from IPython.core.error import StdinNotImplementedError
23 from IPython.core.error import StdinNotImplementedError
24 from IPython.core import release
24 from IPython.core import release
25 from IPython.utils import py3compat
25 from IPython.utils import py3compat
26 from IPython.utils.py3compat import unicode_type, string_types
26 from IPython.utils.py3compat import unicode_type, string_types
27 from IPython.utils.jsonutil import json_clean
27 from IPython.utils.jsonutil import json_clean
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool,
29 Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool,
30 )
30 )
31
31
32 from .session import Session
32 from .session import Session
33
33
34
34
35 class Kernel(Configurable):
35 class Kernel(Configurable):
36
36
37 #---------------------------------------------------------------------------
37 #---------------------------------------------------------------------------
38 # Kernel interface
38 # Kernel interface
39 #---------------------------------------------------------------------------
39 #---------------------------------------------------------------------------
40
40
41 # attribute to override with a GUI
41 # attribute to override with a GUI
42 eventloop = Any(None)
42 eventloop = Any(None)
43 def _eventloop_changed(self, name, old, new):
43 def _eventloop_changed(self, name, old, new):
44 """schedule call to eventloop from IOLoop"""
44 """schedule call to eventloop from IOLoop"""
45 loop = ioloop.IOLoop.instance()
45 loop = ioloop.IOLoop.instance()
46 loop.add_callback(self.enter_eventloop)
46 loop.add_callback(self.enter_eventloop)
47
47
48 session = Instance(Session)
48 session = Instance(Session)
49 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
49 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
50 shell_streams = List()
50 shell_streams = List()
51 control_stream = Instance(ZMQStream)
51 control_stream = Instance(ZMQStream)
52 iopub_socket = Instance(zmq.Socket)
52 iopub_socket = Instance(zmq.Socket)
53 stdin_socket = Instance(zmq.Socket)
53 stdin_socket = Instance(zmq.Socket)
54 log = Instance(logging.Logger)
54 log = Instance(logging.Logger)
55
55
56 # identities:
56 # identities:
57 int_id = Integer(-1)
57 int_id = Integer(-1)
58 ident = Unicode()
58 ident = Unicode()
59
59
60 def _ident_default(self):
60 def _ident_default(self):
61 return unicode_type(uuid.uuid4())
61 return unicode_type(uuid.uuid4())
62
62
63 # Private interface
63 # Private interface
64
64
65 _darwin_app_nap = Bool(True, config=True,
65 _darwin_app_nap = Bool(True, config=True,
66 help="""Whether to use appnope for compatiblity with OS X App Nap.
66 help="""Whether to use appnope for compatiblity with OS X App Nap.
67
67
68 Only affects OS X >= 10.9.
68 Only affects OS X >= 10.9.
69 """
69 """
70 )
70 )
71
71
72 # track associations with current request
72 # track associations with current request
73 _allow_stdin = Bool(False)
73 _allow_stdin = Bool(False)
74 _parent_header = Dict()
74 _parent_header = Dict()
75 _parent_ident = Any(b'')
75 _parent_ident = Any(b'')
76 # Time to sleep after flushing the stdout/err buffers in each execute
76 # Time to sleep after flushing the stdout/err buffers in each execute
77 # cycle. While this introduces a hard limit on the minimal latency of the
77 # cycle. While this introduces a hard limit on the minimal latency of the
78 # execute cycle, it helps prevent output synchronization problems for
78 # execute cycle, it helps prevent output synchronization problems for
79 # clients.
79 # clients.
80 # Units are in seconds. The minimum zmq latency on local host is probably
80 # Units are in seconds. The minimum zmq latency on local host is probably
81 # ~150 microseconds, set this to 500us for now. We may need to increase it
81 # ~150 microseconds, set this to 500us for now. We may need to increase it
82 # a little if it's not enough after more interactive testing.
82 # a little if it's not enough after more interactive testing.
83 _execute_sleep = Float(0.0005, config=True)
83 _execute_sleep = Float(0.0005, config=True)
84
84
85 # Frequency of the kernel's event loop.
85 # Frequency of the kernel's event loop.
86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
87 # adapt to milliseconds.
87 # adapt to milliseconds.
88 _poll_interval = Float(0.05, config=True)
88 _poll_interval = Float(0.05, config=True)
89
89
90 # If the shutdown was requested over the network, we leave here the
90 # If the shutdown was requested over the network, we leave here the
91 # necessary reply message so it can be sent by our registered atexit
91 # necessary reply message so it can be sent by our registered atexit
92 # handler. This ensures that the reply is only sent to clients truly at
92 # handler. This ensures that the reply is only sent to clients truly at
93 # the end of our shutdown process (which happens after the underlying
93 # the end of our shutdown process (which happens after the underlying
94 # IPython shell's own shutdown).
94 # IPython shell's own shutdown).
95 _shutdown_message = None
95 _shutdown_message = None
96
96
97 # This is a dict of port number that the kernel is listening on. It is set
97 # This is a dict of port number that the kernel is listening on. It is set
98 # by record_ports and used by connect_request.
98 # by record_ports and used by connect_request.
99 _recorded_ports = Dict()
99 _recorded_ports = Dict()
100
100
101 # set of aborted msg_ids
101 # set of aborted msg_ids
102 aborted = Set()
102 aborted = Set()
103
103
104 # Track execution count here. For IPython, we override this to use the
104 # Track execution count here. For IPython, we override this to use the
105 # execution count we store in the shell.
105 # execution count we store in the shell.
106 execution_count = 0
106 execution_count = 0
107
107
108
108
109 def __init__(self, **kwargs):
109 def __init__(self, **kwargs):
110 super(Kernel, self).__init__(**kwargs)
110 super(Kernel, self).__init__(**kwargs)
111
111
112 # Build dict of handlers for message types
112 # Build dict of handlers for message types
113 msg_types = [ 'execute_request', 'complete_request',
113 msg_types = [ 'execute_request', 'complete_request',
114 'inspect_request', 'history_request',
114 'inspect_request', 'history_request',
115 'kernel_info_request',
115 'kernel_info_request',
116 'connect_request', 'shutdown_request',
116 'connect_request', 'shutdown_request',
117 'apply_request', 'is_complete_request',
117 'apply_request', 'is_complete_request',
118 ]
118 ]
119 self.shell_handlers = {}
119 self.shell_handlers = {}
120 for msg_type in msg_types:
120 for msg_type in msg_types:
121 self.shell_handlers[msg_type] = getattr(self, msg_type)
121 self.shell_handlers[msg_type] = getattr(self, msg_type)
122
122
123 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
123 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
124 self.control_handlers = {}
124 self.control_handlers = {}
125 for msg_type in control_msg_types:
125 for msg_type in control_msg_types:
126 self.control_handlers[msg_type] = getattr(self, msg_type)
126 self.control_handlers[msg_type] = getattr(self, msg_type)
127
127
128
128
129 def dispatch_control(self, msg):
129 def dispatch_control(self, msg):
130 """dispatch control requests"""
130 """dispatch control requests"""
131 idents,msg = self.session.feed_identities(msg, copy=False)
131 idents,msg = self.session.feed_identities(msg, copy=False)
132 try:
132 try:
133 msg = self.session.unserialize(msg, content=True, copy=False)
133 msg = self.session.unserialize(msg, content=True, copy=False)
134 except:
134 except:
135 self.log.error("Invalid Control Message", exc_info=True)
135 self.log.error("Invalid Control Message", exc_info=True)
136 return
136 return
137
137
138 self.log.debug("Control received: %s", msg)
138 self.log.debug("Control received: %s", msg)
139
139
140 # Set the parent message for side effects.
140 # Set the parent message for side effects.
141 self.set_parent(idents, msg)
141 self.set_parent(idents, msg)
142 self._publish_status(u'busy')
142 self._publish_status(u'busy')
143
143
144 header = msg['header']
144 header = msg['header']
145 msg_type = header['msg_type']
145 msg_type = header['msg_type']
146
146
147 handler = self.control_handlers.get(msg_type, None)
147 handler = self.control_handlers.get(msg_type, None)
148 if handler is None:
148 if handler is None:
149 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
149 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
150 else:
150 else:
151 try:
151 try:
152 handler(self.control_stream, idents, msg)
152 handler(self.control_stream, idents, msg)
153 except Exception:
153 except Exception:
154 self.log.error("Exception in control handler:", exc_info=True)
154 self.log.error("Exception in control handler:", exc_info=True)
155
155
156 sys.stdout.flush()
156 sys.stdout.flush()
157 sys.stderr.flush()
157 sys.stderr.flush()
158 self._publish_status(u'idle')
158 self._publish_status(u'idle')
159
159
160 def dispatch_shell(self, stream, msg):
160 def dispatch_shell(self, stream, msg):
161 """dispatch shell requests"""
161 """dispatch shell requests"""
162 # flush control requests first
162 # flush control requests first
163 if self.control_stream:
163 if self.control_stream:
164 self.control_stream.flush()
164 self.control_stream.flush()
165
165
166 idents,msg = self.session.feed_identities(msg, copy=False)
166 idents,msg = self.session.feed_identities(msg, copy=False)
167 try:
167 try:
168 msg = self.session.unserialize(msg, content=True, copy=False)
168 msg = self.session.unserialize(msg, content=True, copy=False)
169 except:
169 except:
170 self.log.error("Invalid Message", exc_info=True)
170 self.log.error("Invalid Message", exc_info=True)
171 return
171 return
172
172
173 # Set the parent message for side effects.
173 # Set the parent message for side effects.
174 self.set_parent(idents, msg)
174 self.set_parent(idents, msg)
175 self._publish_status(u'busy')
175 self._publish_status(u'busy')
176
176
177 header = msg['header']
177 header = msg['header']
178 msg_id = header['msg_id']
178 msg_id = header['msg_id']
179 msg_type = msg['header']['msg_type']
179 msg_type = msg['header']['msg_type']
180
180
181 # Print some info about this message and leave a '--->' marker, so it's
181 # Print some info about this message and leave a '--->' marker, so it's
182 # easier to trace visually the message chain when debugging. Each
182 # easier to trace visually the message chain when debugging. Each
183 # handler prints its message at the end.
183 # handler prints its message at the end.
184 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
184 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
185 self.log.debug(' Content: %s\n --->\n ', msg['content'])
185 self.log.debug(' Content: %s\n --->\n ', msg['content'])
186
186
187 if msg_id in self.aborted:
187 if msg_id in self.aborted:
188 self.aborted.remove(msg_id)
188 self.aborted.remove(msg_id)
189 # is it safe to assume a msg_id will not be resubmitted?
189 # is it safe to assume a msg_id will not be resubmitted?
190 reply_type = msg_type.split('_')[0] + '_reply'
190 reply_type = msg_type.split('_')[0] + '_reply'
191 status = {'status' : 'aborted'}
191 status = {'status' : 'aborted'}
192 md = {'engine' : self.ident}
192 md = {'engine' : self.ident}
193 md.update(status)
193 md.update(status)
194 self.session.send(stream, reply_type, metadata=md,
194 self.session.send(stream, reply_type, metadata=md,
195 content=status, parent=msg, ident=idents)
195 content=status, parent=msg, ident=idents)
196 return
196 return
197
197
198 handler = self.shell_handlers.get(msg_type, None)
198 handler = self.shell_handlers.get(msg_type, None)
199 if handler is None:
199 if handler is None:
200 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
200 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
201 else:
201 else:
202 # ensure default_int_handler during handler call
202 # ensure default_int_handler during handler call
203 sig = signal(SIGINT, default_int_handler)
203 sig = signal(SIGINT, default_int_handler)
204 self.log.debug("%s: %s", msg_type, msg)
204 self.log.debug("%s: %s", msg_type, msg)
205 try:
205 try:
206 handler(stream, idents, msg)
206 handler(stream, idents, msg)
207 except Exception:
207 except Exception:
208 self.log.error("Exception in message handler:", exc_info=True)
208 self.log.error("Exception in message handler:", exc_info=True)
209 finally:
209 finally:
210 signal(SIGINT, sig)
210 signal(SIGINT, sig)
211
211
212 sys.stdout.flush()
212 sys.stdout.flush()
213 sys.stderr.flush()
213 sys.stderr.flush()
214 self._publish_status(u'idle')
214 self._publish_status(u'idle')
215
215
216 def enter_eventloop(self):
216 def enter_eventloop(self):
217 """enter eventloop"""
217 """enter eventloop"""
218 self.log.info("entering eventloop %s", self.eventloop)
218 self.log.info("entering eventloop %s", self.eventloop)
219 for stream in self.shell_streams:
219 for stream in self.shell_streams:
220 # flush any pending replies,
220 # flush any pending replies,
221 # which may be skipped by entering the eventloop
221 # which may be skipped by entering the eventloop
222 stream.flush(zmq.POLLOUT)
222 stream.flush(zmq.POLLOUT)
223 # restore default_int_handler
223 # restore default_int_handler
224 signal(SIGINT, default_int_handler)
224 signal(SIGINT, default_int_handler)
225 while self.eventloop is not None:
225 while self.eventloop is not None:
226 try:
226 try:
227 self.eventloop(self)
227 self.eventloop(self)
228 except KeyboardInterrupt:
228 except KeyboardInterrupt:
229 # Ctrl-C shouldn't crash the kernel
229 # Ctrl-C shouldn't crash the kernel
230 self.log.error("KeyboardInterrupt caught in kernel")
230 self.log.error("KeyboardInterrupt caught in kernel")
231 continue
231 continue
232 else:
232 else:
233 # eventloop exited cleanly, this means we should stop (right?)
233 # eventloop exited cleanly, this means we should stop (right?)
234 self.eventloop = None
234 self.eventloop = None
235 break
235 break
236 self.log.info("exiting eventloop")
236 self.log.info("exiting eventloop")
237
237
238 def start(self):
238 def start(self):
239 """register dispatchers for streams"""
239 """register dispatchers for streams"""
240 if self.control_stream:
240 if self.control_stream:
241 self.control_stream.on_recv(self.dispatch_control, copy=False)
241 self.control_stream.on_recv(self.dispatch_control, copy=False)
242
242
243 def make_dispatcher(stream):
243 def make_dispatcher(stream):
244 def dispatcher(msg):
244 def dispatcher(msg):
245 return self.dispatch_shell(stream, msg)
245 return self.dispatch_shell(stream, msg)
246 return dispatcher
246 return dispatcher
247
247
248 for s in self.shell_streams:
248 for s in self.shell_streams:
249 s.on_recv(make_dispatcher(s), copy=False)
249 s.on_recv(make_dispatcher(s), copy=False)
250
250
251 # publish idle status
251 # publish idle status
252 self._publish_status('starting')
252 self._publish_status('starting')
253
253
254 def do_one_iteration(self):
254 def do_one_iteration(self):
255 """step eventloop just once"""
255 """step eventloop just once"""
256 if self.control_stream:
256 if self.control_stream:
257 self.control_stream.flush()
257 self.control_stream.flush()
258 for stream in self.shell_streams:
258 for stream in self.shell_streams:
259 # handle at most one request per iteration
259 # handle at most one request per iteration
260 stream.flush(zmq.POLLIN, 1)
260 stream.flush(zmq.POLLIN, 1)
261 stream.flush(zmq.POLLOUT)
261 stream.flush(zmq.POLLOUT)
262
262
263
263
264 def record_ports(self, ports):
264 def record_ports(self, ports):
265 """Record the ports that this kernel is using.
265 """Record the ports that this kernel is using.
266
266
267 The creator of the Kernel instance must call this methods if they
267 The creator of the Kernel instance must call this methods if they
268 want the :meth:`connect_request` method to return the port numbers.
268 want the :meth:`connect_request` method to return the port numbers.
269 """
269 """
270 self._recorded_ports = ports
270 self._recorded_ports = ports
271
271
272 #---------------------------------------------------------------------------
272 #---------------------------------------------------------------------------
273 # Kernel request handlers
273 # Kernel request handlers
274 #---------------------------------------------------------------------------
274 #---------------------------------------------------------------------------
275
275
276 def _make_metadata(self, other=None):
276 def _make_metadata(self, other=None):
277 """init metadata dict, for execute/apply_reply"""
277 """init metadata dict, for execute/apply_reply"""
278 new_md = {
278 new_md = {
279 'dependencies_met' : True,
279 'dependencies_met' : True,
280 'engine' : self.ident,
280 'engine' : self.ident,
281 'started': datetime.now(),
281 'started': datetime.now(),
282 }
282 }
283 if other:
283 if other:
284 new_md.update(other)
284 new_md.update(other)
285 return new_md
285 return new_md
286
286
287 def _publish_execute_input(self, code, parent, execution_count):
287 def _publish_execute_input(self, code, parent, execution_count):
288 """Publish the code request on the iopub stream."""
288 """Publish the code request on the iopub stream."""
289
289
290 self.session.send(self.iopub_socket, u'execute_input',
290 self.session.send(self.iopub_socket, u'execute_input',
291 {u'code':code, u'execution_count': execution_count},
291 {u'code':code, u'execution_count': execution_count},
292 parent=parent, ident=self._topic('execute_input')
292 parent=parent, ident=self._topic('execute_input')
293 )
293 )
294
294
295 def _publish_status(self, status, parent=None):
295 def _publish_status(self, status, parent=None):
296 """send status (busy/idle) on IOPub"""
296 """send status (busy/idle) on IOPub"""
297 self.session.send(self.iopub_socket,
297 self.session.send(self.iopub_socket,
298 u'status',
298 u'status',
299 {u'execution_state': status},
299 {u'execution_state': status},
300 parent=parent or self._parent_header,
300 parent=parent or self._parent_header,
301 ident=self._topic('status'),
301 ident=self._topic('status'),
302 )
302 )
303
303
304 def set_parent(self, ident, parent):
304 def set_parent(self, ident, parent):
305 """Set the current parent_header
305 """Set the current parent_header
306
306
307 Side effects (IOPub messages) and replies are associated with
307 Side effects (IOPub messages) and replies are associated with
308 the request that caused them via the parent_header.
308 the request that caused them via the parent_header.
309
309
310 The parent identity is used to route input_request messages
310 The parent identity is used to route input_request messages
311 on the stdin channel.
311 on the stdin channel.
312 """
312 """
313 self._parent_ident = ident
313 self._parent_ident = ident
314 self._parent_header = parent
314 self._parent_header = parent
315
315
316 def send_response(self, stream, msg_or_type, content=None, ident=None,
316 def send_response(self, stream, msg_or_type, content=None, ident=None,
317 buffers=None, track=False, header=None, metadata=None):
317 buffers=None, track=False, header=None, metadata=None):
318 """Send a response to the message we're currently processing.
318 """Send a response to the message we're currently processing.
319
319
320 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
320 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
321 except ``parent``.
321 except ``parent``.
322
322
323 This relies on :meth:`set_parent` having been called for the current
323 This relies on :meth:`set_parent` having been called for the current
324 message.
324 message.
325 """
325 """
326 return self.session.send(stream, msg_or_type, content, self._parent_header,
326 return self.session.send(stream, msg_or_type, content, self._parent_header,
327 ident, buffers, track, header, metadata)
327 ident, buffers, track, header, metadata)
328
328
329 def execute_request(self, stream, ident, parent):
329 def execute_request(self, stream, ident, parent):
330 """handle an execute_request"""
330 """handle an execute_request"""
331
331
332 try:
332 try:
333 content = parent[u'content']
333 content = parent[u'content']
334 code = py3compat.cast_unicode_py2(content[u'code'])
334 code = py3compat.cast_unicode_py2(content[u'code'])
335 silent = content[u'silent']
335 silent = content[u'silent']
336 store_history = content.get(u'store_history', not silent)
336 store_history = content.get(u'store_history', not silent)
337 user_expressions = content.get('user_expressions', {})
337 user_expressions = content.get('user_expressions', {})
338 allow_stdin = content.get('allow_stdin', False)
338 allow_stdin = content.get('allow_stdin', False)
339 except:
339 except:
340 self.log.error("Got bad msg: ")
340 self.log.error("Got bad msg: ")
341 self.log.error("%s", parent)
341 self.log.error("%s", parent)
342 return
342 return
343
343
344 md = self._make_metadata(parent['metadata'])
344 md = self._make_metadata(parent['metadata'])
345
345
346 # Re-broadcast our input for the benefit of listening clients, and
346 # Re-broadcast our input for the benefit of listening clients, and
347 # start computing output
347 # start computing output
348 if not silent:
348 if not silent:
349 self.execution_count += 1
349 self.execution_count += 1
350 self._publish_execute_input(code, parent, self.execution_count)
350 self._publish_execute_input(code, parent, self.execution_count)
351
351
352 reply_content = self.do_execute(code, silent, store_history,
352 reply_content = self.do_execute(code, silent, store_history,
353 user_expressions, allow_stdin)
353 user_expressions, allow_stdin)
354
354
355 # Flush output before sending the reply.
355 # Flush output before sending the reply.
356 sys.stdout.flush()
356 sys.stdout.flush()
357 sys.stderr.flush()
357 sys.stderr.flush()
358 # FIXME: on rare occasions, the flush doesn't seem to make it to the
358 # FIXME: on rare occasions, the flush doesn't seem to make it to the
359 # clients... This seems to mitigate the problem, but we definitely need
359 # clients... This seems to mitigate the problem, but we definitely need
360 # to better understand what's going on.
360 # to better understand what's going on.
361 if self._execute_sleep:
361 if self._execute_sleep:
362 time.sleep(self._execute_sleep)
362 time.sleep(self._execute_sleep)
363
363
364 # Send the reply.
364 # Send the reply.
365 reply_content = json_clean(reply_content)
365 reply_content = json_clean(reply_content)
366
366
367 md['status'] = reply_content['status']
367 md['status'] = reply_content['status']
368 if reply_content['status'] == 'error' and \
368 if reply_content['status'] == 'error' and \
369 reply_content['ename'] == 'UnmetDependency':
369 reply_content['ename'] == 'UnmetDependency':
370 md['dependencies_met'] = False
370 md['dependencies_met'] = False
371
371
372 reply_msg = self.session.send(stream, u'execute_reply',
372 reply_msg = self.session.send(stream, u'execute_reply',
373 reply_content, parent, metadata=md,
373 reply_content, parent, metadata=md,
374 ident=ident)
374 ident=ident)
375
375
376 self.log.debug("%s", reply_msg)
376 self.log.debug("%s", reply_msg)
377
377
378 if not silent and reply_msg['content']['status'] == u'error':
378 if not silent and reply_msg['content']['status'] == u'error':
379 self._abort_queues()
379 self._abort_queues()
380
380
381 def do_execute(self, code, silent, store_history=True,
381 def do_execute(self, code, silent, store_history=True,
382 user_experssions=None, allow_stdin=False):
382 user_experssions=None, allow_stdin=False):
383 """Execute user code. Must be overridden by subclasses.
383 """Execute user code. Must be overridden by subclasses.
384 """
384 """
385 raise NotImplementedError
385 raise NotImplementedError
386
386
387 def complete_request(self, stream, ident, parent):
387 def complete_request(self, stream, ident, parent):
388 content = parent['content']
388 content = parent['content']
389 code = content['code']
389 code = content['code']
390 cursor_pos = content['cursor_pos']
390 cursor_pos = content['cursor_pos']
391
391
392 matches = self.do_complete(code, cursor_pos)
392 matches = self.do_complete(code, cursor_pos)
393 matches = json_clean(matches)
393 matches = json_clean(matches)
394 completion_msg = self.session.send(stream, 'complete_reply',
394 completion_msg = self.session.send(stream, 'complete_reply',
395 matches, parent, ident)
395 matches, parent, ident)
396 self.log.debug("%s", completion_msg)
396 self.log.debug("%s", completion_msg)
397
397
398 def do_complete(self, code, cursor_pos):
398 def do_complete(self, code, cursor_pos):
399 """Override in subclasses to find completions.
399 """Override in subclasses to find completions.
400 """
400 """
401 return {'matches' : [],
401 return {'matches' : [],
402 'cursor_end' : cursor_pos,
402 'cursor_end' : cursor_pos,
403 'cursor_start' : cursor_pos,
403 'cursor_start' : cursor_pos,
404 'metadata' : {},
404 'metadata' : {},
405 'status' : 'ok'}
405 'status' : 'ok'}
406
406
407 def inspect_request(self, stream, ident, parent):
407 def inspect_request(self, stream, ident, parent):
408 content = parent['content']
408 content = parent['content']
409
409
410 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
410 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
411 content.get('detail_level', 0))
411 content.get('detail_level', 0))
412 # Before we send this object over, we scrub it for JSON usage
412 # Before we send this object over, we scrub it for JSON usage
413 reply_content = json_clean(reply_content)
413 reply_content = json_clean(reply_content)
414 msg = self.session.send(stream, 'inspect_reply',
414 msg = self.session.send(stream, 'inspect_reply',
415 reply_content, parent, ident)
415 reply_content, parent, ident)
416 self.log.debug("%s", msg)
416 self.log.debug("%s", msg)
417
417
418 def do_inspect(self, code, cursor_pos, detail_level=0):
418 def do_inspect(self, code, cursor_pos, detail_level=0):
419 """Override in subclasses to allow introspection.
419 """Override in subclasses to allow introspection.
420 """
420 """
421 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
421 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
422
422
423 def history_request(self, stream, ident, parent):
423 def history_request(self, stream, ident, parent):
424 content = parent['content']
424 content = parent['content']
425
425
426 reply_content = self.do_history(**content)
426 reply_content = self.do_history(**content)
427
427
428 reply_content = json_clean(reply_content)
428 reply_content = json_clean(reply_content)
429 msg = self.session.send(stream, 'history_reply',
429 msg = self.session.send(stream, 'history_reply',
430 reply_content, parent, ident)
430 reply_content, parent, ident)
431 self.log.debug("%s", msg)
431 self.log.debug("%s", msg)
432
432
433 def do_history(self, hist_access_type, output, raw, session=None, start=None,
433 def do_history(self, hist_access_type, output, raw, session=None, start=None,
434 stop=None, n=None, pattern=None, unique=False):
434 stop=None, n=None, pattern=None, unique=False):
435 """Override in subclasses to access history.
435 """Override in subclasses to access history.
436 """
436 """
437 return {'history': []}
437 return {'history': []}
438
438
439 def connect_request(self, stream, ident, parent):
439 def connect_request(self, stream, ident, parent):
440 if self._recorded_ports is not None:
440 if self._recorded_ports is not None:
441 content = self._recorded_ports.copy()
441 content = self._recorded_ports.copy()
442 else:
442 else:
443 content = {}
443 content = {}
444 msg = self.session.send(stream, 'connect_reply',
444 msg = self.session.send(stream, 'connect_reply',
445 content, parent, ident)
445 content, parent, ident)
446 self.log.debug("%s", msg)
446 self.log.debug("%s", msg)
447
447
448 @property
448 @property
449 def kernel_info(self):
449 def kernel_info(self):
450 return {
450 return {
451 'protocol_version': release.kernel_protocol_version,
451 'protocol_version': release.kernel_protocol_version,
452 'implementation': self.implementation,
452 'implementation': self.implementation,
453 'implementation_version': self.implementation_version,
453 'implementation_version': self.implementation_version,
454 'language': self.language,
454 'language': self.language,
455 'language_version': self.language_version,
455 'language_version': self.language_version,
456 'banner': self.banner,
456 'banner': self.banner,
457 }
457 }
458
458
459 def kernel_info_request(self, stream, ident, parent):
459 def kernel_info_request(self, stream, ident, parent):
460 msg = self.session.send(stream, 'kernel_info_reply',
460 msg = self.session.send(stream, 'kernel_info_reply',
461 self.kernel_info, parent, ident)
461 self.kernel_info, parent, ident)
462 self.log.debug("%s", msg)
462 self.log.debug("%s", msg)
463
463
464 def shutdown_request(self, stream, ident, parent):
464 def shutdown_request(self, stream, ident, parent):
465 content = self.do_shutdown(parent['content']['restart'])
465 content = self.do_shutdown(parent['content']['restart'])
466 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
466 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
467 # same content, but different msg_id for broadcasting on IOPub
467 # same content, but different msg_id for broadcasting on IOPub
468 self._shutdown_message = self.session.msg(u'shutdown_reply',
468 self._shutdown_message = self.session.msg(u'shutdown_reply',
469 content, parent
469 content, parent
470 )
470 )
471
471
472 self._at_shutdown()
472 self._at_shutdown()
473 # call sys.exit after a short delay
473 # call sys.exit after a short delay
474 loop = ioloop.IOLoop.instance()
474 loop = ioloop.IOLoop.instance()
475 loop.add_timeout(time.time()+0.1, loop.stop)
475 loop.add_timeout(time.time()+0.1, loop.stop)
476
476
477 def do_shutdown(self, restart):
477 def do_shutdown(self, restart):
478 """Override in subclasses to do things when the frontend shuts down the
478 """Override in subclasses to do things when the frontend shuts down the
479 kernel.
479 kernel.
480 """
480 """
481 return {'status': 'ok', 'restart': restart}
481 return {'status': 'ok', 'restart': restart}
482
482
483 def is_complete_request(self, stream, ident, parent):
483 def is_complete_request(self, stream, ident, parent):
484 content = parent['content']
484 content = parent['content']
485 code = content['code']
485 code = content['code']
486
486
487 reply_content = self.do_is_complete(code)
487 reply_content = self.do_is_complete(code)
488 reply_content = json_clean(reply_content)
488 reply_content = json_clean(reply_content)
489 reply_msg = self.session.send(stream, 'is_complete_reply',
489 reply_msg = self.session.send(stream, 'is_complete_reply',
490 reply_content, parent, ident)
490 reply_content, parent, ident)
491 self.log.debug("%s", reply_msg)
491 self.log.debug("%s", reply_msg)
492
492
493 def do_is_complete(self, code):
493 def do_is_complete(self, code):
494 """Override in subclasses to find completions.
494 """Override in subclasses to find completions.
495 """
495 """
496 return {'complete' : True,
496 return {'status' : 'unknown',
497 }
497 }
498
498
499 #---------------------------------------------------------------------------
499 #---------------------------------------------------------------------------
500 # Engine methods
500 # Engine methods
501 #---------------------------------------------------------------------------
501 #---------------------------------------------------------------------------
502
502
503 def apply_request(self, stream, ident, parent):
503 def apply_request(self, stream, ident, parent):
504 try:
504 try:
505 content = parent[u'content']
505 content = parent[u'content']
506 bufs = parent[u'buffers']
506 bufs = parent[u'buffers']
507 msg_id = parent['header']['msg_id']
507 msg_id = parent['header']['msg_id']
508 except:
508 except:
509 self.log.error("Got bad msg: %s", parent, exc_info=True)
509 self.log.error("Got bad msg: %s", parent, exc_info=True)
510 return
510 return
511
511
512 md = self._make_metadata(parent['metadata'])
512 md = self._make_metadata(parent['metadata'])
513
513
514 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
514 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
515
515
516 # put 'ok'/'error' status in header, for scheduler introspection:
516 # put 'ok'/'error' status in header, for scheduler introspection:
517 md['status'] = reply_content['status']
517 md['status'] = reply_content['status']
518
518
519 # flush i/o
519 # flush i/o
520 sys.stdout.flush()
520 sys.stdout.flush()
521 sys.stderr.flush()
521 sys.stderr.flush()
522
522
523 self.session.send(stream, u'apply_reply', reply_content,
523 self.session.send(stream, u'apply_reply', reply_content,
524 parent=parent, ident=ident,buffers=result_buf, metadata=md)
524 parent=parent, ident=ident,buffers=result_buf, metadata=md)
525
525
526 def do_apply(self, content, bufs, msg_id, reply_metadata):
526 def do_apply(self, content, bufs, msg_id, reply_metadata):
527 """Override in subclasses to support the IPython parallel framework.
527 """Override in subclasses to support the IPython parallel framework.
528 """
528 """
529 raise NotImplementedError
529 raise NotImplementedError
530
530
531 #---------------------------------------------------------------------------
531 #---------------------------------------------------------------------------
532 # Control messages
532 # Control messages
533 #---------------------------------------------------------------------------
533 #---------------------------------------------------------------------------
534
534
535 def abort_request(self, stream, ident, parent):
535 def abort_request(self, stream, ident, parent):
536 """abort a specifig msg by id"""
536 """abort a specifig msg by id"""
537 msg_ids = parent['content'].get('msg_ids', None)
537 msg_ids = parent['content'].get('msg_ids', None)
538 if isinstance(msg_ids, string_types):
538 if isinstance(msg_ids, string_types):
539 msg_ids = [msg_ids]
539 msg_ids = [msg_ids]
540 if not msg_ids:
540 if not msg_ids:
541 self.abort_queues()
541 self.abort_queues()
542 for mid in msg_ids:
542 for mid in msg_ids:
543 self.aborted.add(str(mid))
543 self.aborted.add(str(mid))
544
544
545 content = dict(status='ok')
545 content = dict(status='ok')
546 reply_msg = self.session.send(stream, 'abort_reply', content=content,
546 reply_msg = self.session.send(stream, 'abort_reply', content=content,
547 parent=parent, ident=ident)
547 parent=parent, ident=ident)
548 self.log.debug("%s", reply_msg)
548 self.log.debug("%s", reply_msg)
549
549
550 def clear_request(self, stream, idents, parent):
550 def clear_request(self, stream, idents, parent):
551 """Clear our namespace."""
551 """Clear our namespace."""
552 content = self.do_clear()
552 content = self.do_clear()
553 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
553 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
554 content = content)
554 content = content)
555
555
556 def do_clear(self):
556 def do_clear(self):
557 """Override in subclasses to clear the namespace
557 """Override in subclasses to clear the namespace
558
558
559 This is only required for IPython.parallel.
559 This is only required for IPython.parallel.
560 """
560 """
561 raise NotImplementedError
561 raise NotImplementedError
562
562
563 #---------------------------------------------------------------------------
563 #---------------------------------------------------------------------------
564 # Protected interface
564 # Protected interface
565 #---------------------------------------------------------------------------
565 #---------------------------------------------------------------------------
566
566
567 def _topic(self, topic):
567 def _topic(self, topic):
568 """prefixed topic for IOPub messages"""
568 """prefixed topic for IOPub messages"""
569 if self.int_id >= 0:
569 if self.int_id >= 0:
570 base = "engine.%i" % self.int_id
570 base = "engine.%i" % self.int_id
571 else:
571 else:
572 base = "kernel.%s" % self.ident
572 base = "kernel.%s" % self.ident
573
573
574 return py3compat.cast_bytes("%s.%s" % (base, topic))
574 return py3compat.cast_bytes("%s.%s" % (base, topic))
575
575
576 def _abort_queues(self):
576 def _abort_queues(self):
577 for stream in self.shell_streams:
577 for stream in self.shell_streams:
578 if stream:
578 if stream:
579 self._abort_queue(stream)
579 self._abort_queue(stream)
580
580
581 def _abort_queue(self, stream):
581 def _abort_queue(self, stream):
582 poller = zmq.Poller()
582 poller = zmq.Poller()
583 poller.register(stream.socket, zmq.POLLIN)
583 poller.register(stream.socket, zmq.POLLIN)
584 while True:
584 while True:
585 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
585 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
586 if msg is None:
586 if msg is None:
587 return
587 return
588
588
589 self.log.info("Aborting:")
589 self.log.info("Aborting:")
590 self.log.info("%s", msg)
590 self.log.info("%s", msg)
591 msg_type = msg['header']['msg_type']
591 msg_type = msg['header']['msg_type']
592 reply_type = msg_type.split('_')[0] + '_reply'
592 reply_type = msg_type.split('_')[0] + '_reply'
593
593
594 status = {'status' : 'aborted'}
594 status = {'status' : 'aborted'}
595 md = {'engine' : self.ident}
595 md = {'engine' : self.ident}
596 md.update(status)
596 md.update(status)
597 reply_msg = self.session.send(stream, reply_type, metadata=md,
597 reply_msg = self.session.send(stream, reply_type, metadata=md,
598 content=status, parent=msg, ident=idents)
598 content=status, parent=msg, ident=idents)
599 self.log.debug("%s", reply_msg)
599 self.log.debug("%s", reply_msg)
600 # We need to wait a bit for requests to come in. This can probably
600 # We need to wait a bit for requests to come in. This can probably
601 # be set shorter for true asynchronous clients.
601 # be set shorter for true asynchronous clients.
602 poller.poll(50)
602 poller.poll(50)
603
603
604
604
605 def _no_raw_input(self):
605 def _no_raw_input(self):
606 """Raise StdinNotImplentedError if active frontend doesn't support
606 """Raise StdinNotImplentedError if active frontend doesn't support
607 stdin."""
607 stdin."""
608 raise StdinNotImplementedError("raw_input was called, but this "
608 raise StdinNotImplementedError("raw_input was called, but this "
609 "frontend does not support stdin.")
609 "frontend does not support stdin.")
610
610
611 def getpass(self, prompt=''):
611 def getpass(self, prompt=''):
612 """Forward getpass to frontends
612 """Forward getpass to frontends
613
613
614 Raises
614 Raises
615 ------
615 ------
616 StdinNotImplentedError if active frontend doesn't support stdin.
616 StdinNotImplentedError if active frontend doesn't support stdin.
617 """
617 """
618 if not self._allow_stdin:
618 if not self._allow_stdin:
619 raise StdinNotImplementedError(
619 raise StdinNotImplementedError(
620 "getpass was called, but this frontend does not support input requests."
620 "getpass was called, but this frontend does not support input requests."
621 )
621 )
622 return self._input_request(prompt,
622 return self._input_request(prompt,
623 self._parent_ident,
623 self._parent_ident,
624 self._parent_header,
624 self._parent_header,
625 password=True,
625 password=True,
626 )
626 )
627
627
628 def raw_input(self, prompt=''):
628 def raw_input(self, prompt=''):
629 """Forward raw_input to frontends
629 """Forward raw_input to frontends
630
630
631 Raises
631 Raises
632 ------
632 ------
633 StdinNotImplentedError if active frontend doesn't support stdin.
633 StdinNotImplentedError if active frontend doesn't support stdin.
634 """
634 """
635 if not self._allow_stdin:
635 if not self._allow_stdin:
636 raise StdinNotImplementedError(
636 raise StdinNotImplementedError(
637 "raw_input was called, but this frontend does not support input requests."
637 "raw_input was called, but this frontend does not support input requests."
638 )
638 )
639 return self._input_request(prompt,
639 return self._input_request(prompt,
640 self._parent_ident,
640 self._parent_ident,
641 self._parent_header,
641 self._parent_header,
642 password=False,
642 password=False,
643 )
643 )
644
644
645 def _input_request(self, prompt, ident, parent, password=False):
645 def _input_request(self, prompt, ident, parent, password=False):
646 # Flush output before making the request.
646 # Flush output before making the request.
647 sys.stderr.flush()
647 sys.stderr.flush()
648 sys.stdout.flush()
648 sys.stdout.flush()
649 # flush the stdin socket, to purge stale replies
649 # flush the stdin socket, to purge stale replies
650 while True:
650 while True:
651 try:
651 try:
652 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
652 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
653 except zmq.ZMQError as e:
653 except zmq.ZMQError as e:
654 if e.errno == zmq.EAGAIN:
654 if e.errno == zmq.EAGAIN:
655 break
655 break
656 else:
656 else:
657 raise
657 raise
658
658
659 # Send the input request.
659 # Send the input request.
660 content = json_clean(dict(prompt=prompt, password=password))
660 content = json_clean(dict(prompt=prompt, password=password))
661 self.session.send(self.stdin_socket, u'input_request', content, parent,
661 self.session.send(self.stdin_socket, u'input_request', content, parent,
662 ident=ident)
662 ident=ident)
663
663
664 # Await a response.
664 # Await a response.
665 while True:
665 while True:
666 try:
666 try:
667 ident, reply = self.session.recv(self.stdin_socket, 0)
667 ident, reply = self.session.recv(self.stdin_socket, 0)
668 except Exception:
668 except Exception:
669 self.log.warn("Invalid Message:", exc_info=True)
669 self.log.warn("Invalid Message:", exc_info=True)
670 except KeyboardInterrupt:
670 except KeyboardInterrupt:
671 # re-raise KeyboardInterrupt, to truncate traceback
671 # re-raise KeyboardInterrupt, to truncate traceback
672 raise KeyboardInterrupt
672 raise KeyboardInterrupt
673 else:
673 else:
674 break
674 break
675 try:
675 try:
676 value = py3compat.unicode_to_str(reply['content']['value'])
676 value = py3compat.unicode_to_str(reply['content']['value'])
677 except:
677 except:
678 self.log.error("Bad input_reply: %s", parent)
678 self.log.error("Bad input_reply: %s", parent)
679 value = ''
679 value = ''
680 if value == '\x04':
680 if value == '\x04':
681 # EOF
681 # EOF
682 raise EOFError
682 raise EOFError
683 return value
683 return value
684
684
685 def _at_shutdown(self):
685 def _at_shutdown(self):
686 """Actions taken at shutdown by the kernel, called by python's atexit.
686 """Actions taken at shutdown by the kernel, called by python's atexit.
687 """
687 """
688 # io.rprint("Kernel at_shutdown") # dbg
688 # io.rprint("Kernel at_shutdown") # dbg
689 if self._shutdown_message is not None:
689 if self._shutdown_message is not None:
690 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
690 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
691 self.log.debug("%s", self._shutdown_message)
691 self.log.debug("%s", self._shutdown_message)
692 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
692 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
693
693
@@ -1,1095 +1,1111 b''
1 .. _messaging:
1 .. _messaging:
2
2
3 ======================
3 ======================
4 Messaging in IPython
4 Messaging in IPython
5 ======================
5 ======================
6
6
7
7
8 Versioning
8 Versioning
9 ==========
9 ==========
10
10
11 The IPython message specification is versioned independently of IPython.
11 The IPython message specification is versioned independently of IPython.
12 The current version of the specification is 5.1.
12 The current version of the specification is 5.1.
13
13
14
14
15 Introduction
15 Introduction
16 ============
16 ============
17
17
18 This document explains the basic communications design and messaging
18 This document explains the basic communications design and messaging
19 specification for how the various IPython objects interact over a network
19 specification for how the various IPython objects interact over a network
20 transport. The current implementation uses the ZeroMQ_ library for messaging
20 transport. The current implementation uses the ZeroMQ_ library for messaging
21 within and between hosts.
21 within and between hosts.
22
22
23 .. Note::
23 .. Note::
24
24
25 This document should be considered the authoritative description of the
25 This document should be considered the authoritative description of the
26 IPython messaging protocol, and all developers are strongly encouraged to
26 IPython messaging protocol, and all developers are strongly encouraged to
27 keep it updated as the implementation evolves, so that we have a single
27 keep it updated as the implementation evolves, so that we have a single
28 common reference for all protocol details.
28 common reference for all protocol details.
29
29
30 The basic design is explained in the following diagram:
30 The basic design is explained in the following diagram:
31
31
32 .. image:: figs/frontend-kernel.png
32 .. image:: figs/frontend-kernel.png
33 :width: 450px
33 :width: 450px
34 :alt: IPython kernel/frontend messaging architecture.
34 :alt: IPython kernel/frontend messaging architecture.
35 :align: center
35 :align: center
36 :target: ../_images/frontend-kernel.png
36 :target: ../_images/frontend-kernel.png
37
37
38 A single kernel can be simultaneously connected to one or more frontends. The
38 A single kernel can be simultaneously connected to one or more frontends. The
39 kernel has three sockets that serve the following functions:
39 kernel has three sockets that serve the following functions:
40
40
41 1. Shell: this single ROUTER socket allows multiple incoming connections from
41 1. Shell: this single ROUTER socket allows multiple incoming connections from
42 frontends, and this is the socket where requests for code execution, object
42 frontends, and this is the socket where requests for code execution, object
43 information, prompts, etc. are made to the kernel by any frontend. The
43 information, prompts, etc. are made to the kernel by any frontend. The
44 communication on this socket is a sequence of request/reply actions from
44 communication on this socket is a sequence of request/reply actions from
45 each frontend and the kernel.
45 each frontend and the kernel.
46
46
47 2. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
47 2. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
48 side effects (stdout, stderr, etc.) as well as the requests coming from any
48 side effects (stdout, stderr, etc.) as well as the requests coming from any
49 client over the shell socket and its own requests on the stdin socket. There
49 client over the shell socket and its own requests on the stdin socket. There
50 are a number of actions in Python which generate side effects: :func:`print`
50 are a number of actions in Python which generate side effects: :func:`print`
51 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
51 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
52 a multi-client scenario, we want all frontends to be able to know what each
52 a multi-client scenario, we want all frontends to be able to know what each
53 other has sent to the kernel (this can be useful in collaborative scenarios,
53 other has sent to the kernel (this can be useful in collaborative scenarios,
54 for example). This socket allows both side effects and the information
54 for example). This socket allows both side effects and the information
55 about communications taking place with one client over the shell channel
55 about communications taking place with one client over the shell channel
56 to be made available to all clients in a uniform manner.
56 to be made available to all clients in a uniform manner.
57
57
58 3. stdin: this ROUTER socket is connected to all frontends, and it allows
58 3. stdin: this ROUTER socket is connected to all frontends, and it allows
59 the kernel to request input from the active frontend when :func:`raw_input` is called.
59 the kernel to request input from the active frontend when :func:`raw_input` is called.
60 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
60 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
61 for the kernel while this communication is happening (illustrated in the
61 for the kernel while this communication is happening (illustrated in the
62 figure by the black outline around the central keyboard). In practice,
62 figure by the black outline around the central keyboard). In practice,
63 frontends may display such kernel requests using a special input widget or
63 frontends may display such kernel requests using a special input widget or
64 otherwise indicating that the user is to type input for the kernel instead
64 otherwise indicating that the user is to type input for the kernel instead
65 of normal commands in the frontend.
65 of normal commands in the frontend.
66
66
67 All messages are tagged with enough information (details below) for clients
67 All messages are tagged with enough information (details below) for clients
68 to know which messages come from their own interaction with the kernel and
68 to know which messages come from their own interaction with the kernel and
69 which ones are from other clients, so they can display each type
69 which ones are from other clients, so they can display each type
70 appropriately.
70 appropriately.
71
71
72 4. Control: This channel is identical to Shell, but operates on a separate socket,
72 4. Control: This channel is identical to Shell, but operates on a separate socket,
73 to allow important messages to avoid queueing behind execution requests (e.g. shutdown or abort).
73 to allow important messages to avoid queueing behind execution requests (e.g. shutdown or abort).
74
74
75 The actual format of the messages allowed on each of these channels is
75 The actual format of the messages allowed on each of these channels is
76 specified below. Messages are dicts of dicts with string keys and values that
76 specified below. Messages are dicts of dicts with string keys and values that
77 are reasonably representable in JSON. Our current implementation uses JSON
77 are reasonably representable in JSON. Our current implementation uses JSON
78 explicitly as its message format, but this shouldn't be considered a permanent
78 explicitly as its message format, but this shouldn't be considered a permanent
79 feature. As we've discovered that JSON has non-trivial performance issues due
79 feature. As we've discovered that JSON has non-trivial performance issues due
80 to excessive copying, we may in the future move to a pure pickle-based raw
80 to excessive copying, we may in the future move to a pure pickle-based raw
81 message format. However, it should be possible to easily convert from the raw
81 message format. However, it should be possible to easily convert from the raw
82 objects to JSON, since we may have non-python clients (e.g. a web frontend).
82 objects to JSON, since we may have non-python clients (e.g. a web frontend).
83 As long as it's easy to make a JSON version of the objects that is a faithful
83 As long as it's easy to make a JSON version of the objects that is a faithful
84 representation of all the data, we can communicate with such clients.
84 representation of all the data, we can communicate with such clients.
85
85
86 .. Note::
86 .. Note::
87
87
88 Not all of these have yet been fully fleshed out, but the key ones are, see
88 Not all of these have yet been fully fleshed out, but the key ones are, see
89 kernel and frontend files for actual implementation details.
89 kernel and frontend files for actual implementation details.
90
90
91 General Message Format
91 General Message Format
92 ======================
92 ======================
93
93
94 A message is defined by the following four-dictionary structure::
94 A message is defined by the following four-dictionary structure::
95
95
96 {
96 {
97 # The message header contains a pair of unique identifiers for the
97 # The message header contains a pair of unique identifiers for the
98 # originating session and the actual message id, in addition to the
98 # originating session and the actual message id, in addition to the
99 # username for the process that generated the message. This is useful in
99 # username for the process that generated the message. This is useful in
100 # collaborative settings where multiple users may be interacting with the
100 # collaborative settings where multiple users may be interacting with the
101 # same kernel simultaneously, so that frontends can label the various
101 # same kernel simultaneously, so that frontends can label the various
102 # messages in a meaningful way.
102 # messages in a meaningful way.
103 'header' : {
103 'header' : {
104 'msg_id' : uuid,
104 'msg_id' : uuid,
105 'username' : str,
105 'username' : str,
106 'session' : uuid,
106 'session' : uuid,
107 # All recognized message type strings are listed below.
107 # All recognized message type strings are listed below.
108 'msg_type' : str,
108 'msg_type' : str,
109 # the message protocol version
109 # the message protocol version
110 'version' : '5.0',
110 'version' : '5.0',
111 },
111 },
112
112
113 # In a chain of messages, the header from the parent is copied so that
113 # In a chain of messages, the header from the parent is copied so that
114 # clients can track where messages come from.
114 # clients can track where messages come from.
115 'parent_header' : dict,
115 'parent_header' : dict,
116
116
117 # Any metadata associated with the message.
117 # Any metadata associated with the message.
118 'metadata' : dict,
118 'metadata' : dict,
119
119
120 # The actual content of the message must be a dict, whose structure
120 # The actual content of the message must be a dict, whose structure
121 # depends on the message type.
121 # depends on the message type.
122 'content' : dict,
122 'content' : dict,
123 }
123 }
124
124
125 .. versionchanged:: 5.0
125 .. versionchanged:: 5.0
126
126
127 ``version`` key added to the header.
127 ``version`` key added to the header.
128
128
129 .. _wire_protocol:
129 .. _wire_protocol:
130
130
131 The Wire Protocol
131 The Wire Protocol
132 =================
132 =================
133
133
134
134
135 This message format exists at a high level,
135 This message format exists at a high level,
136 but does not describe the actual *implementation* at the wire level in zeromq.
136 but does not describe the actual *implementation* at the wire level in zeromq.
137 The canonical implementation of the message spec is our :class:`~IPython.kernel.zmq.session.Session` class.
137 The canonical implementation of the message spec is our :class:`~IPython.kernel.zmq.session.Session` class.
138
138
139 .. note::
139 .. note::
140
140
141 This section should only be relevant to non-Python consumers of the protocol.
141 This section should only be relevant to non-Python consumers of the protocol.
142 Python consumers should simply import and use IPython's own implementation of the wire protocol
142 Python consumers should simply import and use IPython's own implementation of the wire protocol
143 in the :class:`IPython.kernel.zmq.session.Session` object.
143 in the :class:`IPython.kernel.zmq.session.Session` object.
144
144
145 Every message is serialized to a sequence of at least six blobs of bytes:
145 Every message is serialized to a sequence of at least six blobs of bytes:
146
146
147 .. sourcecode:: python
147 .. sourcecode:: python
148
148
149 [
149 [
150 b'u-u-i-d', # zmq identity(ies)
150 b'u-u-i-d', # zmq identity(ies)
151 b'<IDS|MSG>', # delimiter
151 b'<IDS|MSG>', # delimiter
152 b'baddad42', # HMAC signature
152 b'baddad42', # HMAC signature
153 b'{header}', # serialized header dict
153 b'{header}', # serialized header dict
154 b'{parent_header}', # serialized parent header dict
154 b'{parent_header}', # serialized parent header dict
155 b'{metadata}', # serialized metadata dict
155 b'{metadata}', # serialized metadata dict
156 b'{content}, # serialized content dict
156 b'{content}, # serialized content dict
157 b'blob', # extra raw data buffer(s)
157 b'blob', # extra raw data buffer(s)
158 ...
158 ...
159 ]
159 ]
160
160
161 The front of the message is the ZeroMQ routing prefix,
161 The front of the message is the ZeroMQ routing prefix,
162 which can be zero or more socket identities.
162 which can be zero or more socket identities.
163 This is every piece of the message prior to the delimiter key ``<IDS|MSG>``.
163 This is every piece of the message prior to the delimiter key ``<IDS|MSG>``.
164 In the case of IOPub, there should be just one prefix component,
164 In the case of IOPub, there should be just one prefix component,
165 which is the topic for IOPub subscribers, e.g. ``execute_result``, ``display_data``.
165 which is the topic for IOPub subscribers, e.g. ``execute_result``, ``display_data``.
166
166
167 .. note::
167 .. note::
168
168
169 In most cases, the IOPub topics are irrelevant and completely ignored,
169 In most cases, the IOPub topics are irrelevant and completely ignored,
170 because frontends just subscribe to all topics.
170 because frontends just subscribe to all topics.
171 The convention used in the IPython kernel is to use the msg_type as the topic,
171 The convention used in the IPython kernel is to use the msg_type as the topic,
172 and possibly extra information about the message, e.g. ``execute_result`` or ``stream.stdout``
172 and possibly extra information about the message, e.g. ``execute_result`` or ``stream.stdout``
173
173
174 After the delimiter is the `HMAC`_ signature of the message, used for authentication.
174 After the delimiter is the `HMAC`_ signature of the message, used for authentication.
175 If authentication is disabled, this should be an empty string.
175 If authentication is disabled, this should be an empty string.
176 By default, the hashing function used for computing these signatures is sha256.
176 By default, the hashing function used for computing these signatures is sha256.
177
177
178 .. _HMAC: http://en.wikipedia.org/wiki/HMAC
178 .. _HMAC: http://en.wikipedia.org/wiki/HMAC
179
179
180 .. note::
180 .. note::
181
181
182 To disable authentication and signature checking,
182 To disable authentication and signature checking,
183 set the `key` field of a connection file to an empty string.
183 set the `key` field of a connection file to an empty string.
184
184
185 The signature is the HMAC hex digest of the concatenation of:
185 The signature is the HMAC hex digest of the concatenation of:
186
186
187 - A shared key (typically the ``key`` field of a connection file)
187 - A shared key (typically the ``key`` field of a connection file)
188 - The serialized header dict
188 - The serialized header dict
189 - The serialized parent header dict
189 - The serialized parent header dict
190 - The serialized metadata dict
190 - The serialized metadata dict
191 - The serialized content dict
191 - The serialized content dict
192
192
193 In Python, this is implemented via:
193 In Python, this is implemented via:
194
194
195 .. sourcecode:: python
195 .. sourcecode:: python
196
196
197 # once:
197 # once:
198 digester = HMAC(key, digestmod=hashlib.sha256)
198 digester = HMAC(key, digestmod=hashlib.sha256)
199
199
200 # for each message
200 # for each message
201 d = digester.copy()
201 d = digester.copy()
202 for serialized_dict in (header, parent, metadata, content):
202 for serialized_dict in (header, parent, metadata, content):
203 d.update(serialized_dict)
203 d.update(serialized_dict)
204 signature = d.hexdigest()
204 signature = d.hexdigest()
205
205
206 After the signature is the actual message, always in four frames of bytes.
206 After the signature is the actual message, always in four frames of bytes.
207 The four dictionaries that compose a message are serialized separately,
207 The four dictionaries that compose a message are serialized separately,
208 in the order of header, parent header, metadata, and content.
208 in the order of header, parent header, metadata, and content.
209 These can be serialized by any function that turns a dict into bytes.
209 These can be serialized by any function that turns a dict into bytes.
210 The default and most common serialization is JSON, but msgpack and pickle
210 The default and most common serialization is JSON, but msgpack and pickle
211 are common alternatives.
211 are common alternatives.
212
212
213 After the serialized dicts are zero to many raw data buffers,
213 After the serialized dicts are zero to many raw data buffers,
214 which can be used by message types that support binary data (mainly apply and data_pub).
214 which can be used by message types that support binary data (mainly apply and data_pub).
215
215
216
216
217 Python functional API
217 Python functional API
218 =====================
218 =====================
219
219
220 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
220 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
221 should develop, at a few key points, functional forms of all the requests that
221 should develop, at a few key points, functional forms of all the requests that
222 take arguments in this manner and automatically construct the necessary dict
222 take arguments in this manner and automatically construct the necessary dict
223 for sending.
223 for sending.
224
224
225 In addition, the Python implementation of the message specification extends
225 In addition, the Python implementation of the message specification extends
226 messages upon deserialization to the following form for convenience::
226 messages upon deserialization to the following form for convenience::
227
227
228 {
228 {
229 'header' : dict,
229 'header' : dict,
230 # The msg's unique identifier and type are always stored in the header,
230 # The msg's unique identifier and type are always stored in the header,
231 # but the Python implementation copies them to the top level.
231 # but the Python implementation copies them to the top level.
232 'msg_id' : uuid,
232 'msg_id' : uuid,
233 'msg_type' : str,
233 'msg_type' : str,
234 'parent_header' : dict,
234 'parent_header' : dict,
235 'content' : dict,
235 'content' : dict,
236 'metadata' : dict,
236 'metadata' : dict,
237 }
237 }
238
238
239 All messages sent to or received by any IPython process should have this
239 All messages sent to or received by any IPython process should have this
240 extended structure.
240 extended structure.
241
241
242
242
243 Messages on the shell ROUTER/DEALER sockets
243 Messages on the shell ROUTER/DEALER sockets
244 ===========================================
244 ===========================================
245
245
246 .. _execute:
246 .. _execute:
247
247
248 Execute
248 Execute
249 -------
249 -------
250
250
251 This message type is used by frontends to ask the kernel to execute code on
251 This message type is used by frontends to ask the kernel to execute code on
252 behalf of the user, in a namespace reserved to the user's variables (and thus
252 behalf of the user, in a namespace reserved to the user's variables (and thus
253 separate from the kernel's own internal code and variables).
253 separate from the kernel's own internal code and variables).
254
254
255 Message type: ``execute_request``::
255 Message type: ``execute_request``::
256
256
257 content = {
257 content = {
258 # Source code to be executed by the kernel, one or more lines.
258 # Source code to be executed by the kernel, one or more lines.
259 'code' : str,
259 'code' : str,
260
260
261 # A boolean flag which, if True, signals the kernel to execute
261 # A boolean flag which, if True, signals the kernel to execute
262 # this code as quietly as possible.
262 # this code as quietly as possible.
263 # silent=True forces store_history to be False,
263 # silent=True forces store_history to be False,
264 # and will *not*:
264 # and will *not*:
265 # - broadcast output on the IOPUB channel
265 # - broadcast output on the IOPUB channel
266 # - have an execute_result
266 # - have an execute_result
267 # The default is False.
267 # The default is False.
268 'silent' : bool,
268 'silent' : bool,
269
269
270 # A boolean flag which, if True, signals the kernel to populate history
270 # A boolean flag which, if True, signals the kernel to populate history
271 # The default is True if silent is False. If silent is True, store_history
271 # The default is True if silent is False. If silent is True, store_history
272 # is forced to be False.
272 # is forced to be False.
273 'store_history' : bool,
273 'store_history' : bool,
274
274
275 # A dict mapping names to expressions to be evaluated in the
275 # A dict mapping names to expressions to be evaluated in the
276 # user's dict. The rich display-data representation of each will be evaluated after execution.
276 # user's dict. The rich display-data representation of each will be evaluated after execution.
277 # See the display_data content for the structure of the representation data.
277 # See the display_data content for the structure of the representation data.
278 'user_expressions' : dict,
278 'user_expressions' : dict,
279
279
280 # Some frontends do not support stdin requests.
280 # Some frontends do not support stdin requests.
281 # If raw_input is called from code executed from such a frontend,
281 # If raw_input is called from code executed from such a frontend,
282 # a StdinNotImplementedError will be raised.
282 # a StdinNotImplementedError will be raised.
283 'allow_stdin' : True,
283 'allow_stdin' : True,
284 }
284 }
285
285
286 .. versionchanged:: 5.0
286 .. versionchanged:: 5.0
287
287
288 ``user_variables`` removed, because it is redundant with user_expressions.
288 ``user_variables`` removed, because it is redundant with user_expressions.
289
289
290 The ``code`` field contains a single string (possibly multiline) to be executed.
290 The ``code`` field contains a single string (possibly multiline) to be executed.
291
291
292 The ``user_expressions`` field deserves a detailed explanation. In the past, IPython had
292 The ``user_expressions`` field deserves a detailed explanation. In the past, IPython had
293 the notion of a prompt string that allowed arbitrary code to be evaluated, and
293 the notion of a prompt string that allowed arbitrary code to be evaluated, and
294 this was put to good use by many in creating prompts that displayed system
294 this was put to good use by many in creating prompts that displayed system
295 status, path information, and even more esoteric uses like remote instrument
295 status, path information, and even more esoteric uses like remote instrument
296 status acquired over the network. But now that IPython has a clean separation
296 status acquired over the network. But now that IPython has a clean separation
297 between the kernel and the clients, the kernel has no prompt knowledge; prompts
297 between the kernel and the clients, the kernel has no prompt knowledge; prompts
298 are a frontend feature, and it should be even possible for different
298 are a frontend feature, and it should be even possible for different
299 frontends to display different prompts while interacting with the same kernel.
299 frontends to display different prompts while interacting with the same kernel.
300 ``user_expressions`` can be used to retrieve this information.
300 ``user_expressions`` can be used to retrieve this information.
301
301
302 Any error in evaluating any expression in ``user_expressions`` will result in
302 Any error in evaluating any expression in ``user_expressions`` will result in
303 only that key containing a standard error message, of the form::
303 only that key containing a standard error message, of the form::
304
304
305 {
305 {
306 'status' : 'error',
306 'status' : 'error',
307 'ename' : 'NameError',
307 'ename' : 'NameError',
308 'evalue' : 'foo',
308 'evalue' : 'foo',
309 'traceback' : ...
309 'traceback' : ...
310 }
310 }
311
311
312 .. Note::
312 .. Note::
313
313
314 In order to obtain the current execution counter for the purposes of
314 In order to obtain the current execution counter for the purposes of
315 displaying input prompts, frontends may make an execution request with an
315 displaying input prompts, frontends may make an execution request with an
316 empty code string and ``silent=True``.
316 empty code string and ``silent=True``.
317
317
318 Upon completion of the execution request, the kernel *always* sends a reply,
318 Upon completion of the execution request, the kernel *always* sends a reply,
319 with a status code indicating what happened and additional data depending on
319 with a status code indicating what happened and additional data depending on
320 the outcome. See :ref:`below <execution_results>` for the possible return
320 the outcome. See :ref:`below <execution_results>` for the possible return
321 codes and associated data.
321 codes and associated data.
322
322
323 .. seealso::
323 .. seealso::
324
324
325 :ref:`execution_semantics`
325 :ref:`execution_semantics`
326
326
327 .. _execution_counter:
327 .. _execution_counter:
328
328
329 Execution counter (prompt number)
329 Execution counter (prompt number)
330 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
330 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
331
331
332 The kernel should have a single, monotonically increasing counter of all execution
332 The kernel should have a single, monotonically increasing counter of all execution
333 requests that are made with ``store_history=True``. This counter is used to populate
333 requests that are made with ``store_history=True``. This counter is used to populate
334 the ``In[n]`` and ``Out[n]`` prompts. The value of this counter will be returned as the
334 the ``In[n]`` and ``Out[n]`` prompts. The value of this counter will be returned as the
335 ``execution_count`` field of all ``execute_reply`` and ``execute_input`` messages.
335 ``execution_count`` field of all ``execute_reply`` and ``execute_input`` messages.
336
336
337 .. _execution_results:
337 .. _execution_results:
338
338
339 Execution results
339 Execution results
340 ~~~~~~~~~~~~~~~~~
340 ~~~~~~~~~~~~~~~~~
341
341
342 Message type: ``execute_reply``::
342 Message type: ``execute_reply``::
343
343
344 content = {
344 content = {
345 # One of: 'ok' OR 'error' OR 'abort'
345 # One of: 'ok' OR 'error' OR 'abort'
346 'status' : str,
346 'status' : str,
347
347
348 # The global kernel counter that increases by one with each request that
348 # The global kernel counter that increases by one with each request that
349 # stores history. This will typically be used by clients to display
349 # stores history. This will typically be used by clients to display
350 # prompt numbers to the user. If the request did not store history, this will
350 # prompt numbers to the user. If the request did not store history, this will
351 # be the current value of the counter in the kernel.
351 # be the current value of the counter in the kernel.
352 'execution_count' : int,
352 'execution_count' : int,
353 }
353 }
354
354
355 When status is 'ok', the following extra fields are present::
355 When status is 'ok', the following extra fields are present::
356
356
357 {
357 {
358 # 'payload' will be a list of payload dicts.
358 # 'payload' will be a list of payload dicts.
359 # Each execution payload is a dict with string keys that may have been
359 # Each execution payload is a dict with string keys that may have been
360 # produced by the code being executed. It is retrieved by the kernel at
360 # produced by the code being executed. It is retrieved by the kernel at
361 # the end of the execution and sent back to the front end, which can take
361 # the end of the execution and sent back to the front end, which can take
362 # action on it as needed.
362 # action on it as needed.
363 # The only requirement of each payload dict is that it have a 'source' key,
363 # The only requirement of each payload dict is that it have a 'source' key,
364 # which is a string classifying the payload (e.g. 'pager').
364 # which is a string classifying the payload (e.g. 'pager').
365 'payload' : list(dict),
365 'payload' : list(dict),
366
366
367 # Results for the user_expressions.
367 # Results for the user_expressions.
368 'user_expressions' : dict,
368 'user_expressions' : dict,
369 }
369 }
370
370
371 .. versionchanged:: 5.0
371 .. versionchanged:: 5.0
372
372
373 ``user_variables`` is removed, use user_expressions instead.
373 ``user_variables`` is removed, use user_expressions instead.
374
374
375 .. admonition:: Execution payloads
375 .. admonition:: Execution payloads
376
376
377 The notion of an 'execution payload' is different from a return value of a
377 The notion of an 'execution payload' is different from a return value of a
378 given set of code, which normally is just displayed on the execute_result stream
378 given set of code, which normally is just displayed on the execute_result stream
379 through the PUB socket. The idea of a payload is to allow special types of
379 through the PUB socket. The idea of a payload is to allow special types of
380 code, typically magics, to populate a data container in the IPython kernel
380 code, typically magics, to populate a data container in the IPython kernel
381 that will be shipped back to the caller via this channel. The kernel
381 that will be shipped back to the caller via this channel. The kernel
382 has an API for this in the PayloadManager::
382 has an API for this in the PayloadManager::
383
383
384 ip.payload_manager.write_payload(payload_dict)
384 ip.payload_manager.write_payload(payload_dict)
385
385
386 which appends a dictionary to the list of payloads.
386 which appends a dictionary to the list of payloads.
387
387
388 The payload API is not yet stabilized,
388 The payload API is not yet stabilized,
389 and should probably not be supported by non-Python kernels at this time.
389 and should probably not be supported by non-Python kernels at this time.
390 In such cases, the payload list should always be empty.
390 In such cases, the payload list should always be empty.
391
391
392
392
393 When status is 'error', the following extra fields are present::
393 When status is 'error', the following extra fields are present::
394
394
395 {
395 {
396 'ename' : str, # Exception name, as a string
396 'ename' : str, # Exception name, as a string
397 'evalue' : str, # Exception value, as a string
397 'evalue' : str, # Exception value, as a string
398
398
399 # The traceback will contain a list of frames, represented each as a
399 # The traceback will contain a list of frames, represented each as a
400 # string. For now we'll stick to the existing design of ultraTB, which
400 # string. For now we'll stick to the existing design of ultraTB, which
401 # controls exception level of detail statefully. But eventually we'll
401 # controls exception level of detail statefully. But eventually we'll
402 # want to grow into a model where more information is collected and
402 # want to grow into a model where more information is collected and
403 # packed into the traceback object, with clients deciding how little or
403 # packed into the traceback object, with clients deciding how little or
404 # how much of it to unpack. But for now, let's start with a simple list
404 # how much of it to unpack. But for now, let's start with a simple list
405 # of strings, since that requires only minimal changes to ultratb as
405 # of strings, since that requires only minimal changes to ultratb as
406 # written.
406 # written.
407 'traceback' : list,
407 'traceback' : list,
408 }
408 }
409
409
410
410
411 When status is 'abort', there are for now no additional data fields. This
411 When status is 'abort', there are for now no additional data fields. This
412 happens when the kernel was interrupted by a signal.
412 happens when the kernel was interrupted by a signal.
413
413
414 .. _msging_inspection:
414 .. _msging_inspection:
415
415
416 Introspection
416 Introspection
417 -------------
417 -------------
418
418
419 Code can be inspected to show useful information to the user.
419 Code can be inspected to show useful information to the user.
420 It is up to the Kernel to decide what information should be displayed, and its formatting.
420 It is up to the Kernel to decide what information should be displayed, and its formatting.
421
421
422 Message type: ``inspect_request``::
422 Message type: ``inspect_request``::
423
423
424 content = {
424 content = {
425 # The code context in which introspection is requested
425 # The code context in which introspection is requested
426 # this may be up to an entire multiline cell.
426 # this may be up to an entire multiline cell.
427 'code' : str,
427 'code' : str,
428
428
429 # The cursor position within 'code' (in unicode characters) where inspection is requested
429 # The cursor position within 'code' (in unicode characters) where inspection is requested
430 'cursor_pos' : int,
430 'cursor_pos' : int,
431
431
432 # The level of detail desired. In IPython, the default (0) is equivalent to typing
432 # The level of detail desired. In IPython, the default (0) is equivalent to typing
433 # 'x?' at the prompt, 1 is equivalent to 'x??'.
433 # 'x?' at the prompt, 1 is equivalent to 'x??'.
434 # The difference is up to kernels, but in IPython level 1 includes the source code
434 # The difference is up to kernels, but in IPython level 1 includes the source code
435 # if available.
435 # if available.
436 'detail_level' : 0 or 1,
436 'detail_level' : 0 or 1,
437 }
437 }
438
438
439 .. versionchanged:: 5.0
439 .. versionchanged:: 5.0
440
440
441 ``object_info_request`` renamed to ``inspect_request``.
441 ``object_info_request`` renamed to ``inspect_request``.
442
442
443 .. versionchanged:: 5.0
443 .. versionchanged:: 5.0
444
444
445 ``name`` key replaced with ``code`` and ``cursor_pos``,
445 ``name`` key replaced with ``code`` and ``cursor_pos``,
446 moving the lexing responsibility to the kernel.
446 moving the lexing responsibility to the kernel.
447
447
448 The reply is a mime-bundle, like a `display_data`_ message,
448 The reply is a mime-bundle, like a `display_data`_ message,
449 which should be a formatted representation of information about the context.
449 which should be a formatted representation of information about the context.
450 In the notebook, this is used to show tooltips over function calls, etc.
450 In the notebook, this is used to show tooltips over function calls, etc.
451
451
452 Message type: ``inspect_reply``::
452 Message type: ``inspect_reply``::
453
453
454 content = {
454 content = {
455 # 'ok' if the request succeeded or 'error', with error information as in all other replies.
455 # 'ok' if the request succeeded or 'error', with error information as in all other replies.
456 'status' : 'ok',
456 'status' : 'ok',
457
457
458 # data can be empty if nothing is found
458 # data can be empty if nothing is found
459 'data' : dict,
459 'data' : dict,
460 'metadata' : dict,
460 'metadata' : dict,
461 }
461 }
462
462
463 .. versionchanged:: 5.0
463 .. versionchanged:: 5.0
464
464
465 ``object_info_reply`` renamed to ``inspect_reply``.
465 ``object_info_reply`` renamed to ``inspect_reply``.
466
466
467 .. versionchanged:: 5.0
467 .. versionchanged:: 5.0
468
468
469 Reply is changed from structured data to a mime bundle, allowing formatting decisions to be made by the kernel.
469 Reply is changed from structured data to a mime bundle, allowing formatting decisions to be made by the kernel.
470
470
471 .. _msging_completion:
471 .. _msging_completion:
472
472
473 Completion
473 Completion
474 ----------
474 ----------
475
475
476 Message type: ``complete_request``::
476 Message type: ``complete_request``::
477
477
478 content = {
478 content = {
479 # The code context in which completion is requested
479 # The code context in which completion is requested
480 # this may be up to an entire multiline cell, such as
480 # this may be up to an entire multiline cell, such as
481 # 'foo = a.isal'
481 # 'foo = a.isal'
482 'code' : str,
482 'code' : str,
483
483
484 # The cursor position within 'code' (in unicode characters) where completion is requested
484 # The cursor position within 'code' (in unicode characters) where completion is requested
485 'cursor_pos' : int,
485 'cursor_pos' : int,
486 }
486 }
487
487
488 .. versionchanged:: 5.0
488 .. versionchanged:: 5.0
489
489
490 ``line``, ``block``, and ``text`` keys are removed in favor of a single ``code`` for context.
490 ``line``, ``block``, and ``text`` keys are removed in favor of a single ``code`` for context.
491 Lexing is up to the kernel.
491 Lexing is up to the kernel.
492
492
493
493
494 Message type: ``complete_reply``::
494 Message type: ``complete_reply``::
495
495
496 content = {
496 content = {
497 # The list of all matches to the completion request, such as
497 # The list of all matches to the completion request, such as
498 # ['a.isalnum', 'a.isalpha'] for the above example.
498 # ['a.isalnum', 'a.isalpha'] for the above example.
499 'matches' : list,
499 'matches' : list,
500
500
501 # The range of text that should be replaced by the above matches when a completion is accepted.
501 # The range of text that should be replaced by the above matches when a completion is accepted.
502 # typically cursor_end is the same as cursor_pos in the request.
502 # typically cursor_end is the same as cursor_pos in the request.
503 'cursor_start' : int,
503 'cursor_start' : int,
504 'cursor_end' : int,
504 'cursor_end' : int,
505
505
506 # Information that frontend plugins might use for extra display information about completions.
506 # Information that frontend plugins might use for extra display information about completions.
507 'metadata' : dict,
507 'metadata' : dict,
508
508
509 # status should be 'ok' unless an exception was raised during the request,
509 # status should be 'ok' unless an exception was raised during the request,
510 # in which case it should be 'error', along with the usual error message content
510 # in which case it should be 'error', along with the usual error message content
511 # in other messages.
511 # in other messages.
512 'status' : 'ok'
512 'status' : 'ok'
513 }
513 }
514
514
515 .. versionchanged:: 5.0
515 .. versionchanged:: 5.0
516
516
517 - ``matched_text`` is removed in favor of ``cursor_start`` and ``cursor_end``.
517 - ``matched_text`` is removed in favor of ``cursor_start`` and ``cursor_end``.
518 - ``metadata`` is added for extended information.
518 - ``metadata`` is added for extended information.
519
519
520 .. _msging_history:
520 .. _msging_history:
521
521
522 History
522 History
523 -------
523 -------
524
524
525 For clients to explicitly request history from a kernel. The kernel has all
525 For clients to explicitly request history from a kernel. The kernel has all
526 the actual execution history stored in a single location, so clients can
526 the actual execution history stored in a single location, so clients can
527 request it from the kernel when needed.
527 request it from the kernel when needed.
528
528
529 Message type: ``history_request``::
529 Message type: ``history_request``::
530
530
531 content = {
531 content = {
532
532
533 # If True, also return output history in the resulting dict.
533 # If True, also return output history in the resulting dict.
534 'output' : bool,
534 'output' : bool,
535
535
536 # If True, return the raw input history, else the transformed input.
536 # If True, return the raw input history, else the transformed input.
537 'raw' : bool,
537 'raw' : bool,
538
538
539 # So far, this can be 'range', 'tail' or 'search'.
539 # So far, this can be 'range', 'tail' or 'search'.
540 'hist_access_type' : str,
540 'hist_access_type' : str,
541
541
542 # If hist_access_type is 'range', get a range of input cells. session can
542 # If hist_access_type is 'range', get a range of input cells. session can
543 # be a positive session number, or a negative number to count back from
543 # be a positive session number, or a negative number to count back from
544 # the current session.
544 # the current session.
545 'session' : int,
545 'session' : int,
546 # start and stop are line numbers within that session.
546 # start and stop are line numbers within that session.
547 'start' : int,
547 'start' : int,
548 'stop' : int,
548 'stop' : int,
549
549
550 # If hist_access_type is 'tail' or 'search', get the last n cells.
550 # If hist_access_type is 'tail' or 'search', get the last n cells.
551 'n' : int,
551 'n' : int,
552
552
553 # If hist_access_type is 'search', get cells matching the specified glob
553 # If hist_access_type is 'search', get cells matching the specified glob
554 # pattern (with * and ? as wildcards).
554 # pattern (with * and ? as wildcards).
555 'pattern' : str,
555 'pattern' : str,
556
556
557 # If hist_access_type is 'search' and unique is true, do not
557 # If hist_access_type is 'search' and unique is true, do not
558 # include duplicated history. Default is false.
558 # include duplicated history. Default is false.
559 'unique' : bool,
559 'unique' : bool,
560
560
561 }
561 }
562
562
563 .. versionadded:: 4.0
563 .. versionadded:: 4.0
564 The key ``unique`` for ``history_request``.
564 The key ``unique`` for ``history_request``.
565
565
566 Message type: ``history_reply``::
566 Message type: ``history_reply``::
567
567
568 content = {
568 content = {
569 # A list of 3 tuples, either:
569 # A list of 3 tuples, either:
570 # (session, line_number, input) or
570 # (session, line_number, input) or
571 # (session, line_number, (input, output)),
571 # (session, line_number, (input, output)),
572 # depending on whether output was False or True, respectively.
572 # depending on whether output was False or True, respectively.
573 'history' : list,
573 'history' : list,
574 }
574 }
575
575
576 .. _msging_is_complete:
576 .. _msging_is_complete:
577
577
578 Code completeness
578 Code completeness
579 -----------------
579 -----------------
580
580
581 .. versionadded:: 5.1
581 .. versionadded:: 5.1
582
582
583 When the user enters a line in a console style interface, the console must
583 When the user enters a line in a console style interface, the console must
584 decide whether to immediately execute the current code, or whether to show a
584 decide whether to immediately execute the current code, or whether to show a
585 continuation prompt for further input. For instance, in Python ``a = 5`` would
585 continuation prompt for further input. For instance, in Python ``a = 5`` would
586 be executed immediately, while ``for i in range(5):`` would expect further input.
586 be executed immediately, while ``for i in range(5):`` would expect further input.
587
587
588 There are four possible replies:
589
590 - *complete* code is ready to be executed
591 - *incomplete* code should prompt for another line
592 - *invalid* code will typically be sent for execution, so that the user sees the
593 error soonest.
594 - *unknown* - if the kernel is not able to determine this. The frontend should
595 also handle the kernel not replying promptly. It may default to sending the
596 code for execution, or it may implement simple fallback heuristics for whether
597 to execute the code (e.g. execute after a blank line).
598
588 Frontends may have ways to override this, forcing the code to be sent for
599 Frontends may have ways to override this, forcing the code to be sent for
589 execution or forcing a continuation prompt. If the kernel does not reply promptly,
600 execution or forcing a continuation prompt.
590 the frontend will probably default to sending the code to be executed.
591
601
592 Message type: ``is_complete_request``::
602 Message type: ``is_complete_request``::
593
603
594 content = {
604 content = {
595 # The code entered so far as a multiline string
605 # The code entered so far as a multiline string
596 'code' : str,
606 'code' : str,
597 }
607 }
598
608
599 Message type: ``complete_reply``::
609 Message type: ``is_complete_reply``::
600
610
601 content = {
611 content = {
602 # True if the code is ready to execute, False if not
612 # One of 'complete', 'incomplete', 'invalid', 'unknown'
603 'complete' : bool,
613 'status' : str,
614
615 # If status is 'incomplete', indent should contain the characters to use
616 # to indent the next line. This is only a hint: frontends may ignore it
617 # and use their own autoindentation rules. For other statuses, this
618 # field does not exist.
619 'indent': str,
604 }
620 }
605
621
606 Connect
622 Connect
607 -------
623 -------
608
624
609 When a client connects to the request/reply socket of the kernel, it can issue
625 When a client connects to the request/reply socket of the kernel, it can issue
610 a connect request to get basic information about the kernel, such as the ports
626 a connect request to get basic information about the kernel, such as the ports
611 the other ZeroMQ sockets are listening on. This allows clients to only have
627 the other ZeroMQ sockets are listening on. This allows clients to only have
612 to know about a single port (the shell channel) to connect to a kernel.
628 to know about a single port (the shell channel) to connect to a kernel.
613
629
614 Message type: ``connect_request``::
630 Message type: ``connect_request``::
615
631
616 content = {
632 content = {
617 }
633 }
618
634
619 Message type: ``connect_reply``::
635 Message type: ``connect_reply``::
620
636
621 content = {
637 content = {
622 'shell_port' : int, # The port the shell ROUTER socket is listening on.
638 'shell_port' : int, # The port the shell ROUTER socket is listening on.
623 'iopub_port' : int, # The port the PUB socket is listening on.
639 'iopub_port' : int, # The port the PUB socket is listening on.
624 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
640 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
625 'hb_port' : int, # The port the heartbeat socket is listening on.
641 'hb_port' : int, # The port the heartbeat socket is listening on.
626 }
642 }
627
643
628 .. _msging_kernel_info:
644 .. _msging_kernel_info:
629
645
630 Kernel info
646 Kernel info
631 -----------
647 -----------
632
648
633 If a client needs to know information about the kernel, it can
649 If a client needs to know information about the kernel, it can
634 make a request of the kernel's information.
650 make a request of the kernel's information.
635 This message can be used to fetch core information of the
651 This message can be used to fetch core information of the
636 kernel, including language (e.g., Python), language version number and
652 kernel, including language (e.g., Python), language version number and
637 IPython version number, and the IPython message spec version number.
653 IPython version number, and the IPython message spec version number.
638
654
639 Message type: ``kernel_info_request``::
655 Message type: ``kernel_info_request``::
640
656
641 content = {
657 content = {
642 }
658 }
643
659
644 Message type: ``kernel_info_reply``::
660 Message type: ``kernel_info_reply``::
645
661
646 content = {
662 content = {
647 # Version of messaging protocol.
663 # Version of messaging protocol.
648 # The first integer indicates major version. It is incremented when
664 # The first integer indicates major version. It is incremented when
649 # there is any backward incompatible change.
665 # there is any backward incompatible change.
650 # The second integer indicates minor version. It is incremented when
666 # The second integer indicates minor version. It is incremented when
651 # there is any backward compatible change.
667 # there is any backward compatible change.
652 'protocol_version': 'X.Y.Z',
668 'protocol_version': 'X.Y.Z',
653
669
654 # The kernel implementation name
670 # The kernel implementation name
655 # (e.g. 'ipython' for the IPython kernel)
671 # (e.g. 'ipython' for the IPython kernel)
656 'implementation': str,
672 'implementation': str,
657
673
658 # Implementation version number.
674 # Implementation version number.
659 # The version number of the kernel's implementation
675 # The version number of the kernel's implementation
660 # (e.g. IPython.__version__ for the IPython kernel)
676 # (e.g. IPython.__version__ for the IPython kernel)
661 'implementation_version': 'X.Y.Z',
677 'implementation_version': 'X.Y.Z',
662
678
663 # Programming language in which kernel is implemented.
679 # Programming language in which kernel is implemented.
664 # Kernel included in IPython returns 'python'.
680 # Kernel included in IPython returns 'python'.
665 'language': str,
681 'language': str,
666
682
667 # Language version number.
683 # Language version number.
668 # It is Python version number (e.g., '2.7.3') for the kernel
684 # It is Python version number (e.g., '2.7.3') for the kernel
669 # included in IPython.
685 # included in IPython.
670 'language_version': 'X.Y.Z',
686 'language_version': 'X.Y.Z',
671
687
672 # A banner of information about the kernel,
688 # A banner of information about the kernel,
673 # which may be desplayed in console environments.
689 # which may be desplayed in console environments.
674 'banner' : str,
690 'banner' : str,
675 }
691 }
676
692
677 .. versionchanged:: 5.0
693 .. versionchanged:: 5.0
678
694
679 Versions changed from lists of integers to strings.
695 Versions changed from lists of integers to strings.
680
696
681 .. versionchanged:: 5.0
697 .. versionchanged:: 5.0
682
698
683 ``ipython_version`` is removed.
699 ``ipython_version`` is removed.
684
700
685 .. versionchanged:: 5.0
701 .. versionchanged:: 5.0
686
702
687 ``implementation``, ``implementation_version``, and ``banner`` keys are added.
703 ``implementation``, ``implementation_version``, and ``banner`` keys are added.
688
704
689 .. _msging_shutdown:
705 .. _msging_shutdown:
690
706
691 Kernel shutdown
707 Kernel shutdown
692 ---------------
708 ---------------
693
709
694 The clients can request the kernel to shut itself down; this is used in
710 The clients can request the kernel to shut itself down; this is used in
695 multiple cases:
711 multiple cases:
696
712
697 - when the user chooses to close the client application via a menu or window
713 - when the user chooses to close the client application via a menu or window
698 control.
714 control.
699 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
715 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
700 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
716 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
701 IPythonQt client) to force a kernel restart to get a clean kernel without
717 IPythonQt client) to force a kernel restart to get a clean kernel without
702 losing client-side state like history or inlined figures.
718 losing client-side state like history or inlined figures.
703
719
704 The client sends a shutdown request to the kernel, and once it receives the
720 The client sends a shutdown request to the kernel, and once it receives the
705 reply message (which is otherwise empty), it can assume that the kernel has
721 reply message (which is otherwise empty), it can assume that the kernel has
706 completed shutdown safely.
722 completed shutdown safely.
707
723
708 Upon their own shutdown, client applications will typically execute a last
724 Upon their own shutdown, client applications will typically execute a last
709 minute sanity check and forcefully terminate any kernel that is still alive, to
725 minute sanity check and forcefully terminate any kernel that is still alive, to
710 avoid leaving stray processes in the user's machine.
726 avoid leaving stray processes in the user's machine.
711
727
712 Message type: ``shutdown_request``::
728 Message type: ``shutdown_request``::
713
729
714 content = {
730 content = {
715 'restart' : bool # whether the shutdown is final, or precedes a restart
731 'restart' : bool # whether the shutdown is final, or precedes a restart
716 }
732 }
717
733
718 Message type: ``shutdown_reply``::
734 Message type: ``shutdown_reply``::
719
735
720 content = {
736 content = {
721 'restart' : bool # whether the shutdown is final, or precedes a restart
737 'restart' : bool # whether the shutdown is final, or precedes a restart
722 }
738 }
723
739
724 .. Note::
740 .. Note::
725
741
726 When the clients detect a dead kernel thanks to inactivity on the heartbeat
742 When the clients detect a dead kernel thanks to inactivity on the heartbeat
727 socket, they simply send a forceful process termination signal, since a dead
743 socket, they simply send a forceful process termination signal, since a dead
728 process is unlikely to respond in any useful way to messages.
744 process is unlikely to respond in any useful way to messages.
729
745
730
746
731 Messages on the PUB/SUB socket
747 Messages on the PUB/SUB socket
732 ==============================
748 ==============================
733
749
734 Streams (stdout, stderr, etc)
750 Streams (stdout, stderr, etc)
735 ------------------------------
751 ------------------------------
736
752
737 Message type: ``stream``::
753 Message type: ``stream``::
738
754
739 content = {
755 content = {
740 # The name of the stream is one of 'stdout', 'stderr'
756 # The name of the stream is one of 'stdout', 'stderr'
741 'name' : str,
757 'name' : str,
742
758
743 # The data is an arbitrary string to be written to that stream
759 # The data is an arbitrary string to be written to that stream
744 'data' : str,
760 'data' : str,
745 }
761 }
746
762
747 Display Data
763 Display Data
748 ------------
764 ------------
749
765
750 This type of message is used to bring back data that should be displayed (text,
766 This type of message is used to bring back data that should be displayed (text,
751 html, svg, etc.) in the frontends. This data is published to all frontends.
767 html, svg, etc.) in the frontends. This data is published to all frontends.
752 Each message can have multiple representations of the data; it is up to the
768 Each message can have multiple representations of the data; it is up to the
753 frontend to decide which to use and how. A single message should contain all
769 frontend to decide which to use and how. A single message should contain all
754 possible representations of the same information. Each representation should
770 possible representations of the same information. Each representation should
755 be a JSON'able data structure, and should be a valid MIME type.
771 be a JSON'able data structure, and should be a valid MIME type.
756
772
757 Some questions remain about this design:
773 Some questions remain about this design:
758
774
759 * Do we use this message type for execute_result/displayhook? Probably not, because
775 * Do we use this message type for execute_result/displayhook? Probably not, because
760 the displayhook also has to handle the Out prompt display. On the other hand
776 the displayhook also has to handle the Out prompt display. On the other hand
761 we could put that information into the metadata section.
777 we could put that information into the metadata section.
762
778
763 .. _display_data:
779 .. _display_data:
764
780
765 Message type: ``display_data``::
781 Message type: ``display_data``::
766
782
767 content = {
783 content = {
768
784
769 # Who create the data
785 # Who create the data
770 'source' : str,
786 'source' : str,
771
787
772 # The data dict contains key/value pairs, where the keys are MIME
788 # The data dict contains key/value pairs, where the keys are MIME
773 # types and the values are the raw data of the representation in that
789 # types and the values are the raw data of the representation in that
774 # format.
790 # format.
775 'data' : dict,
791 'data' : dict,
776
792
777 # Any metadata that describes the data
793 # Any metadata that describes the data
778 'metadata' : dict
794 'metadata' : dict
779 }
795 }
780
796
781
797
782 The ``metadata`` contains any metadata that describes the output.
798 The ``metadata`` contains any metadata that describes the output.
783 Global keys are assumed to apply to the output as a whole.
799 Global keys are assumed to apply to the output as a whole.
784 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
800 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
785 which are interpreted as applying only to output of that type.
801 which are interpreted as applying only to output of that type.
786 Third parties should put any data they write into a single dict
802 Third parties should put any data they write into a single dict
787 with a reasonably unique name to avoid conflicts.
803 with a reasonably unique name to avoid conflicts.
788
804
789 The only metadata keys currently defined in IPython are the width and height
805 The only metadata keys currently defined in IPython are the width and height
790 of images::
806 of images::
791
807
792 metadata = {
808 metadata = {
793 'image/png' : {
809 'image/png' : {
794 'width': 640,
810 'width': 640,
795 'height': 480
811 'height': 480
796 }
812 }
797 }
813 }
798
814
799
815
800 .. versionchanged:: 5.0
816 .. versionchanged:: 5.0
801
817
802 `application/json` data should be unpacked JSON data,
818 `application/json` data should be unpacked JSON data,
803 not double-serialized as a JSON string.
819 not double-serialized as a JSON string.
804
820
805
821
806 Raw Data Publication
822 Raw Data Publication
807 --------------------
823 --------------------
808
824
809 ``display_data`` lets you publish *representations* of data, such as images and html.
825 ``display_data`` lets you publish *representations* of data, such as images and html.
810 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
826 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
811
827
812 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
828 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
813
829
814 .. sourcecode:: python
830 .. sourcecode:: python
815
831
816 from IPython.kernel.zmq.datapub import publish_data
832 from IPython.kernel.zmq.datapub import publish_data
817 ns = dict(x=my_array)
833 ns = dict(x=my_array)
818 publish_data(ns)
834 publish_data(ns)
819
835
820
836
821 Message type: ``data_pub``::
837 Message type: ``data_pub``::
822
838
823 content = {
839 content = {
824 # the keys of the data dict, after it has been unserialized
840 # the keys of the data dict, after it has been unserialized
825 'keys' : ['a', 'b']
841 'keys' : ['a', 'b']
826 }
842 }
827 # the namespace dict will be serialized in the message buffers,
843 # the namespace dict will be serialized in the message buffers,
828 # which will have a length of at least one
844 # which will have a length of at least one
829 buffers = [b'pdict', ...]
845 buffers = [b'pdict', ...]
830
846
831
847
832 The interpretation of a sequence of data_pub messages for a given parent request should be
848 The interpretation of a sequence of data_pub messages for a given parent request should be
833 to update a single namespace with subsequent results.
849 to update a single namespace with subsequent results.
834
850
835 .. note::
851 .. note::
836
852
837 No frontends directly handle data_pub messages at this time.
853 No frontends directly handle data_pub messages at this time.
838 It is currently only used by the client/engines in :mod:`IPython.parallel`,
854 It is currently only used by the client/engines in :mod:`IPython.parallel`,
839 where engines may publish *data* to the Client,
855 where engines may publish *data* to the Client,
840 of which the Client can then publish *representations* via ``display_data``
856 of which the Client can then publish *representations* via ``display_data``
841 to various frontends.
857 to various frontends.
842
858
843 Code inputs
859 Code inputs
844 -----------
860 -----------
845
861
846 To let all frontends know what code is being executed at any given time, these
862 To let all frontends know what code is being executed at any given time, these
847 messages contain a re-broadcast of the ``code`` portion of an
863 messages contain a re-broadcast of the ``code`` portion of an
848 :ref:`execute_request <execute>`, along with the :ref:`execution_count
864 :ref:`execute_request <execute>`, along with the :ref:`execution_count
849 <execution_counter>`.
865 <execution_counter>`.
850
866
851 Message type: ``execute_input``::
867 Message type: ``execute_input``::
852
868
853 content = {
869 content = {
854 'code' : str, # Source code to be executed, one or more lines
870 'code' : str, # Source code to be executed, one or more lines
855
871
856 # The counter for this execution is also provided so that clients can
872 # The counter for this execution is also provided so that clients can
857 # display it, since IPython automatically creates variables called _iN
873 # display it, since IPython automatically creates variables called _iN
858 # (for input prompt In[N]).
874 # (for input prompt In[N]).
859 'execution_count' : int
875 'execution_count' : int
860 }
876 }
861
877
862 .. versionchanged:: 5.0
878 .. versionchanged:: 5.0
863
879
864 ``pyin`` is renamed to ``execute_input``.
880 ``pyin`` is renamed to ``execute_input``.
865
881
866
882
867 Execution results
883 Execution results
868 -----------------
884 -----------------
869
885
870 Results of an execution are published as an ``execute_result``.
886 Results of an execution are published as an ``execute_result``.
871 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
887 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
872
888
873 Results can have multiple simultaneous formats depending on its
889 Results can have multiple simultaneous formats depending on its
874 configuration. A plain text representation should always be provided
890 configuration. A plain text representation should always be provided
875 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
891 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
876 according to its capabilities.
892 according to its capabilities.
877 Frontends should ignore mime-types they do not understand. The data itself is
893 Frontends should ignore mime-types they do not understand. The data itself is
878 any JSON object and depends on the format. It is often, but not always a string.
894 any JSON object and depends on the format. It is often, but not always a string.
879
895
880 Message type: ``execute_result``::
896 Message type: ``execute_result``::
881
897
882 content = {
898 content = {
883
899
884 # The counter for this execution is also provided so that clients can
900 # The counter for this execution is also provided so that clients can
885 # display it, since IPython automatically creates variables called _N
901 # display it, since IPython automatically creates variables called _N
886 # (for prompt N).
902 # (for prompt N).
887 'execution_count' : int,
903 'execution_count' : int,
888
904
889 # data and metadata are identical to a display_data message.
905 # data and metadata are identical to a display_data message.
890 # the object being displayed is that passed to the display hook,
906 # the object being displayed is that passed to the display hook,
891 # i.e. the *result* of the execution.
907 # i.e. the *result* of the execution.
892 'data' : dict,
908 'data' : dict,
893 'metadata' : dict,
909 'metadata' : dict,
894 }
910 }
895
911
896 Execution errors
912 Execution errors
897 ----------------
913 ----------------
898
914
899 When an error occurs during code execution
915 When an error occurs during code execution
900
916
901 Message type: ``error``::
917 Message type: ``error``::
902
918
903 content = {
919 content = {
904 # Similar content to the execute_reply messages for the 'error' case,
920 # Similar content to the execute_reply messages for the 'error' case,
905 # except the 'status' field is omitted.
921 # except the 'status' field is omitted.
906 }
922 }
907
923
908 .. versionchanged:: 5.0
924 .. versionchanged:: 5.0
909
925
910 ``pyerr`` renamed to ``error``
926 ``pyerr`` renamed to ``error``
911
927
912 Kernel status
928 Kernel status
913 -------------
929 -------------
914
930
915 This message type is used by frontends to monitor the status of the kernel.
931 This message type is used by frontends to monitor the status of the kernel.
916
932
917 Message type: ``status``::
933 Message type: ``status``::
918
934
919 content = {
935 content = {
920 # When the kernel starts to handle a message, it will enter the 'busy'
936 # When the kernel starts to handle a message, it will enter the 'busy'
921 # state and when it finishes, it will enter the 'idle' state.
937 # state and when it finishes, it will enter the 'idle' state.
922 # The kernel will publish state 'starting' exactly once at process startup.
938 # The kernel will publish state 'starting' exactly once at process startup.
923 execution_state : ('busy', 'idle', 'starting')
939 execution_state : ('busy', 'idle', 'starting')
924 }
940 }
925
941
926 .. versionchanged:: 5.0
942 .. versionchanged:: 5.0
927
943
928 Busy and idle messages should be sent before/after handling every message,
944 Busy and idle messages should be sent before/after handling every message,
929 not just execution.
945 not just execution.
930
946
931 Clear output
947 Clear output
932 ------------
948 ------------
933
949
934 This message type is used to clear the output that is visible on the frontend.
950 This message type is used to clear the output that is visible on the frontend.
935
951
936 Message type: ``clear_output``::
952 Message type: ``clear_output``::
937
953
938 content = {
954 content = {
939
955
940 # Wait to clear the output until new output is available. Clears the
956 # Wait to clear the output until new output is available. Clears the
941 # existing output immediately before the new output is displayed.
957 # existing output immediately before the new output is displayed.
942 # Useful for creating simple animations with minimal flickering.
958 # Useful for creating simple animations with minimal flickering.
943 'wait' : bool,
959 'wait' : bool,
944 }
960 }
945
961
946 .. versionchanged:: 4.1
962 .. versionchanged:: 4.1
947
963
948 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
964 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
949 and ``wait`` is added.
965 and ``wait`` is added.
950 The selective clearing keys are ignored in v4 and the default behavior remains the same,
966 The selective clearing keys are ignored in v4 and the default behavior remains the same,
951 so v4 clear_output messages will be safely handled by a v4.1 frontend.
967 so v4 clear_output messages will be safely handled by a v4.1 frontend.
952
968
953
969
954 Messages on the stdin ROUTER/DEALER sockets
970 Messages on the stdin ROUTER/DEALER sockets
955 ===========================================
971 ===========================================
956
972
957 This is a socket where the request/reply pattern goes in the opposite direction:
973 This is a socket where the request/reply pattern goes in the opposite direction:
958 from the kernel to a *single* frontend, and its purpose is to allow
974 from the kernel to a *single* frontend, and its purpose is to allow
959 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
975 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
960 to be fulfilled by the client. The request should be made to the frontend that
976 to be fulfilled by the client. The request should be made to the frontend that
961 made the execution request that prompted ``raw_input`` to be called. For now we
977 made the execution request that prompted ``raw_input`` to be called. For now we
962 will keep these messages as simple as possible, since they only mean to convey
978 will keep these messages as simple as possible, since they only mean to convey
963 the ``raw_input(prompt)`` call.
979 the ``raw_input(prompt)`` call.
964
980
965 Message type: ``input_request``::
981 Message type: ``input_request``::
966
982
967 content = {
983 content = {
968 # the text to show at the prompt
984 # the text to show at the prompt
969 'prompt' : str,
985 'prompt' : str,
970 # Is the request for a password?
986 # Is the request for a password?
971 # If so, the frontend shouldn't echo input.
987 # If so, the frontend shouldn't echo input.
972 'password' : bool
988 'password' : bool
973 }
989 }
974
990
975 Message type: ``input_reply``::
991 Message type: ``input_reply``::
976
992
977 content = { 'value' : str }
993 content = { 'value' : str }
978
994
979
995
980 When ``password`` is True, the frontend should not echo the input as it is entered.
996 When ``password`` is True, the frontend should not echo the input as it is entered.
981
997
982 .. versionchanged:: 5.0
998 .. versionchanged:: 5.0
983
999
984 ``password`` key added.
1000 ``password`` key added.
985
1001
986 .. note::
1002 .. note::
987
1003
988 The stdin socket of the client is required to have the same zmq IDENTITY
1004 The stdin socket of the client is required to have the same zmq IDENTITY
989 as the client's shell socket.
1005 as the client's shell socket.
990 Because of this, the ``input_request`` must be sent with the same IDENTITY
1006 Because of this, the ``input_request`` must be sent with the same IDENTITY
991 routing prefix as the ``execute_reply`` in order for the frontend to receive
1007 routing prefix as the ``execute_reply`` in order for the frontend to receive
992 the message.
1008 the message.
993
1009
994 .. note::
1010 .. note::
995
1011
996 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
1012 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
997 practice the kernel should behave like an interactive program. When a
1013 practice the kernel should behave like an interactive program. When a
998 program is opened on the console, the keyboard effectively takes over the
1014 program is opened on the console, the keyboard effectively takes over the
999 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1015 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1000 Since the IPython kernel effectively behaves like a console program (albeit
1016 Since the IPython kernel effectively behaves like a console program (albeit
1001 one whose "keyboard" is actually living in a separate process and
1017 one whose "keyboard" is actually living in a separate process and
1002 transported over the zmq connection), raw ``stdin`` isn't expected to be
1018 transported over the zmq connection), raw ``stdin`` isn't expected to be
1003 available.
1019 available.
1004
1020
1005 .. _kernel_heartbeat:
1021 .. _kernel_heartbeat:
1006
1022
1007 Heartbeat for kernels
1023 Heartbeat for kernels
1008 =====================
1024 =====================
1009
1025
1010 Clients send ping messages on a REQ socket, which are echoed right back
1026 Clients send ping messages on a REQ socket, which are echoed right back
1011 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
1027 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
1012
1028
1013
1029
1014 Custom Messages
1030 Custom Messages
1015 ===============
1031 ===============
1016
1032
1017 .. versionadded:: 4.1
1033 .. versionadded:: 4.1
1018
1034
1019 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
1035 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
1020 and Kernel-side components, and allow them to communicate with each other.
1036 and Kernel-side components, and allow them to communicate with each other.
1021 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
1037 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
1022 and can communicate in either direction.
1038 and can communicate in either direction.
1023
1039
1024 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1040 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1025 and no messages expect a reply.
1041 and no messages expect a reply.
1026 The Kernel listens for these messages on the Shell channel,
1042 The Kernel listens for these messages on the Shell channel,
1027 and the Frontend listens for them on the IOPub channel.
1043 and the Frontend listens for them on the IOPub channel.
1028
1044
1029 Opening a Comm
1045 Opening a Comm
1030 --------------
1046 --------------
1031
1047
1032 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1048 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1033
1049
1034 {
1050 {
1035 'comm_id' : 'u-u-i-d',
1051 'comm_id' : 'u-u-i-d',
1036 'target_name' : 'my_comm',
1052 'target_name' : 'my_comm',
1037 'data' : {}
1053 'data' : {}
1038 }
1054 }
1039
1055
1040 Every Comm has an ID and a target name.
1056 Every Comm has an ID and a target name.
1041 The code handling the message on the receiving side is responsible for maintaining a mapping
1057 The code handling the message on the receiving side is responsible for maintaining a mapping
1042 of target_name keys to constructors.
1058 of target_name keys to constructors.
1043 After a ``comm_open`` message has been sent,
1059 After a ``comm_open`` message has been sent,
1044 there should be a corresponding Comm instance on both sides.
1060 there should be a corresponding Comm instance on both sides.
1045 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1061 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1046
1062
1047 If the ``target_name`` key is not found on the receiving side,
1063 If the ``target_name`` key is not found on the receiving side,
1048 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1064 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1049
1065
1050 Comm Messages
1066 Comm Messages
1051 -------------
1067 -------------
1052
1068
1053 Comm messages are one-way communications to update comm state,
1069 Comm messages are one-way communications to update comm state,
1054 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1070 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1055
1071
1056 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1072 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1057
1073
1058 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1074 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1059
1075
1060 Message type: ``comm_msg``::
1076 Message type: ``comm_msg``::
1061
1077
1062 {
1078 {
1063 'comm_id' : 'u-u-i-d',
1079 'comm_id' : 'u-u-i-d',
1064 'data' : {}
1080 'data' : {}
1065 }
1081 }
1066
1082
1067 Tearing Down Comms
1083 Tearing Down Comms
1068 ------------------
1084 ------------------
1069
1085
1070 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1086 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1071 This is done with a ``comm_close`` message.
1087 This is done with a ``comm_close`` message.
1072
1088
1073 Message type: ``comm_close``::
1089 Message type: ``comm_close``::
1074
1090
1075 {
1091 {
1076 'comm_id' : 'u-u-i-d',
1092 'comm_id' : 'u-u-i-d',
1077 'data' : {}
1093 'data' : {}
1078 }
1094 }
1079
1095
1080 Output Side Effects
1096 Output Side Effects
1081 -------------------
1097 -------------------
1082
1098
1083 Since comm messages can execute arbitrary user code,
1099 Since comm messages can execute arbitrary user code,
1084 handlers should set the parent header and publish status busy / idle,
1100 handlers should set the parent header and publish status busy / idle,
1085 just like an execute request.
1101 just like an execute request.
1086
1102
1087
1103
1088 To Do
1104 To Do
1089 =====
1105 =====
1090
1106
1091 Missing things include:
1107 Missing things include:
1092
1108
1093 * Important: finish thinking through the payload concept and API.
1109 * Important: finish thinking through the payload concept and API.
1094
1110
1095 .. include:: ../links.txt
1111 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now