##// END OF EJS Templates
Merge pull request #6297 from takluyver/is-complete-request...
Min RK -
r18369:d236bec1 merge
parent child Browse files
Show More
@@ -1,638 +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
16
17 # Copyright (c) IPython Development Team.
17 # Copyright (c) IPython Development Team.
18 # Distributed under the terms of the Modified BSD License.
18 # Distributed under the terms of the Modified BSD License.
19 import ast
19 import ast
20 import codeop
20 import codeop
21 import re
21 import re
22 import sys
22 import sys
23
23
24 from IPython.utils.py3compat import cast_unicode
24 from IPython.utils.py3compat import cast_unicode
25 from IPython.core.inputtransformer import (leading_indent,
25 from IPython.core.inputtransformer import (leading_indent,
26 classic_prompt,
26 classic_prompt,
27 ipy_prompt,
27 ipy_prompt,
28 strip_encoding_cookie,
28 strip_encoding_cookie,
29 cellmagic,
29 cellmagic,
30 assemble_logical_lines,
30 assemble_logical_lines,
31 help_end,
31 help_end,
32 escaped_commands,
32 escaped_commands,
33 assign_from_magic,
33 assign_from_magic,
34 assign_from_system,
34 assign_from_system,
35 assemble_python_lines,
35 assemble_python_lines,
36 )
36 )
37
37
38 # These are available in this module for backwards compatibility.
38 # These are available in this module for backwards compatibility.
39 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
39 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
40 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
40 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
41 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
41 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Utilities
44 # Utilities
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 # FIXME: These are general-purpose utilities that later can be moved to the
47 # FIXME: These are general-purpose utilities that later can be moved to the
48 # general ward. Kept here for now because we're being very strict about test
48 # general ward. Kept here for now because we're being very strict about test
49 # coverage with this code, and this lets us ensure that we keep 100% coverage
49 # coverage with this code, and this lets us ensure that we keep 100% coverage
50 # while developing.
50 # while developing.
51
51
52 # compiled regexps for autoindent management
52 # compiled regexps for autoindent management
53 dedent_re = re.compile('|'.join([
53 dedent_re = re.compile('|'.join([
54 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
54 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
55 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
55 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
56 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
56 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
57 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
57 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
58 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
58 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
59 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
59 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
60 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
60 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
61 ]))
61 ]))
62 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
62 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
63
63
64 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
64 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
65 # before pure comments
65 # before pure comments
66 comment_line_re = re.compile('^\s*\#')
66 comment_line_re = re.compile('^\s*\#')
67
67
68
68
69 def num_ini_spaces(s):
69 def num_ini_spaces(s):
70 """Return the number of initial spaces in a string.
70 """Return the number of initial spaces in a string.
71
71
72 Note that tabs are counted as a single space. For now, we do *not* support
72 Note that tabs are counted as a single space. For now, we do *not* support
73 mixing of tabs and spaces in the user's input.
73 mixing of tabs and spaces in the user's input.
74
74
75 Parameters
75 Parameters
76 ----------
76 ----------
77 s : string
77 s : string
78
78
79 Returns
79 Returns
80 -------
80 -------
81 n : int
81 n : int
82 """
82 """
83
83
84 ini_spaces = ini_spaces_re.match(s)
84 ini_spaces = ini_spaces_re.match(s)
85 if ini_spaces:
85 if ini_spaces:
86 return ini_spaces.end()
86 return ini_spaces.end()
87 else:
87 else:
88 return 0
88 return 0
89
89
90 def last_blank(src):
90 def last_blank(src):
91 """Determine if the input source ends in a blank.
91 """Determine if the input source ends in a blank.
92
92
93 A blank is either a newline or a line consisting of whitespace.
93 A blank is either a newline or a line consisting of whitespace.
94
94
95 Parameters
95 Parameters
96 ----------
96 ----------
97 src : string
97 src : string
98 A single or multiline string.
98 A single or multiline string.
99 """
99 """
100 if not src: return False
100 if not src: return False
101 ll = src.splitlines()[-1]
101 ll = src.splitlines()[-1]
102 return (ll == '') or ll.isspace()
102 return (ll == '') or ll.isspace()
103
103
104
104
105 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
105 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
106 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
106 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
107
107
108 def last_two_blanks(src):
108 def last_two_blanks(src):
109 """Determine if the input source ends in two blanks.
109 """Determine if the input source ends in two blanks.
110
110
111 A blank is either a newline or a line consisting of whitespace.
111 A blank is either a newline or a line consisting of whitespace.
112
112
113 Parameters
113 Parameters
114 ----------
114 ----------
115 src : string
115 src : string
116 A single or multiline string.
116 A single or multiline string.
117 """
117 """
118 if not src: return False
118 if not src: return False
119 # The logic here is tricky: I couldn't get a regexp to work and pass all
119 # The logic here is tricky: I couldn't get a regexp to work and pass all
120 # the tests, so I took a different approach: split the source by lines,
120 # the tests, so I took a different approach: split the source by lines,
121 # grab the last two and prepend '###\n' as a stand-in for whatever was in
121 # grab the last two and prepend '###\n' as a stand-in for whatever was in
122 # the body before the last two lines. Then, with that structure, it's
122 # the body before the last two lines. Then, with that structure, it's
123 # possible to analyze with two regexps. Not the most elegant solution, but
123 # possible to analyze with two regexps. Not the most elegant solution, but
124 # it works. If anyone tries to change this logic, make sure to validate
124 # it works. If anyone tries to change this logic, make sure to validate
125 # the whole test suite first!
125 # the whole test suite first!
126 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
126 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
127 return (bool(last_two_blanks_re.match(new_src)) or
127 return (bool(last_two_blanks_re.match(new_src)) or
128 bool(last_two_blanks_re2.match(new_src)) )
128 bool(last_two_blanks_re2.match(new_src)) )
129
129
130
130
131 def remove_comments(src):
131 def remove_comments(src):
132 """Remove all comments from input source.
132 """Remove all comments from input source.
133
133
134 Note: comments are NOT recognized inside of strings!
134 Note: comments are NOT recognized inside of strings!
135
135
136 Parameters
136 Parameters
137 ----------
137 ----------
138 src : string
138 src : string
139 A single or multiline input string.
139 A single or multiline input string.
140
140
141 Returns
141 Returns
142 -------
142 -------
143 String with all Python comments removed.
143 String with all Python comments removed.
144 """
144 """
145
145
146 return re.sub('#.*', '', src)
146 return re.sub('#.*', '', src)
147
147
148
148
149 def get_input_encoding():
149 def get_input_encoding():
150 """Return the default standard input encoding.
150 """Return the default standard input encoding.
151
151
152 If sys.stdin has no encoding, 'ascii' is returned."""
152 If sys.stdin has no encoding, 'ascii' is returned."""
153 # There are strange environments for which sys.stdin.encoding is None. We
153 # There are strange environments for which sys.stdin.encoding is None. We
154 # ensure that a valid encoding is returned.
154 # ensure that a valid encoding is returned.
155 encoding = getattr(sys.stdin, 'encoding', None)
155 encoding = getattr(sys.stdin, 'encoding', None)
156 if encoding is None:
156 if encoding is None:
157 encoding = 'ascii'
157 encoding = 'ascii'
158 return encoding
158 return encoding
159
159
160 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
161 # Classes and functions for normal Python syntax handling
161 # Classes and functions for normal Python syntax handling
162 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
163
163
164 class InputSplitter(object):
164 class InputSplitter(object):
165 r"""An object that can accumulate lines of Python source before execution.
165 r"""An object that can accumulate lines of Python source before execution.
166
166
167 This object is designed to be fed python source line-by-line, using
167 This object is designed to be fed python source line-by-line, using
168 :meth:`push`. It will return on each push whether the currently pushed
168 :meth:`push`. It will return on each push whether the currently pushed
169 code could be executed already. In addition, it provides a method called
169 code could be executed already. In addition, it provides a method called
170 :meth:`push_accepts_more` that can be used to query whether more input
170 :meth:`push_accepts_more` that can be used to query whether more input
171 can be pushed into a single interactive block.
171 can be pushed into a single interactive block.
172
172
173 This is a simple example of how an interactive terminal-based client can use
173 This is a simple example of how an interactive terminal-based client can use
174 this tool::
174 this tool::
175
175
176 isp = InputSplitter()
176 isp = InputSplitter()
177 while isp.push_accepts_more():
177 while isp.push_accepts_more():
178 indent = ' '*isp.indent_spaces
178 indent = ' '*isp.indent_spaces
179 prompt = '>>> ' + indent
179 prompt = '>>> ' + indent
180 line = indent + raw_input(prompt)
180 line = indent + raw_input(prompt)
181 isp.push(line)
181 isp.push(line)
182 print 'Input source was:\n', isp.source_reset(),
182 print 'Input source was:\n', isp.source_reset(),
183 """
183 """
184 # Number of spaces of indentation computed from input that has been pushed
184 # Number of spaces of indentation computed from input that has been pushed
185 # so far. This is the attributes callers should query to get the current
185 # so far. This is the attributes callers should query to get the current
186 # indentation level, in order to provide auto-indent facilities.
186 # indentation level, in order to provide auto-indent facilities.
187 indent_spaces = 0
187 indent_spaces = 0
188 # String, indicating the default input encoding. It is computed by default
188 # String, indicating the default input encoding. It is computed by default
189 # at initialization time via get_input_encoding(), but it can be reset by a
189 # at initialization time via get_input_encoding(), but it can be reset by a
190 # client with specific knowledge of the encoding.
190 # client with specific knowledge of the encoding.
191 encoding = ''
191 encoding = ''
192 # String where the current full source input is stored, properly encoded.
192 # String where the current full source input is stored, properly encoded.
193 # Reading this attribute is the normal way of querying the currently pushed
193 # Reading this attribute is the normal way of querying the currently pushed
194 # source code, that has been properly encoded.
194 # source code, that has been properly encoded.
195 source = ''
195 source = ''
196 # Code object corresponding to the current source. It is automatically
196 # Code object corresponding to the current source. It is automatically
197 # synced to the source, so it can be queried at any time to obtain the code
197 # synced to the source, so it can be queried at any time to obtain the code
198 # object; it will be None if the source doesn't compile to valid Python.
198 # object; it will be None if the source doesn't compile to valid Python.
199 code = None
199 code = None
200
200
201 # Private attributes
201 # Private attributes
202
202
203 # List with lines of input accumulated so far
203 # List with lines of input accumulated so far
204 _buffer = None
204 _buffer = None
205 # Command compiler
205 # Command compiler
206 _compile = None
206 _compile = None
207 # Mark when input has changed indentation all the way back to flush-left
207 # Mark when input has changed indentation all the way back to flush-left
208 _full_dedent = False
208 _full_dedent = False
209 # Boolean indicating whether the current block is complete
209 # Boolean indicating whether the current block is complete
210 _is_complete = None
210 _is_complete = None
211 # Boolean indicating whether the current block has an unrecoverable syntax error
212 _is_invalid = False
211
213
212 def __init__(self):
214 def __init__(self):
213 """Create a new InputSplitter instance.
215 """Create a new InputSplitter instance.
214 """
216 """
215 self._buffer = []
217 self._buffer = []
216 self._compile = codeop.CommandCompiler()
218 self._compile = codeop.CommandCompiler()
217 self.encoding = get_input_encoding()
219 self.encoding = get_input_encoding()
218
220
219 def reset(self):
221 def reset(self):
220 """Reset the input buffer and associated state."""
222 """Reset the input buffer and associated state."""
221 self.indent_spaces = 0
223 self.indent_spaces = 0
222 self._buffer[:] = []
224 self._buffer[:] = []
223 self.source = ''
225 self.source = ''
224 self.code = None
226 self.code = None
225 self._is_complete = False
227 self._is_complete = False
228 self._is_invalid = False
226 self._full_dedent = False
229 self._full_dedent = False
227
230
228 def source_reset(self):
231 def source_reset(self):
229 """Return the input source and perform a full reset.
232 """Return the input source and perform a full reset.
230 """
233 """
231 out = self.source
234 out = self.source
232 self.reset()
235 self.reset()
233 return out
236 return out
234
237
238 def check_complete(self, source):
239 """Return whether a block of code is ready to execute, or should be continued
240
241 This is a non-stateful API, and will reset the state of this InputSplitter.
242
243 Parameters
244 ----------
245 source : string
246 Python input code, which can be multiline.
247
248 Returns
249 -------
250 status : str
251 One of 'complete', 'incomplete', or 'invalid' if source is not a
252 prefix of valid code.
253 indent_spaces : int or None
254 The number of spaces by which to indent the next line of code. If
255 status is not 'incomplete', this is None.
256 """
257 self.reset()
258 try:
259 self.push(source)
260 except SyntaxError:
261 # Transformers in IPythonInputSplitter can raise SyntaxError,
262 # which push() will not catch.
263 return 'invalid', None
264 else:
265 if self._is_invalid:
266 return 'invalid', None
267 elif self.push_accepts_more():
268 return 'incomplete', self.indent_spaces
269 else:
270 return 'complete', None
271 finally:
272 self.reset()
273
235 def push(self, lines):
274 def push(self, lines):
236 """Push one or more lines of input.
275 """Push one or more lines of input.
237
276
238 This stores the given lines and returns a status code indicating
277 This stores the given lines and returns a status code indicating
239 whether the code forms a complete Python block or not.
278 whether the code forms a complete Python block or not.
240
279
241 Any exceptions generated in compilation are swallowed, but if an
280 Any exceptions generated in compilation are swallowed, but if an
242 exception was produced, the method returns True.
281 exception was produced, the method returns True.
243
282
244 Parameters
283 Parameters
245 ----------
284 ----------
246 lines : string
285 lines : string
247 One or more lines of Python input.
286 One or more lines of Python input.
248
287
249 Returns
288 Returns
250 -------
289 -------
251 is_complete : boolean
290 is_complete : boolean
252 True if the current input source (the result of the current input
291 True if the current input source (the result of the current input
253 plus prior inputs) forms a complete Python execution block. Note that
292 plus prior inputs) forms a complete Python execution block. Note that
254 this value is also stored as a private attribute (``_is_complete``), so it
293 this value is also stored as a private attribute (``_is_complete``), so it
255 can be queried at any time.
294 can be queried at any time.
256 """
295 """
257 self._store(lines)
296 self._store(lines)
258 source = self.source
297 source = self.source
259
298
260 # Before calling _compile(), reset the code object to None so that if an
299 # Before calling _compile(), reset the code object to None so that if an
261 # exception is raised in compilation, we don't mislead by having
300 # exception is raised in compilation, we don't mislead by having
262 # inconsistent code/source attributes.
301 # inconsistent code/source attributes.
263 self.code, self._is_complete = None, None
302 self.code, self._is_complete = None, None
303 self._is_invalid = False
264
304
265 # Honor termination lines properly
305 # Honor termination lines properly
266 if source.endswith('\\\n'):
306 if source.endswith('\\\n'):
267 return False
307 return False
268
308
269 self._update_indent(lines)
309 self._update_indent(lines)
270 try:
310 try:
271 self.code = self._compile(source, symbol="exec")
311 self.code = self._compile(source, symbol="exec")
272 # Invalid syntax can produce any of a number of different errors from
312 # Invalid syntax can produce any of a number of different errors from
273 # inside the compiler, so we have to catch them all. Syntax errors
313 # inside the compiler, so we have to catch them all. Syntax errors
274 # immediately produce a 'ready' block, so the invalid Python can be
314 # immediately produce a 'ready' block, so the invalid Python can be
275 # sent to the kernel for evaluation with possible ipython
315 # sent to the kernel for evaluation with possible ipython
276 # special-syntax conversion.
316 # special-syntax conversion.
277 except (SyntaxError, OverflowError, ValueError, TypeError,
317 except (SyntaxError, OverflowError, ValueError, TypeError,
278 MemoryError):
318 MemoryError):
279 self._is_complete = True
319 self._is_complete = True
320 self._is_invalid = True
280 else:
321 else:
281 # Compilation didn't produce any exceptions (though it may not have
322 # Compilation didn't produce any exceptions (though it may not have
282 # given a complete code object)
323 # given a complete code object)
283 self._is_complete = self.code is not None
324 self._is_complete = self.code is not None
284
325
285 return self._is_complete
326 return self._is_complete
286
327
287 def push_accepts_more(self):
328 def push_accepts_more(self):
288 """Return whether a block of interactive input can accept more input.
329 """Return whether a block of interactive input can accept more input.
289
330
290 This method is meant to be used by line-oriented frontends, who need to
331 This method is meant to be used by line-oriented frontends, who need to
291 guess whether a block is complete or not based solely on prior and
332 guess whether a block is complete or not based solely on prior and
292 current input lines. The InputSplitter considers it has a complete
333 current input lines. The InputSplitter considers it has a complete
293 interactive block and will not accept more input when either:
334 interactive block and will not accept more input when either:
294
335
295 * A SyntaxError is raised
336 * A SyntaxError is raised
296
337
297 * The code is complete and consists of a single line or a single
338 * The code is complete and consists of a single line or a single
298 non-compound statement
339 non-compound statement
299
340
300 * The code is complete and has a blank line at the end
341 * The code is complete and has a blank line at the end
301
342
302 If the current input produces a syntax error, this method immediately
343 If the current input produces a syntax error, this method immediately
303 returns False but does *not* raise the syntax error exception, as
344 returns False but does *not* raise the syntax error exception, as
304 typically clients will want to send invalid syntax to an execution
345 typically clients will want to send invalid syntax to an execution
305 backend which might convert the invalid syntax into valid Python via
346 backend which might convert the invalid syntax into valid Python via
306 one of the dynamic IPython mechanisms.
347 one of the dynamic IPython mechanisms.
307 """
348 """
308
349
309 # With incomplete input, unconditionally accept more
350 # With incomplete input, unconditionally accept more
310 # A syntax error also sets _is_complete to True - see push()
351 # A syntax error also sets _is_complete to True - see push()
311 if not self._is_complete:
352 if not self._is_complete:
312 #print("Not complete") # debug
353 #print("Not complete") # debug
313 return True
354 return True
314
355
315 # The user can make any (complete) input execute by leaving a blank line
356 # The user can make any (complete) input execute by leaving a blank line
316 last_line = self.source.splitlines()[-1]
357 last_line = self.source.splitlines()[-1]
317 if (not last_line) or last_line.isspace():
358 if (not last_line) or last_line.isspace():
318 #print("Blank line") # debug
359 #print("Blank line") # debug
319 return False
360 return False
320
361
321 # If there's just a single line or AST node, and we're flush left, as is
362 # If there's just a single line or AST node, and we're flush left, as is
322 # the case after a simple statement such as 'a=1', we want to execute it
363 # the case after a simple statement such as 'a=1', we want to execute it
323 # straight away.
364 # straight away.
324 if self.indent_spaces==0:
365 if self.indent_spaces==0:
325 if len(self.source.splitlines()) <= 1:
366 if len(self.source.splitlines()) <= 1:
326 return False
367 return False
327
368
328 try:
369 try:
329 code_ast = ast.parse(u''.join(self._buffer))
370 code_ast = ast.parse(u''.join(self._buffer))
330 except Exception:
371 except Exception:
331 #print("Can't parse AST") # debug
372 #print("Can't parse AST") # debug
332 return False
373 return False
333 else:
374 else:
334 if len(code_ast.body) == 1 and \
375 if len(code_ast.body) == 1 and \
335 not hasattr(code_ast.body[0], 'body'):
376 not hasattr(code_ast.body[0], 'body'):
336 #print("Simple statement") # debug
377 #print("Simple statement") # debug
337 return False
378 return False
338
379
339 # General fallback - accept more code
380 # General fallback - accept more code
340 return True
381 return True
341
382
342 #------------------------------------------------------------------------
383 #------------------------------------------------------------------------
343 # Private interface
384 # Private interface
344 #------------------------------------------------------------------------
385 #------------------------------------------------------------------------
345
386
346 def _find_indent(self, line):
387 def _find_indent(self, line):
347 """Compute the new indentation level for a single line.
388 """Compute the new indentation level for a single line.
348
389
349 Parameters
390 Parameters
350 ----------
391 ----------
351 line : str
392 line : str
352 A single new line of non-whitespace, non-comment Python input.
393 A single new line of non-whitespace, non-comment Python input.
353
394
354 Returns
395 Returns
355 -------
396 -------
356 indent_spaces : int
397 indent_spaces : int
357 New value for the indent level (it may be equal to self.indent_spaces
398 New value for the indent level (it may be equal to self.indent_spaces
358 if indentation doesn't change.
399 if indentation doesn't change.
359
400
360 full_dedent : boolean
401 full_dedent : boolean
361 Whether the new line causes a full flush-left dedent.
402 Whether the new line causes a full flush-left dedent.
362 """
403 """
363 indent_spaces = self.indent_spaces
404 indent_spaces = self.indent_spaces
364 full_dedent = self._full_dedent
405 full_dedent = self._full_dedent
365
406
366 inisp = num_ini_spaces(line)
407 inisp = num_ini_spaces(line)
367 if inisp < indent_spaces:
408 if inisp < indent_spaces:
368 indent_spaces = inisp
409 indent_spaces = inisp
369 if indent_spaces <= 0:
410 if indent_spaces <= 0:
370 #print 'Full dedent in text',self.source # dbg
411 #print 'Full dedent in text',self.source # dbg
371 full_dedent = True
412 full_dedent = True
372
413
373 if line.rstrip()[-1] == ':':
414 if line.rstrip()[-1] == ':':
374 indent_spaces += 4
415 indent_spaces += 4
375 elif dedent_re.match(line):
416 elif dedent_re.match(line):
376 indent_spaces -= 4
417 indent_spaces -= 4
377 if indent_spaces <= 0:
418 if indent_spaces <= 0:
378 full_dedent = True
419 full_dedent = True
379
420
380 # Safety
421 # Safety
381 if indent_spaces < 0:
422 if indent_spaces < 0:
382 indent_spaces = 0
423 indent_spaces = 0
383 #print 'safety' # dbg
424 #print 'safety' # dbg
384
425
385 return indent_spaces, full_dedent
426 return indent_spaces, full_dedent
386
427
387 def _update_indent(self, lines):
428 def _update_indent(self, lines):
388 for line in remove_comments(lines).splitlines():
429 for line in remove_comments(lines).splitlines():
389 if line and not line.isspace():
430 if line and not line.isspace():
390 self.indent_spaces, self._full_dedent = self._find_indent(line)
431 self.indent_spaces, self._full_dedent = self._find_indent(line)
391
432
392 def _store(self, lines, buffer=None, store='source'):
433 def _store(self, lines, buffer=None, store='source'):
393 """Store one or more lines of input.
434 """Store one or more lines of input.
394
435
395 If input lines are not newline-terminated, a newline is automatically
436 If input lines are not newline-terminated, a newline is automatically
396 appended."""
437 appended."""
397
438
398 if buffer is None:
439 if buffer is None:
399 buffer = self._buffer
440 buffer = self._buffer
400
441
401 if lines.endswith('\n'):
442 if lines.endswith('\n'):
402 buffer.append(lines)
443 buffer.append(lines)
403 else:
444 else:
404 buffer.append(lines+'\n')
445 buffer.append(lines+'\n')
405 setattr(self, store, self._set_source(buffer))
446 setattr(self, store, self._set_source(buffer))
406
447
407 def _set_source(self, buffer):
448 def _set_source(self, buffer):
408 return u''.join(buffer)
449 return u''.join(buffer)
409
450
410
451
411 class IPythonInputSplitter(InputSplitter):
452 class IPythonInputSplitter(InputSplitter):
412 """An input splitter that recognizes all of IPython's special syntax."""
453 """An input splitter that recognizes all of IPython's special syntax."""
413
454
414 # String with raw, untransformed input.
455 # String with raw, untransformed input.
415 source_raw = ''
456 source_raw = ''
416
457
417 # Flag to track when a transformer has stored input that it hasn't given
458 # Flag to track when a transformer has stored input that it hasn't given
418 # back yet.
459 # back yet.
419 transformer_accumulating = False
460 transformer_accumulating = False
420
461
421 # Flag to track when assemble_python_lines has stored input that it hasn't
462 # Flag to track when assemble_python_lines has stored input that it hasn't
422 # given back yet.
463 # given back yet.
423 within_python_line = False
464 within_python_line = False
424
465
425 # Private attributes
466 # Private attributes
426
467
427 # List with lines of raw input accumulated so far.
468 # List with lines of raw input accumulated so far.
428 _buffer_raw = None
469 _buffer_raw = None
429
470
430 def __init__(self, line_input_checker=True, physical_line_transforms=None,
471 def __init__(self, line_input_checker=True, physical_line_transforms=None,
431 logical_line_transforms=None, python_line_transforms=None):
472 logical_line_transforms=None, python_line_transforms=None):
432 super(IPythonInputSplitter, self).__init__()
473 super(IPythonInputSplitter, self).__init__()
433 self._buffer_raw = []
474 self._buffer_raw = []
434 self._validate = True
475 self._validate = True
435
476
436 if physical_line_transforms is not None:
477 if physical_line_transforms is not None:
437 self.physical_line_transforms = physical_line_transforms
478 self.physical_line_transforms = physical_line_transforms
438 else:
479 else:
439 self.physical_line_transforms = [
480 self.physical_line_transforms = [
440 leading_indent(),
481 leading_indent(),
441 classic_prompt(),
482 classic_prompt(),
442 ipy_prompt(),
483 ipy_prompt(),
443 strip_encoding_cookie(),
484 strip_encoding_cookie(),
444 cellmagic(end_on_blank_line=line_input_checker),
485 cellmagic(end_on_blank_line=line_input_checker),
445 ]
486 ]
446
487
447 self.assemble_logical_lines = assemble_logical_lines()
488 self.assemble_logical_lines = assemble_logical_lines()
448 if logical_line_transforms is not None:
489 if logical_line_transforms is not None:
449 self.logical_line_transforms = logical_line_transforms
490 self.logical_line_transforms = logical_line_transforms
450 else:
491 else:
451 self.logical_line_transforms = [
492 self.logical_line_transforms = [
452 help_end(),
493 help_end(),
453 escaped_commands(),
494 escaped_commands(),
454 assign_from_magic(),
495 assign_from_magic(),
455 assign_from_system(),
496 assign_from_system(),
456 ]
497 ]
457
498
458 self.assemble_python_lines = assemble_python_lines()
499 self.assemble_python_lines = assemble_python_lines()
459 if python_line_transforms is not None:
500 if python_line_transforms is not None:
460 self.python_line_transforms = python_line_transforms
501 self.python_line_transforms = python_line_transforms
461 else:
502 else:
462 # We don't use any of these at present
503 # We don't use any of these at present
463 self.python_line_transforms = []
504 self.python_line_transforms = []
464
505
465 @property
506 @property
466 def transforms(self):
507 def transforms(self):
467 "Quick access to all transformers."
508 "Quick access to all transformers."
468 return self.physical_line_transforms + \
509 return self.physical_line_transforms + \
469 [self.assemble_logical_lines] + self.logical_line_transforms + \
510 [self.assemble_logical_lines] + self.logical_line_transforms + \
470 [self.assemble_python_lines] + self.python_line_transforms
511 [self.assemble_python_lines] + self.python_line_transforms
471
512
472 @property
513 @property
473 def transforms_in_use(self):
514 def transforms_in_use(self):
474 """Transformers, excluding logical line transformers if we're in a
515 """Transformers, excluding logical line transformers if we're in a
475 Python line."""
516 Python line."""
476 t = self.physical_line_transforms[:]
517 t = self.physical_line_transforms[:]
477 if not self.within_python_line:
518 if not self.within_python_line:
478 t += [self.assemble_logical_lines] + self.logical_line_transforms
519 t += [self.assemble_logical_lines] + self.logical_line_transforms
479 return t + [self.assemble_python_lines] + self.python_line_transforms
520 return t + [self.assemble_python_lines] + self.python_line_transforms
480
521
481 def reset(self):
522 def reset(self):
482 """Reset the input buffer and associated state."""
523 """Reset the input buffer and associated state."""
483 super(IPythonInputSplitter, self).reset()
524 super(IPythonInputSplitter, self).reset()
484 self._buffer_raw[:] = []
525 self._buffer_raw[:] = []
485 self.source_raw = ''
526 self.source_raw = ''
486 self.transformer_accumulating = False
527 self.transformer_accumulating = False
487 self.within_python_line = False
528 self.within_python_line = False
488
529
489 for t in self.transforms:
530 for t in self.transforms:
490 try:
531 try:
491 t.reset()
532 t.reset()
492 except SyntaxError:
533 except SyntaxError:
493 # Nothing that calls reset() expects to handle transformer
534 # Nothing that calls reset() expects to handle transformer
494 # errors
535 # errors
495 pass
536 pass
496
537
497 def flush_transformers(self):
538 def flush_transformers(self):
498 def _flush(transform, outs):
539 def _flush(transform, outs):
499 """yield transformed lines
540 """yield transformed lines
500
541
501 always strings, never None
542 always strings, never None
502
543
503 transform: the current transform
544 transform: the current transform
504 outs: an iterable of previously transformed inputs.
545 outs: an iterable of previously transformed inputs.
505 Each may be multiline, which will be passed
546 Each may be multiline, which will be passed
506 one line at a time to transform.
547 one line at a time to transform.
507 """
548 """
508 for out in outs:
549 for out in outs:
509 for line in out.splitlines():
550 for line in out.splitlines():
510 # push one line at a time
551 # push one line at a time
511 tmp = transform.push(line)
552 tmp = transform.push(line)
512 if tmp is not None:
553 if tmp is not None:
513 yield tmp
554 yield tmp
514
555
515 # reset the transform
556 # reset the transform
516 tmp = transform.reset()
557 tmp = transform.reset()
517 if tmp is not None:
558 if tmp is not None:
518 yield tmp
559 yield tmp
519
560
520 out = []
561 out = []
521 for t in self.transforms_in_use:
562 for t in self.transforms_in_use:
522 out = _flush(t, out)
563 out = _flush(t, out)
523
564
524 out = list(out)
565 out = list(out)
525 if out:
566 if out:
526 self._store('\n'.join(out))
567 self._store('\n'.join(out))
527
568
528 def raw_reset(self):
569 def raw_reset(self):
529 """Return raw input only and perform a full reset.
570 """Return raw input only and perform a full reset.
530 """
571 """
531 out = self.source_raw
572 out = self.source_raw
532 self.reset()
573 self.reset()
533 return out
574 return out
534
575
535 def source_reset(self):
576 def source_reset(self):
536 try:
577 try:
537 self.flush_transformers()
578 self.flush_transformers()
538 return self.source
579 return self.source
539 finally:
580 finally:
540 self.reset()
581 self.reset()
541
582
542 def push_accepts_more(self):
583 def push_accepts_more(self):
543 if self.transformer_accumulating:
584 if self.transformer_accumulating:
544 return True
585 return True
545 else:
586 else:
546 return super(IPythonInputSplitter, self).push_accepts_more()
587 return super(IPythonInputSplitter, self).push_accepts_more()
547
588
548 def transform_cell(self, cell):
589 def transform_cell(self, cell):
549 """Process and translate a cell of input.
590 """Process and translate a cell of input.
550 """
591 """
551 self.reset()
592 self.reset()
552 try:
593 try:
553 self.push(cell)
594 self.push(cell)
554 self.flush_transformers()
595 self.flush_transformers()
555 return self.source
596 return self.source
556 finally:
597 finally:
557 self.reset()
598 self.reset()
558
599
559 def push(self, lines):
600 def push(self, lines):
560 """Push one or more lines of IPython input.
601 """Push one or more lines of IPython input.
561
602
562 This stores the given lines and returns a status code indicating
603 This stores the given lines and returns a status code indicating
563 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
564 all input lines for special IPython syntax.
605 all input lines for special IPython syntax.
565
606
566 Any exceptions generated in compilation are swallowed, but if an
607 Any exceptions generated in compilation are swallowed, but if an
567 exception was produced, the method returns True.
608 exception was produced, the method returns True.
568
609
569 Parameters
610 Parameters
570 ----------
611 ----------
571 lines : string
612 lines : string
572 One or more lines of Python input.
613 One or more lines of Python input.
573
614
574 Returns
615 Returns
575 -------
616 -------
576 is_complete : boolean
617 is_complete : boolean
577 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
578 plus prior inputs) forms a complete Python execution block. Note that
619 plus prior inputs) forms a complete Python execution block. Note that
579 this value is also stored as a private attribute (_is_complete), so it
620 this value is also stored as a private attribute (_is_complete), so it
580 can be queried at any time.
621 can be queried at any time.
581 """
622 """
582
623
583 # We must ensure all input is pure unicode
624 # We must ensure all input is pure unicode
584 lines = cast_unicode(lines, self.encoding)
625 lines = cast_unicode(lines, self.encoding)
585
626
586 # ''.splitlines() --> [], but we need to push the empty line to transformers
627 # ''.splitlines() --> [], but we need to push the empty line to transformers
587 lines_list = lines.splitlines()
628 lines_list = lines.splitlines()
588 if not lines_list:
629 if not lines_list:
589 lines_list = ['']
630 lines_list = ['']
590
631
591 # Store raw source before applying any transformations to it. Note
632 # Store raw source before applying any transformations to it. Note
592 # 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
593 # flush the buffer.
634 # flush the buffer.
594 self._store(lines, self._buffer_raw, 'source_raw')
635 self._store(lines, self._buffer_raw, 'source_raw')
595
636
596 for line in lines_list:
637 for line in lines_list:
597 out = self.push_line(line)
638 out = self.push_line(line)
598
639
599 return out
640 return out
600
641
601 def push_line(self, line):
642 def push_line(self, line):
602 buf = self._buffer
643 buf = self._buffer
603
644
604 def _accumulating(dbg):
645 def _accumulating(dbg):
605 #print(dbg)
646 #print(dbg)
606 self.transformer_accumulating = True
647 self.transformer_accumulating = True
607 return False
648 return False
608
649
609 for transformer in self.physical_line_transforms:
650 for transformer in self.physical_line_transforms:
610 line = transformer.push(line)
651 line = transformer.push(line)
611 if line is None:
652 if line is None:
612 return _accumulating(transformer)
653 return _accumulating(transformer)
613
654
614 if not self.within_python_line:
655 if not self.within_python_line:
615 line = self.assemble_logical_lines.push(line)
656 line = self.assemble_logical_lines.push(line)
616 if line is None:
657 if line is None:
617 return _accumulating('acc logical line')
658 return _accumulating('acc logical line')
618
659
619 for transformer in self.logical_line_transforms:
660 for transformer in self.logical_line_transforms:
620 line = transformer.push(line)
661 line = transformer.push(line)
621 if line is None:
662 if line is None:
622 return _accumulating(transformer)
663 return _accumulating(transformer)
623
664
624 line = self.assemble_python_lines.push(line)
665 line = self.assemble_python_lines.push(line)
625 if line is None:
666 if line is None:
626 self.within_python_line = True
667 self.within_python_line = True
627 return _accumulating('acc python line')
668 return _accumulating('acc python line')
628 else:
669 else:
629 self.within_python_line = False
670 self.within_python_line = False
630
671
631 for transformer in self.python_line_transforms:
672 for transformer in self.python_line_transforms:
632 line = transformer.push(line)
673 line = transformer.push(line)
633 if line is None:
674 if line is None:
634 return _accumulating(transformer)
675 return _accumulating(transformer)
635
676
636 #print("transformers clear") #debug
677 #print("transformers clear") #debug
637 self.transformer_accumulating = False
678 self.transformer_accumulating = False
638 return super(IPythonInputSplitter, self).push(line)
679 return super(IPythonInputSplitter, self).push(line)
@@ -1,597 +1,604 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module."""
2 """Tests for the inputsplitter module."""
3
3
4 from __future__ import print_function
4 from __future__ import print_function
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import unittest
9 import unittest
10 import sys
10 import sys
11
11
12 import nose.tools as nt
12 import nose.tools as nt
13
13
14 from IPython.core import inputsplitter as isp
14 from IPython.core import inputsplitter as isp
15 from IPython.core.inputtransformer import InputTransformer
15 from IPython.core.inputtransformer import InputTransformer
16 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
16 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
17 from IPython.testing import tools as tt
17 from IPython.testing import tools as tt
18 from IPython.utils import py3compat
18 from IPython.utils import py3compat
19 from IPython.utils.py3compat import string_types, input
19 from IPython.utils.py3compat import string_types, input
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Semi-complete examples (also used as tests)
22 # Semi-complete examples (also used as tests)
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 # Note: at the bottom, there's a slightly more complete version of this that
25 # Note: at the bottom, there's a slightly more complete version of this that
26 # can be useful during development of code here.
26 # can be useful during development of code here.
27
27
28 def mini_interactive_loop(input_func):
28 def mini_interactive_loop(input_func):
29 """Minimal example of the logic of an interactive interpreter loop.
29 """Minimal example of the logic of an interactive interpreter loop.
30
30
31 This serves as an example, and it is used by the test system with a fake
31 This serves as an example, and it is used by the test system with a fake
32 raw_input that simulates interactive input."""
32 raw_input that simulates interactive input."""
33
33
34 from IPython.core.inputsplitter import InputSplitter
34 from IPython.core.inputsplitter import InputSplitter
35
35
36 isp = InputSplitter()
36 isp = InputSplitter()
37 # In practice, this input loop would be wrapped in an outside loop to read
37 # In practice, this input loop would be wrapped in an outside loop to read
38 # input indefinitely, until some exit/quit command was issued. Here we
38 # input indefinitely, until some exit/quit command was issued. Here we
39 # only illustrate the basic inner loop.
39 # only illustrate the basic inner loop.
40 while isp.push_accepts_more():
40 while isp.push_accepts_more():
41 indent = ' '*isp.indent_spaces
41 indent = ' '*isp.indent_spaces
42 prompt = '>>> ' + indent
42 prompt = '>>> ' + indent
43 line = indent + input_func(prompt)
43 line = indent + input_func(prompt)
44 isp.push(line)
44 isp.push(line)
45
45
46 # Here we just return input so we can use it in a test suite, but a real
46 # Here we just return input so we can use it in a test suite, but a real
47 # interpreter would instead send it for execution somewhere.
47 # interpreter would instead send it for execution somewhere.
48 src = isp.source_reset()
48 src = isp.source_reset()
49 #print 'Input source was:\n', src # dbg
49 #print 'Input source was:\n', src # dbg
50 return src
50 return src
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # Test utilities, just for local use
53 # Test utilities, just for local use
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56 def assemble(block):
56 def assemble(block):
57 """Assemble a block into multi-line sub-blocks."""
57 """Assemble a block into multi-line sub-blocks."""
58 return ['\n'.join(sub_block)+'\n' for sub_block in block]
58 return ['\n'.join(sub_block)+'\n' for sub_block in block]
59
59
60
60
61 def pseudo_input(lines):
61 def pseudo_input(lines):
62 """Return a function that acts like raw_input but feeds the input list."""
62 """Return a function that acts like raw_input but feeds the input list."""
63 ilines = iter(lines)
63 ilines = iter(lines)
64 def raw_in(prompt):
64 def raw_in(prompt):
65 try:
65 try:
66 return next(ilines)
66 return next(ilines)
67 except StopIteration:
67 except StopIteration:
68 return ''
68 return ''
69 return raw_in
69 return raw_in
70
70
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72 # Tests
72 # Tests
73 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
74 def test_spaces():
74 def test_spaces():
75 tests = [('', 0),
75 tests = [('', 0),
76 (' ', 1),
76 (' ', 1),
77 ('\n', 0),
77 ('\n', 0),
78 (' \n', 1),
78 (' \n', 1),
79 ('x', 0),
79 ('x', 0),
80 (' x', 1),
80 (' x', 1),
81 (' x',2),
81 (' x',2),
82 (' x',4),
82 (' x',4),
83 # Note: tabs are counted as a single whitespace!
83 # Note: tabs are counted as a single whitespace!
84 ('\tx', 1),
84 ('\tx', 1),
85 ('\t x', 2),
85 ('\t x', 2),
86 ]
86 ]
87 tt.check_pairs(isp.num_ini_spaces, tests)
87 tt.check_pairs(isp.num_ini_spaces, tests)
88
88
89
89
90 def test_remove_comments():
90 def test_remove_comments():
91 tests = [('text', 'text'),
91 tests = [('text', 'text'),
92 ('text # comment', 'text '),
92 ('text # comment', 'text '),
93 ('text # comment\n', 'text \n'),
93 ('text # comment\n', 'text \n'),
94 ('text # comment \n', 'text \n'),
94 ('text # comment \n', 'text \n'),
95 ('line # c \nline\n','line \nline\n'),
95 ('line # c \nline\n','line \nline\n'),
96 ('line # c \nline#c2 \nline\nline #c\n\n',
96 ('line # c \nline#c2 \nline\nline #c\n\n',
97 'line \nline\nline\nline \n\n'),
97 'line \nline\nline\nline \n\n'),
98 ]
98 ]
99 tt.check_pairs(isp.remove_comments, tests)
99 tt.check_pairs(isp.remove_comments, tests)
100
100
101
101
102 def test_get_input_encoding():
102 def test_get_input_encoding():
103 encoding = isp.get_input_encoding()
103 encoding = isp.get_input_encoding()
104 nt.assert_true(isinstance(encoding, string_types))
104 nt.assert_true(isinstance(encoding, string_types))
105 # simple-minded check that at least encoding a simple string works with the
105 # simple-minded check that at least encoding a simple string works with the
106 # encoding we got.
106 # encoding we got.
107 nt.assert_equal(u'test'.encode(encoding), b'test')
107 nt.assert_equal(u'test'.encode(encoding), b'test')
108
108
109
109
110 class NoInputEncodingTestCase(unittest.TestCase):
110 class NoInputEncodingTestCase(unittest.TestCase):
111 def setUp(self):
111 def setUp(self):
112 self.old_stdin = sys.stdin
112 self.old_stdin = sys.stdin
113 class X: pass
113 class X: pass
114 fake_stdin = X()
114 fake_stdin = X()
115 sys.stdin = fake_stdin
115 sys.stdin = fake_stdin
116
116
117 def test(self):
117 def test(self):
118 # Verify that if sys.stdin has no 'encoding' attribute we do the right
118 # Verify that if sys.stdin has no 'encoding' attribute we do the right
119 # thing
119 # thing
120 enc = isp.get_input_encoding()
120 enc = isp.get_input_encoding()
121 self.assertEqual(enc, 'ascii')
121 self.assertEqual(enc, 'ascii')
122
122
123 def tearDown(self):
123 def tearDown(self):
124 sys.stdin = self.old_stdin
124 sys.stdin = self.old_stdin
125
125
126
126
127 class InputSplitterTestCase(unittest.TestCase):
127 class InputSplitterTestCase(unittest.TestCase):
128 def setUp(self):
128 def setUp(self):
129 self.isp = isp.InputSplitter()
129 self.isp = isp.InputSplitter()
130
130
131 def test_reset(self):
131 def test_reset(self):
132 isp = self.isp
132 isp = self.isp
133 isp.push('x=1')
133 isp.push('x=1')
134 isp.reset()
134 isp.reset()
135 self.assertEqual(isp._buffer, [])
135 self.assertEqual(isp._buffer, [])
136 self.assertEqual(isp.indent_spaces, 0)
136 self.assertEqual(isp.indent_spaces, 0)
137 self.assertEqual(isp.source, '')
137 self.assertEqual(isp.source, '')
138 self.assertEqual(isp.code, None)
138 self.assertEqual(isp.code, None)
139 self.assertEqual(isp._is_complete, False)
139 self.assertEqual(isp._is_complete, False)
140
140
141 def test_source(self):
141 def test_source(self):
142 self.isp._store('1')
142 self.isp._store('1')
143 self.isp._store('2')
143 self.isp._store('2')
144 self.assertEqual(self.isp.source, '1\n2\n')
144 self.assertEqual(self.isp.source, '1\n2\n')
145 self.assertTrue(len(self.isp._buffer)>0)
145 self.assertTrue(len(self.isp._buffer)>0)
146 self.assertEqual(self.isp.source_reset(), '1\n2\n')
146 self.assertEqual(self.isp.source_reset(), '1\n2\n')
147 self.assertEqual(self.isp._buffer, [])
147 self.assertEqual(self.isp._buffer, [])
148 self.assertEqual(self.isp.source, '')
148 self.assertEqual(self.isp.source, '')
149
149
150 def test_indent(self):
150 def test_indent(self):
151 isp = self.isp # shorthand
151 isp = self.isp # shorthand
152 isp.push('x=1')
152 isp.push('x=1')
153 self.assertEqual(isp.indent_spaces, 0)
153 self.assertEqual(isp.indent_spaces, 0)
154 isp.push('if 1:\n x=1')
154 isp.push('if 1:\n x=1')
155 self.assertEqual(isp.indent_spaces, 4)
155 self.assertEqual(isp.indent_spaces, 4)
156 isp.push('y=2\n')
156 isp.push('y=2\n')
157 self.assertEqual(isp.indent_spaces, 0)
157 self.assertEqual(isp.indent_spaces, 0)
158
158
159 def test_indent2(self):
159 def test_indent2(self):
160 isp = self.isp
160 isp = self.isp
161 isp.push('if 1:')
161 isp.push('if 1:')
162 self.assertEqual(isp.indent_spaces, 4)
162 self.assertEqual(isp.indent_spaces, 4)
163 isp.push(' x=1')
163 isp.push(' x=1')
164 self.assertEqual(isp.indent_spaces, 4)
164 self.assertEqual(isp.indent_spaces, 4)
165 # Blank lines shouldn't change the indent level
165 # Blank lines shouldn't change the indent level
166 isp.push(' '*2)
166 isp.push(' '*2)
167 self.assertEqual(isp.indent_spaces, 4)
167 self.assertEqual(isp.indent_spaces, 4)
168
168
169 def test_indent3(self):
169 def test_indent3(self):
170 isp = self.isp
170 isp = self.isp
171 # When a multiline statement contains parens or multiline strings, we
171 # When a multiline statement contains parens or multiline strings, we
172 # shouldn't get confused.
172 # shouldn't get confused.
173 isp.push("if 1:")
173 isp.push("if 1:")
174 isp.push(" x = (1+\n 2)")
174 isp.push(" x = (1+\n 2)")
175 self.assertEqual(isp.indent_spaces, 4)
175 self.assertEqual(isp.indent_spaces, 4)
176
176
177 def test_indent4(self):
177 def test_indent4(self):
178 isp = self.isp
178 isp = self.isp
179 # whitespace after ':' should not screw up indent level
179 # whitespace after ':' should not screw up indent level
180 isp.push('if 1: \n x=1')
180 isp.push('if 1: \n x=1')
181 self.assertEqual(isp.indent_spaces, 4)
181 self.assertEqual(isp.indent_spaces, 4)
182 isp.push('y=2\n')
182 isp.push('y=2\n')
183 self.assertEqual(isp.indent_spaces, 0)
183 self.assertEqual(isp.indent_spaces, 0)
184 isp.push('if 1:\t\n x=1')
184 isp.push('if 1:\t\n x=1')
185 self.assertEqual(isp.indent_spaces, 4)
185 self.assertEqual(isp.indent_spaces, 4)
186 isp.push('y=2\n')
186 isp.push('y=2\n')
187 self.assertEqual(isp.indent_spaces, 0)
187 self.assertEqual(isp.indent_spaces, 0)
188
188
189 def test_dedent_pass(self):
189 def test_dedent_pass(self):
190 isp = self.isp # shorthand
190 isp = self.isp # shorthand
191 # should NOT cause dedent
191 # should NOT cause dedent
192 isp.push('if 1:\n passes = 5')
192 isp.push('if 1:\n passes = 5')
193 self.assertEqual(isp.indent_spaces, 4)
193 self.assertEqual(isp.indent_spaces, 4)
194 isp.push('if 1:\n pass')
194 isp.push('if 1:\n pass')
195 self.assertEqual(isp.indent_spaces, 0)
195 self.assertEqual(isp.indent_spaces, 0)
196 isp.push('if 1:\n pass ')
196 isp.push('if 1:\n pass ')
197 self.assertEqual(isp.indent_spaces, 0)
197 self.assertEqual(isp.indent_spaces, 0)
198
198
199 def test_dedent_break(self):
199 def test_dedent_break(self):
200 isp = self.isp # shorthand
200 isp = self.isp # shorthand
201 # should NOT cause dedent
201 # should NOT cause dedent
202 isp.push('while 1:\n breaks = 5')
202 isp.push('while 1:\n breaks = 5')
203 self.assertEqual(isp.indent_spaces, 4)
203 self.assertEqual(isp.indent_spaces, 4)
204 isp.push('while 1:\n break')
204 isp.push('while 1:\n break')
205 self.assertEqual(isp.indent_spaces, 0)
205 self.assertEqual(isp.indent_spaces, 0)
206 isp.push('while 1:\n break ')
206 isp.push('while 1:\n break ')
207 self.assertEqual(isp.indent_spaces, 0)
207 self.assertEqual(isp.indent_spaces, 0)
208
208
209 def test_dedent_continue(self):
209 def test_dedent_continue(self):
210 isp = self.isp # shorthand
210 isp = self.isp # shorthand
211 # should NOT cause dedent
211 # should NOT cause dedent
212 isp.push('while 1:\n continues = 5')
212 isp.push('while 1:\n continues = 5')
213 self.assertEqual(isp.indent_spaces, 4)
213 self.assertEqual(isp.indent_spaces, 4)
214 isp.push('while 1:\n continue')
214 isp.push('while 1:\n continue')
215 self.assertEqual(isp.indent_spaces, 0)
215 self.assertEqual(isp.indent_spaces, 0)
216 isp.push('while 1:\n continue ')
216 isp.push('while 1:\n continue ')
217 self.assertEqual(isp.indent_spaces, 0)
217 self.assertEqual(isp.indent_spaces, 0)
218
218
219 def test_dedent_raise(self):
219 def test_dedent_raise(self):
220 isp = self.isp # shorthand
220 isp = self.isp # shorthand
221 # should NOT cause dedent
221 # should NOT cause dedent
222 isp.push('if 1:\n raised = 4')
222 isp.push('if 1:\n raised = 4')
223 self.assertEqual(isp.indent_spaces, 4)
223 self.assertEqual(isp.indent_spaces, 4)
224 isp.push('if 1:\n raise TypeError()')
224 isp.push('if 1:\n raise TypeError()')
225 self.assertEqual(isp.indent_spaces, 0)
225 self.assertEqual(isp.indent_spaces, 0)
226 isp.push('if 1:\n raise')
226 isp.push('if 1:\n raise')
227 self.assertEqual(isp.indent_spaces, 0)
227 self.assertEqual(isp.indent_spaces, 0)
228 isp.push('if 1:\n raise ')
228 isp.push('if 1:\n raise ')
229 self.assertEqual(isp.indent_spaces, 0)
229 self.assertEqual(isp.indent_spaces, 0)
230
230
231 def test_dedent_return(self):
231 def test_dedent_return(self):
232 isp = self.isp # shorthand
232 isp = self.isp # shorthand
233 # should NOT cause dedent
233 # should NOT cause dedent
234 isp.push('if 1:\n returning = 4')
234 isp.push('if 1:\n returning = 4')
235 self.assertEqual(isp.indent_spaces, 4)
235 self.assertEqual(isp.indent_spaces, 4)
236 isp.push('if 1:\n return 5 + 493')
236 isp.push('if 1:\n return 5 + 493')
237 self.assertEqual(isp.indent_spaces, 0)
237 self.assertEqual(isp.indent_spaces, 0)
238 isp.push('if 1:\n return')
238 isp.push('if 1:\n return')
239 self.assertEqual(isp.indent_spaces, 0)
239 self.assertEqual(isp.indent_spaces, 0)
240 isp.push('if 1:\n return ')
240 isp.push('if 1:\n return ')
241 self.assertEqual(isp.indent_spaces, 0)
241 self.assertEqual(isp.indent_spaces, 0)
242 isp.push('if 1:\n return(0)')
242 isp.push('if 1:\n return(0)')
243 self.assertEqual(isp.indent_spaces, 0)
243 self.assertEqual(isp.indent_spaces, 0)
244
244
245 def test_push(self):
245 def test_push(self):
246 isp = self.isp
246 isp = self.isp
247 self.assertTrue(isp.push('x=1'))
247 self.assertTrue(isp.push('x=1'))
248
248
249 def test_push2(self):
249 def test_push2(self):
250 isp = self.isp
250 isp = self.isp
251 self.assertFalse(isp.push('if 1:'))
251 self.assertFalse(isp.push('if 1:'))
252 for line in [' x=1', '# a comment', ' y=2']:
252 for line in [' x=1', '# a comment', ' y=2']:
253 print(line)
253 print(line)
254 self.assertTrue(isp.push(line))
254 self.assertTrue(isp.push(line))
255
255
256 def test_push3(self):
256 def test_push3(self):
257 isp = self.isp
257 isp = self.isp
258 isp.push('if True:')
258 isp.push('if True:')
259 isp.push(' a = 1')
259 isp.push(' a = 1')
260 self.assertFalse(isp.push('b = [1,'))
260 self.assertFalse(isp.push('b = [1,'))
261
261
262 def test_push_accepts_more(self):
262 def test_push_accepts_more(self):
263 isp = self.isp
263 isp = self.isp
264 isp.push('x=1')
264 isp.push('x=1')
265 self.assertFalse(isp.push_accepts_more())
265 self.assertFalse(isp.push_accepts_more())
266
266
267 def test_push_accepts_more2(self):
267 def test_push_accepts_more2(self):
268 isp = self.isp
268 isp = self.isp
269 isp.push('if 1:')
269 isp.push('if 1:')
270 self.assertTrue(isp.push_accepts_more())
270 self.assertTrue(isp.push_accepts_more())
271 isp.push(' x=1')
271 isp.push(' x=1')
272 self.assertTrue(isp.push_accepts_more())
272 self.assertTrue(isp.push_accepts_more())
273 isp.push('')
273 isp.push('')
274 self.assertFalse(isp.push_accepts_more())
274 self.assertFalse(isp.push_accepts_more())
275
275
276 def test_push_accepts_more3(self):
276 def test_push_accepts_more3(self):
277 isp = self.isp
277 isp = self.isp
278 isp.push("x = (2+\n3)")
278 isp.push("x = (2+\n3)")
279 self.assertFalse(isp.push_accepts_more())
279 self.assertFalse(isp.push_accepts_more())
280
280
281 def test_push_accepts_more4(self):
281 def test_push_accepts_more4(self):
282 isp = self.isp
282 isp = self.isp
283 # When a multiline statement contains parens or multiline strings, we
283 # When a multiline statement contains parens or multiline strings, we
284 # shouldn't get confused.
284 # shouldn't get confused.
285 # FIXME: we should be able to better handle de-dents in statements like
285 # FIXME: we should be able to better handle de-dents in statements like
286 # multiline strings and multiline expressions (continued with \ or
286 # multiline strings and multiline expressions (continued with \ or
287 # parens). Right now we aren't handling the indentation tracking quite
287 # parens). Right now we aren't handling the indentation tracking quite
288 # correctly with this, though in practice it may not be too much of a
288 # correctly with this, though in practice it may not be too much of a
289 # problem. We'll need to see.
289 # problem. We'll need to see.
290 isp.push("if 1:")
290 isp.push("if 1:")
291 isp.push(" x = (2+")
291 isp.push(" x = (2+")
292 isp.push(" 3)")
292 isp.push(" 3)")
293 self.assertTrue(isp.push_accepts_more())
293 self.assertTrue(isp.push_accepts_more())
294 isp.push(" y = 3")
294 isp.push(" y = 3")
295 self.assertTrue(isp.push_accepts_more())
295 self.assertTrue(isp.push_accepts_more())
296 isp.push('')
296 isp.push('')
297 self.assertFalse(isp.push_accepts_more())
297 self.assertFalse(isp.push_accepts_more())
298
298
299 def test_push_accepts_more5(self):
299 def test_push_accepts_more5(self):
300 isp = self.isp
300 isp = self.isp
301 isp.push('try:')
301 isp.push('try:')
302 isp.push(' a = 5')
302 isp.push(' a = 5')
303 isp.push('except:')
303 isp.push('except:')
304 isp.push(' raise')
304 isp.push(' raise')
305 # We want to be able to add an else: block at this point, so it should
305 # We want to be able to add an else: block at this point, so it should
306 # wait for a blank line.
306 # wait for a blank line.
307 self.assertTrue(isp.push_accepts_more())
307 self.assertTrue(isp.push_accepts_more())
308
308
309 def test_continuation(self):
309 def test_continuation(self):
310 isp = self.isp
310 isp = self.isp
311 isp.push("import os, \\")
311 isp.push("import os, \\")
312 self.assertTrue(isp.push_accepts_more())
312 self.assertTrue(isp.push_accepts_more())
313 isp.push("sys")
313 isp.push("sys")
314 self.assertFalse(isp.push_accepts_more())
314 self.assertFalse(isp.push_accepts_more())
315
315
316 def test_syntax_error(self):
316 def test_syntax_error(self):
317 isp = self.isp
317 isp = self.isp
318 # Syntax errors immediately produce a 'ready' block, so the invalid
318 # Syntax errors immediately produce a 'ready' block, so the invalid
319 # Python can be sent to the kernel for evaluation with possible ipython
319 # Python can be sent to the kernel for evaluation with possible ipython
320 # special-syntax conversion.
320 # special-syntax conversion.
321 isp.push('run foo')
321 isp.push('run foo')
322 self.assertFalse(isp.push_accepts_more())
322 self.assertFalse(isp.push_accepts_more())
323
323
324 def test_unicode(self):
324 def test_unicode(self):
325 self.isp.push(u"PΓ©rez")
325 self.isp.push(u"PΓ©rez")
326 self.isp.push(u'\xc3\xa9')
326 self.isp.push(u'\xc3\xa9')
327 self.isp.push(u"u'\xc3\xa9'")
327 self.isp.push(u"u'\xc3\xa9'")
328
328
329 def test_line_continuation(self):
329 def test_line_continuation(self):
330 """ Test issue #2108."""
330 """ Test issue #2108."""
331 isp = self.isp
331 isp = self.isp
332 # A blank line after a line continuation should not accept more
332 # A blank line after a line continuation should not accept more
333 isp.push("1 \\\n\n")
333 isp.push("1 \\\n\n")
334 self.assertFalse(isp.push_accepts_more())
334 self.assertFalse(isp.push_accepts_more())
335 # Whitespace after a \ is a SyntaxError. The only way to test that
335 # Whitespace after a \ is a SyntaxError. The only way to test that
336 # here is to test that push doesn't accept more (as with
336 # here is to test that push doesn't accept more (as with
337 # test_syntax_error() above).
337 # test_syntax_error() above).
338 isp.push(r"1 \ ")
338 isp.push(r"1 \ ")
339 self.assertFalse(isp.push_accepts_more())
339 self.assertFalse(isp.push_accepts_more())
340 # Even if the line is continuable (c.f. the regular Python
340 # Even if the line is continuable (c.f. the regular Python
341 # interpreter)
341 # interpreter)
342 isp.push(r"(1 \ ")
342 isp.push(r"(1 \ ")
343 self.assertFalse(isp.push_accepts_more())
343 self.assertFalse(isp.push_accepts_more())
344
344
345 def test_check_complete(self):
346 isp = self.isp
347 self.assertEqual(isp.check_complete("a = 1"), ('complete', None))
348 self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4))
349 self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None))
350 self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0))
351
345 class InteractiveLoopTestCase(unittest.TestCase):
352 class InteractiveLoopTestCase(unittest.TestCase):
346 """Tests for an interactive loop like a python shell.
353 """Tests for an interactive loop like a python shell.
347 """
354 """
348 def check_ns(self, lines, ns):
355 def check_ns(self, lines, ns):
349 """Validate that the given input lines produce the resulting namespace.
356 """Validate that the given input lines produce the resulting namespace.
350
357
351 Note: the input lines are given exactly as they would be typed in an
358 Note: the input lines are given exactly as they would be typed in an
352 auto-indenting environment, as mini_interactive_loop above already does
359 auto-indenting environment, as mini_interactive_loop above already does
353 auto-indenting and prepends spaces to the input.
360 auto-indenting and prepends spaces to the input.
354 """
361 """
355 src = mini_interactive_loop(pseudo_input(lines))
362 src = mini_interactive_loop(pseudo_input(lines))
356 test_ns = {}
363 test_ns = {}
357 exec(src, test_ns)
364 exec(src, test_ns)
358 # We can't check that the provided ns is identical to the test_ns,
365 # We can't check that the provided ns is identical to the test_ns,
359 # because Python fills test_ns with extra keys (copyright, etc). But
366 # because Python fills test_ns with extra keys (copyright, etc). But
360 # we can check that the given dict is *contained* in test_ns
367 # we can check that the given dict is *contained* in test_ns
361 for k,v in ns.items():
368 for k,v in ns.items():
362 self.assertEqual(test_ns[k], v)
369 self.assertEqual(test_ns[k], v)
363
370
364 def test_simple(self):
371 def test_simple(self):
365 self.check_ns(['x=1'], dict(x=1))
372 self.check_ns(['x=1'], dict(x=1))
366
373
367 def test_simple2(self):
374 def test_simple2(self):
368 self.check_ns(['if 1:', 'x=2'], dict(x=2))
375 self.check_ns(['if 1:', 'x=2'], dict(x=2))
369
376
370 def test_xy(self):
377 def test_xy(self):
371 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
378 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
372
379
373 def test_abc(self):
380 def test_abc(self):
374 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
381 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
375
382
376 def test_multi(self):
383 def test_multi(self):
377 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
384 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
378
385
379
386
380 class IPythonInputTestCase(InputSplitterTestCase):
387 class IPythonInputTestCase(InputSplitterTestCase):
381 """By just creating a new class whose .isp is a different instance, we
388 """By just creating a new class whose .isp is a different instance, we
382 re-run the same test battery on the new input splitter.
389 re-run the same test battery on the new input splitter.
383
390
384 In addition, this runs the tests over the syntax and syntax_ml dicts that
391 In addition, this runs the tests over the syntax and syntax_ml dicts that
385 were tested by individual functions, as part of the OO interface.
392 were tested by individual functions, as part of the OO interface.
386
393
387 It also makes some checks on the raw buffer storage.
394 It also makes some checks on the raw buffer storage.
388 """
395 """
389
396
390 def setUp(self):
397 def setUp(self):
391 self.isp = isp.IPythonInputSplitter()
398 self.isp = isp.IPythonInputSplitter()
392
399
393 def test_syntax(self):
400 def test_syntax(self):
394 """Call all single-line syntax tests from the main object"""
401 """Call all single-line syntax tests from the main object"""
395 isp = self.isp
402 isp = self.isp
396 for example in syntax.values():
403 for example in syntax.values():
397 for raw, out_t in example:
404 for raw, out_t in example:
398 if raw.startswith(' '):
405 if raw.startswith(' '):
399 continue
406 continue
400
407
401 isp.push(raw+'\n')
408 isp.push(raw+'\n')
402 out_raw = isp.source_raw
409 out_raw = isp.source_raw
403 out = isp.source_reset()
410 out = isp.source_reset()
404 self.assertEqual(out.rstrip(), out_t,
411 self.assertEqual(out.rstrip(), out_t,
405 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
412 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
406 self.assertEqual(out_raw.rstrip(), raw.rstrip())
413 self.assertEqual(out_raw.rstrip(), raw.rstrip())
407
414
408 def test_syntax_multiline(self):
415 def test_syntax_multiline(self):
409 isp = self.isp
416 isp = self.isp
410 for example in syntax_ml.values():
417 for example in syntax_ml.values():
411 for line_pairs in example:
418 for line_pairs in example:
412 out_t_parts = []
419 out_t_parts = []
413 raw_parts = []
420 raw_parts = []
414 for lraw, out_t_part in line_pairs:
421 for lraw, out_t_part in line_pairs:
415 if out_t_part is not None:
422 if out_t_part is not None:
416 out_t_parts.append(out_t_part)
423 out_t_parts.append(out_t_part)
417
424
418 if lraw is not None:
425 if lraw is not None:
419 isp.push(lraw)
426 isp.push(lraw)
420 raw_parts.append(lraw)
427 raw_parts.append(lraw)
421
428
422 out_raw = isp.source_raw
429 out_raw = isp.source_raw
423 out = isp.source_reset()
430 out = isp.source_reset()
424 out_t = '\n'.join(out_t_parts).rstrip()
431 out_t = '\n'.join(out_t_parts).rstrip()
425 raw = '\n'.join(raw_parts).rstrip()
432 raw = '\n'.join(raw_parts).rstrip()
426 self.assertEqual(out.rstrip(), out_t)
433 self.assertEqual(out.rstrip(), out_t)
427 self.assertEqual(out_raw.rstrip(), raw)
434 self.assertEqual(out_raw.rstrip(), raw)
428
435
429 def test_syntax_multiline_cell(self):
436 def test_syntax_multiline_cell(self):
430 isp = self.isp
437 isp = self.isp
431 for example in syntax_ml.values():
438 for example in syntax_ml.values():
432
439
433 out_t_parts = []
440 out_t_parts = []
434 for line_pairs in example:
441 for line_pairs in example:
435 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
442 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
436 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
443 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
437 out = isp.transform_cell(raw)
444 out = isp.transform_cell(raw)
438 # Match ignoring trailing whitespace
445 # Match ignoring trailing whitespace
439 self.assertEqual(out.rstrip(), out_t.rstrip())
446 self.assertEqual(out.rstrip(), out_t.rstrip())
440
447
441 def test_cellmagic_preempt(self):
448 def test_cellmagic_preempt(self):
442 isp = self.isp
449 isp = self.isp
443 for raw, name, line, cell in [
450 for raw, name, line, cell in [
444 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
451 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
445 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
452 ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'),
446 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
453 (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'),
447 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
454 ("%%cellm \n>>> hi", u'cellm', u'', u'hi'),
448 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
455 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
449 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
456 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
450 ]:
457 ]:
451 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
458 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
452 name, line, cell
459 name, line, cell
453 )
460 )
454 out = isp.transform_cell(raw)
461 out = isp.transform_cell(raw)
455 self.assertEqual(out.rstrip(), expected.rstrip())
462 self.assertEqual(out.rstrip(), expected.rstrip())
456
463
457 def test_multiline_passthrough(self):
464 def test_multiline_passthrough(self):
458 isp = self.isp
465 isp = self.isp
459 class CommentTransformer(InputTransformer):
466 class CommentTransformer(InputTransformer):
460 def __init__(self):
467 def __init__(self):
461 self._lines = []
468 self._lines = []
462
469
463 def push(self, line):
470 def push(self, line):
464 self._lines.append(line + '#')
471 self._lines.append(line + '#')
465
472
466 def reset(self):
473 def reset(self):
467 text = '\n'.join(self._lines)
474 text = '\n'.join(self._lines)
468 self._lines = []
475 self._lines = []
469 return text
476 return text
470
477
471 isp.physical_line_transforms.insert(0, CommentTransformer())
478 isp.physical_line_transforms.insert(0, CommentTransformer())
472
479
473 for raw, expected in [
480 for raw, expected in [
474 ("a=5", "a=5#"),
481 ("a=5", "a=5#"),
475 ("%ls foo", "get_ipython().magic(%r)" % u'ls foo#'),
482 ("%ls foo", "get_ipython().magic(%r)" % u'ls foo#'),
476 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().magic(%r)" % (
483 ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().magic(%r)" % (
477 u'ls foo#', u'ls bar#'
484 u'ls foo#', u'ls bar#'
478 )),
485 )),
479 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().magic(%r)\n4#\n5#" % u'ls foo#'),
486 ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().magic(%r)\n4#\n5#" % u'ls foo#'),
480 ]:
487 ]:
481 out = isp.transform_cell(raw)
488 out = isp.transform_cell(raw)
482 self.assertEqual(out.rstrip(), expected.rstrip())
489 self.assertEqual(out.rstrip(), expected.rstrip())
483
490
484 #-----------------------------------------------------------------------------
491 #-----------------------------------------------------------------------------
485 # Main - use as a script, mostly for developer experiments
492 # Main - use as a script, mostly for developer experiments
486 #-----------------------------------------------------------------------------
493 #-----------------------------------------------------------------------------
487
494
488 if __name__ == '__main__':
495 if __name__ == '__main__':
489 # A simple demo for interactive experimentation. This code will not get
496 # A simple demo for interactive experimentation. This code will not get
490 # picked up by any test suite.
497 # picked up by any test suite.
491 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
498 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
492
499
493 # configure here the syntax to use, prompt and whether to autoindent
500 # configure here the syntax to use, prompt and whether to autoindent
494 #isp, start_prompt = InputSplitter(), '>>> '
501 #isp, start_prompt = InputSplitter(), '>>> '
495 isp, start_prompt = IPythonInputSplitter(), 'In> '
502 isp, start_prompt = IPythonInputSplitter(), 'In> '
496
503
497 autoindent = True
504 autoindent = True
498 #autoindent = False
505 #autoindent = False
499
506
500 try:
507 try:
501 while True:
508 while True:
502 prompt = start_prompt
509 prompt = start_prompt
503 while isp.push_accepts_more():
510 while isp.push_accepts_more():
504 indent = ' '*isp.indent_spaces
511 indent = ' '*isp.indent_spaces
505 if autoindent:
512 if autoindent:
506 line = indent + input(prompt+indent)
513 line = indent + input(prompt+indent)
507 else:
514 else:
508 line = input(prompt)
515 line = input(prompt)
509 isp.push(line)
516 isp.push(line)
510 prompt = '... '
517 prompt = '... '
511
518
512 # Here we just return input so we can use it in a test suite, but a
519 # Here we just return input so we can use it in a test suite, but a
513 # real interpreter would instead send it for execution somewhere.
520 # real interpreter would instead send it for execution somewhere.
514 #src = isp.source; raise EOFError # dbg
521 #src = isp.source; raise EOFError # dbg
515 raw = isp.source_raw
522 raw = isp.source_raw
516 src = isp.source_reset()
523 src = isp.source_reset()
517 print('Input source was:\n', src)
524 print('Input source was:\n', src)
518 print('Raw source was:\n', raw)
525 print('Raw source was:\n', raw)
519 except EOFError:
526 except EOFError:
520 print('Bye')
527 print('Bye')
521
528
522 # Tests for cell magics support
529 # Tests for cell magics support
523
530
524 def test_last_blank():
531 def test_last_blank():
525 nt.assert_false(isp.last_blank(''))
532 nt.assert_false(isp.last_blank(''))
526 nt.assert_false(isp.last_blank('abc'))
533 nt.assert_false(isp.last_blank('abc'))
527 nt.assert_false(isp.last_blank('abc\n'))
534 nt.assert_false(isp.last_blank('abc\n'))
528 nt.assert_false(isp.last_blank('abc\na'))
535 nt.assert_false(isp.last_blank('abc\na'))
529
536
530 nt.assert_true(isp.last_blank('\n'))
537 nt.assert_true(isp.last_blank('\n'))
531 nt.assert_true(isp.last_blank('\n '))
538 nt.assert_true(isp.last_blank('\n '))
532 nt.assert_true(isp.last_blank('abc\n '))
539 nt.assert_true(isp.last_blank('abc\n '))
533 nt.assert_true(isp.last_blank('abc\n\n'))
540 nt.assert_true(isp.last_blank('abc\n\n'))
534 nt.assert_true(isp.last_blank('abc\nd\n\n'))
541 nt.assert_true(isp.last_blank('abc\nd\n\n'))
535 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
542 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
536 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
543 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
537
544
538
545
539 def test_last_two_blanks():
546 def test_last_two_blanks():
540 nt.assert_false(isp.last_two_blanks(''))
547 nt.assert_false(isp.last_two_blanks(''))
541 nt.assert_false(isp.last_two_blanks('abc'))
548 nt.assert_false(isp.last_two_blanks('abc'))
542 nt.assert_false(isp.last_two_blanks('abc\n'))
549 nt.assert_false(isp.last_two_blanks('abc\n'))
543 nt.assert_false(isp.last_two_blanks('abc\n\na'))
550 nt.assert_false(isp.last_two_blanks('abc\n\na'))
544 nt.assert_false(isp.last_two_blanks('abc\n \n'))
551 nt.assert_false(isp.last_two_blanks('abc\n \n'))
545 nt.assert_false(isp.last_two_blanks('abc\n\n'))
552 nt.assert_false(isp.last_two_blanks('abc\n\n'))
546
553
547 nt.assert_true(isp.last_two_blanks('\n\n'))
554 nt.assert_true(isp.last_two_blanks('\n\n'))
548 nt.assert_true(isp.last_two_blanks('\n\n '))
555 nt.assert_true(isp.last_two_blanks('\n\n '))
549 nt.assert_true(isp.last_two_blanks('\n \n'))
556 nt.assert_true(isp.last_two_blanks('\n \n'))
550 nt.assert_true(isp.last_two_blanks('abc\n\n '))
557 nt.assert_true(isp.last_two_blanks('abc\n\n '))
551 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
558 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
552 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
559 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
553 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
560 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
554 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
561 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
555 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
562 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
556 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
563 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
557
564
558
565
559 class CellMagicsCommon(object):
566 class CellMagicsCommon(object):
560
567
561 def test_whole_cell(self):
568 def test_whole_cell(self):
562 src = "%%cellm line\nbody\n"
569 src = "%%cellm line\nbody\n"
563 out = self.sp.transform_cell(src)
570 out = self.sp.transform_cell(src)
564 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
571 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
565 nt.assert_equal(out, py3compat.u_format(ref))
572 nt.assert_equal(out, py3compat.u_format(ref))
566
573
567 def test_cellmagic_help(self):
574 def test_cellmagic_help(self):
568 self.sp.push('%%cellm?')
575 self.sp.push('%%cellm?')
569 nt.assert_false(self.sp.push_accepts_more())
576 nt.assert_false(self.sp.push_accepts_more())
570
577
571 def tearDown(self):
578 def tearDown(self):
572 self.sp.reset()
579 self.sp.reset()
573
580
574
581
575 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
582 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
576 sp = isp.IPythonInputSplitter(line_input_checker=False)
583 sp = isp.IPythonInputSplitter(line_input_checker=False)
577
584
578 def test_incremental(self):
585 def test_incremental(self):
579 sp = self.sp
586 sp = self.sp
580 sp.push('%%cellm firstline\n')
587 sp.push('%%cellm firstline\n')
581 nt.assert_true(sp.push_accepts_more()) #1
588 nt.assert_true(sp.push_accepts_more()) #1
582 sp.push('line2\n')
589 sp.push('line2\n')
583 nt.assert_true(sp.push_accepts_more()) #2
590 nt.assert_true(sp.push_accepts_more()) #2
584 sp.push('\n')
591 sp.push('\n')
585 # This should accept a blank line and carry on until the cell is reset
592 # This should accept a blank line and carry on until the cell is reset
586 nt.assert_true(sp.push_accepts_more()) #3
593 nt.assert_true(sp.push_accepts_more()) #3
587
594
588 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
595 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
589 sp = isp.IPythonInputSplitter(line_input_checker=True)
596 sp = isp.IPythonInputSplitter(line_input_checker=True)
590
597
591 def test_incremental(self):
598 def test_incremental(self):
592 sp = self.sp
599 sp = self.sp
593 sp.push('%%cellm line2\n')
600 sp.push('%%cellm line2\n')
594 nt.assert_true(sp.push_accepts_more()) #1
601 nt.assert_true(sp.push_accepts_more()) #1
595 sp.push('\n')
602 sp.push('\n')
596 # In this case, a blank line should end the cell magic
603 # In this case, a blank line should end the cell magic
597 nt.assert_false(sp.push_accepts_more()) #2
604 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,639 +1,644 b''
1 """Base classes to manage a Client's interaction with a running kernel"""
1 """Base classes to manage a Client's interaction with a running kernel"""
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 absolute_import
6 from __future__ import absolute_import
7
7
8 import atexit
8 import atexit
9 import errno
9 import errno
10 from threading import Thread
10 from threading import Thread
11 import time
11 import time
12
12
13 import zmq
13 import zmq
14 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
14 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
15 # during garbage collection of threads at exit:
15 # during garbage collection of threads at exit:
16 from zmq import ZMQError
16 from zmq import ZMQError
17 from zmq.eventloop import ioloop, zmqstream
17 from zmq.eventloop import ioloop, zmqstream
18
18
19 from IPython.core.release import kernel_protocol_version_info
19 from IPython.core.release import kernel_protocol_version_info
20
20
21 from .channelsabc import (
21 from .channelsabc import (
22 ShellChannelABC, IOPubChannelABC,
22 ShellChannelABC, IOPubChannelABC,
23 HBChannelABC, StdInChannelABC,
23 HBChannelABC, StdInChannelABC,
24 )
24 )
25 from IPython.utils.py3compat import string_types, iteritems
25 from IPython.utils.py3compat import string_types, iteritems
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Constants and exceptions
28 # Constants and exceptions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 major_protocol_version = kernel_protocol_version_info[0]
31 major_protocol_version = kernel_protocol_version_info[0]
32
32
33 class InvalidPortNumber(Exception):
33 class InvalidPortNumber(Exception):
34 pass
34 pass
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 # Utility functions
37 # Utility functions
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40 # some utilities to validate message structure, these might get moved elsewhere
40 # some utilities to validate message structure, these might get moved elsewhere
41 # if they prove to have more generic utility
41 # if they prove to have more generic utility
42
42
43 def validate_string_list(lst):
43 def validate_string_list(lst):
44 """Validate that the input is a list of strings.
44 """Validate that the input is a list of strings.
45
45
46 Raises ValueError if not."""
46 Raises ValueError if not."""
47 if not isinstance(lst, list):
47 if not isinstance(lst, list):
48 raise ValueError('input %r must be a list' % lst)
48 raise ValueError('input %r must be a list' % lst)
49 for x in lst:
49 for x in lst:
50 if not isinstance(x, string_types):
50 if not isinstance(x, string_types):
51 raise ValueError('element %r in list must be a string' % x)
51 raise ValueError('element %r in list must be a string' % x)
52
52
53
53
54 def validate_string_dict(dct):
54 def validate_string_dict(dct):
55 """Validate that the input is a dict with string keys and values.
55 """Validate that the input is a dict with string keys and values.
56
56
57 Raises ValueError if not."""
57 Raises ValueError if not."""
58 for k,v in iteritems(dct):
58 for k,v in iteritems(dct):
59 if not isinstance(k, string_types):
59 if not isinstance(k, string_types):
60 raise ValueError('key %r in dict must be a string' % k)
60 raise ValueError('key %r in dict must be a string' % k)
61 if not isinstance(v, string_types):
61 if not isinstance(v, string_types):
62 raise ValueError('value %r in dict must be a string' % v)
62 raise ValueError('value %r in dict must be a string' % v)
63
63
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # ZMQ Socket Channel classes
66 # ZMQ Socket Channel classes
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 class ZMQSocketChannel(Thread):
69 class ZMQSocketChannel(Thread):
70 """The base class for the channels that use ZMQ sockets."""
70 """The base class for the channels that use ZMQ sockets."""
71 context = None
71 context = None
72 session = None
72 session = None
73 socket = None
73 socket = None
74 ioloop = None
74 ioloop = None
75 stream = None
75 stream = None
76 _address = None
76 _address = None
77 _exiting = False
77 _exiting = False
78 proxy_methods = []
78 proxy_methods = []
79
79
80 def __init__(self, context, session, address):
80 def __init__(self, context, session, address):
81 """Create a channel.
81 """Create a channel.
82
82
83 Parameters
83 Parameters
84 ----------
84 ----------
85 context : :class:`zmq.Context`
85 context : :class:`zmq.Context`
86 The ZMQ context to use.
86 The ZMQ context to use.
87 session : :class:`session.Session`
87 session : :class:`session.Session`
88 The session to use.
88 The session to use.
89 address : zmq url
89 address : zmq url
90 Standard (ip, port) tuple that the kernel is listening on.
90 Standard (ip, port) tuple that the kernel is listening on.
91 """
91 """
92 super(ZMQSocketChannel, self).__init__()
92 super(ZMQSocketChannel, self).__init__()
93 self.daemon = True
93 self.daemon = True
94
94
95 self.context = context
95 self.context = context
96 self.session = session
96 self.session = session
97 if isinstance(address, tuple):
97 if isinstance(address, tuple):
98 if address[1] == 0:
98 if address[1] == 0:
99 message = 'The port number for a channel cannot be 0.'
99 message = 'The port number for a channel cannot be 0.'
100 raise InvalidPortNumber(message)
100 raise InvalidPortNumber(message)
101 address = "tcp://%s:%i" % address
101 address = "tcp://%s:%i" % address
102 self._address = address
102 self._address = address
103 atexit.register(self._notice_exit)
103 atexit.register(self._notice_exit)
104
104
105 def _notice_exit(self):
105 def _notice_exit(self):
106 self._exiting = True
106 self._exiting = True
107
107
108 def _run_loop(self):
108 def _run_loop(self):
109 """Run my loop, ignoring EINTR events in the poller"""
109 """Run my loop, ignoring EINTR events in the poller"""
110 while True:
110 while True:
111 try:
111 try:
112 self.ioloop.start()
112 self.ioloop.start()
113 except ZMQError as e:
113 except ZMQError as e:
114 if e.errno == errno.EINTR:
114 if e.errno == errno.EINTR:
115 continue
115 continue
116 else:
116 else:
117 raise
117 raise
118 except Exception:
118 except Exception:
119 if self._exiting:
119 if self._exiting:
120 break
120 break
121 else:
121 else:
122 raise
122 raise
123 else:
123 else:
124 break
124 break
125
125
126 def stop(self):
126 def stop(self):
127 """Stop the channel's event loop and join its thread.
127 """Stop the channel's event loop and join its thread.
128
128
129 This calls :meth:`~threading.Thread.join` and returns when the thread
129 This calls :meth:`~threading.Thread.join` and returns when the thread
130 terminates. :class:`RuntimeError` will be raised if
130 terminates. :class:`RuntimeError` will be raised if
131 :meth:`~threading.Thread.start` is called again.
131 :meth:`~threading.Thread.start` is called again.
132 """
132 """
133 if self.ioloop is not None:
133 if self.ioloop is not None:
134 self.ioloop.stop()
134 self.ioloop.stop()
135 self.join()
135 self.join()
136 self.close()
136 self.close()
137
137
138 def close(self):
138 def close(self):
139 if self.ioloop is not None:
139 if self.ioloop is not None:
140 try:
140 try:
141 self.ioloop.close(all_fds=True)
141 self.ioloop.close(all_fds=True)
142 except Exception:
142 except Exception:
143 pass
143 pass
144 if self.socket is not None:
144 if self.socket is not None:
145 try:
145 try:
146 self.socket.close(linger=0)
146 self.socket.close(linger=0)
147 except Exception:
147 except Exception:
148 pass
148 pass
149 self.socket = None
149 self.socket = None
150
150
151 @property
151 @property
152 def address(self):
152 def address(self):
153 """Get the channel's address as a zmq url string.
153 """Get the channel's address as a zmq url string.
154
154
155 These URLS have the form: 'tcp://127.0.0.1:5555'.
155 These URLS have the form: 'tcp://127.0.0.1:5555'.
156 """
156 """
157 return self._address
157 return self._address
158
158
159 def _queue_send(self, msg):
159 def _queue_send(self, msg):
160 """Queue a message to be sent from the IOLoop's thread.
160 """Queue a message to be sent from the IOLoop's thread.
161
161
162 Parameters
162 Parameters
163 ----------
163 ----------
164 msg : message to send
164 msg : message to send
165
165
166 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
166 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
167 thread control of the action.
167 thread control of the action.
168 """
168 """
169 def thread_send():
169 def thread_send():
170 self.session.send(self.stream, msg)
170 self.session.send(self.stream, msg)
171 self.ioloop.add_callback(thread_send)
171 self.ioloop.add_callback(thread_send)
172
172
173 def _handle_recv(self, msg):
173 def _handle_recv(self, msg):
174 """Callback for stream.on_recv.
174 """Callback for stream.on_recv.
175
175
176 Unpacks message, and calls handlers with it.
176 Unpacks message, and calls handlers with it.
177 """
177 """
178 ident,smsg = self.session.feed_identities(msg)
178 ident,smsg = self.session.feed_identities(msg)
179 msg = self.session.unserialize(smsg)
179 msg = self.session.unserialize(smsg)
180 self.call_handlers(msg)
180 self.call_handlers(msg)
181
181
182
182
183
183
184 class ShellChannel(ZMQSocketChannel):
184 class ShellChannel(ZMQSocketChannel):
185 """The shell channel for issuing request/replies to the kernel."""
185 """The shell channel for issuing request/replies to the kernel."""
186
186
187 command_queue = None
187 command_queue = None
188 # flag for whether execute requests should be allowed to call raw_input:
188 # flag for whether execute requests should be allowed to call raw_input:
189 allow_stdin = True
189 allow_stdin = True
190 proxy_methods = [
190 proxy_methods = [
191 'execute',
191 'execute',
192 'complete',
192 'complete',
193 'inspect',
193 'inspect',
194 'history',
194 'history',
195 'kernel_info',
195 'kernel_info',
196 'shutdown',
196 'shutdown',
197 'is_complete',
197 ]
198 ]
198
199
199 def __init__(self, context, session, address):
200 def __init__(self, context, session, address):
200 super(ShellChannel, self).__init__(context, session, address)
201 super(ShellChannel, self).__init__(context, session, address)
201 self.ioloop = ioloop.IOLoop()
202 self.ioloop = ioloop.IOLoop()
202
203
203 def run(self):
204 def run(self):
204 """The thread's main activity. Call start() instead."""
205 """The thread's main activity. Call start() instead."""
205 self.socket = self.context.socket(zmq.DEALER)
206 self.socket = self.context.socket(zmq.DEALER)
206 self.socket.linger = 1000
207 self.socket.linger = 1000
207 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
208 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
208 self.socket.connect(self.address)
209 self.socket.connect(self.address)
209 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
210 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
210 self.stream.on_recv(self._handle_recv)
211 self.stream.on_recv(self._handle_recv)
211 self._run_loop()
212 self._run_loop()
212
213
213 def call_handlers(self, msg):
214 def call_handlers(self, msg):
214 """This method is called in the ioloop thread when a message arrives.
215 """This method is called in the ioloop thread when a message arrives.
215
216
216 Subclasses should override this method to handle incoming messages.
217 Subclasses should override this method to handle incoming messages.
217 It is important to remember that this method is called in the thread
218 It is important to remember that this method is called in the thread
218 so that some logic must be done to ensure that the application level
219 so that some logic must be done to ensure that the application level
219 handlers are called in the application thread.
220 handlers are called in the application thread.
220 """
221 """
221 raise NotImplementedError('call_handlers must be defined in a subclass.')
222 raise NotImplementedError('call_handlers must be defined in a subclass.')
222
223
223 def execute(self, code, silent=False, store_history=True,
224 def execute(self, code, silent=False, store_history=True,
224 user_expressions=None, allow_stdin=None):
225 user_expressions=None, allow_stdin=None):
225 """Execute code in the kernel.
226 """Execute code in the kernel.
226
227
227 Parameters
228 Parameters
228 ----------
229 ----------
229 code : str
230 code : str
230 A string of Python code.
231 A string of Python code.
231
232
232 silent : bool, optional (default False)
233 silent : bool, optional (default False)
233 If set, the kernel will execute the code as quietly possible, and
234 If set, the kernel will execute the code as quietly possible, and
234 will force store_history to be False.
235 will force store_history to be False.
235
236
236 store_history : bool, optional (default True)
237 store_history : bool, optional (default True)
237 If set, the kernel will store command history. This is forced
238 If set, the kernel will store command history. This is forced
238 to be False if silent is True.
239 to be False if silent is True.
239
240
240 user_expressions : dict, optional
241 user_expressions : dict, optional
241 A dict mapping names to expressions to be evaluated in the user's
242 A dict mapping names to expressions to be evaluated in the user's
242 dict. The expression values are returned as strings formatted using
243 dict. The expression values are returned as strings formatted using
243 :func:`repr`.
244 :func:`repr`.
244
245
245 allow_stdin : bool, optional (default self.allow_stdin)
246 allow_stdin : bool, optional (default self.allow_stdin)
246 Flag for whether the kernel can send stdin requests to frontends.
247 Flag for whether the kernel can send stdin requests to frontends.
247
248
248 Some frontends (e.g. the Notebook) do not support stdin requests.
249 Some frontends (e.g. the Notebook) do not support stdin requests.
249 If raw_input is called from code executed from such a frontend, a
250 If raw_input is called from code executed from such a frontend, a
250 StdinNotImplementedError will be raised.
251 StdinNotImplementedError will be raised.
251
252
252 Returns
253 Returns
253 -------
254 -------
254 The msg_id of the message sent.
255 The msg_id of the message sent.
255 """
256 """
256 if user_expressions is None:
257 if user_expressions is None:
257 user_expressions = {}
258 user_expressions = {}
258 if allow_stdin is None:
259 if allow_stdin is None:
259 allow_stdin = self.allow_stdin
260 allow_stdin = self.allow_stdin
260
261
261
262
262 # Don't waste network traffic if inputs are invalid
263 # Don't waste network traffic if inputs are invalid
263 if not isinstance(code, string_types):
264 if not isinstance(code, string_types):
264 raise ValueError('code %r must be a string' % code)
265 raise ValueError('code %r must be a string' % code)
265 validate_string_dict(user_expressions)
266 validate_string_dict(user_expressions)
266
267
267 # Create class for content/msg creation. Related to, but possibly
268 # Create class for content/msg creation. Related to, but possibly
268 # not in Session.
269 # not in Session.
269 content = dict(code=code, silent=silent, store_history=store_history,
270 content = dict(code=code, silent=silent, store_history=store_history,
270 user_expressions=user_expressions,
271 user_expressions=user_expressions,
271 allow_stdin=allow_stdin,
272 allow_stdin=allow_stdin,
272 )
273 )
273 msg = self.session.msg('execute_request', content)
274 msg = self.session.msg('execute_request', content)
274 self._queue_send(msg)
275 self._queue_send(msg)
275 return msg['header']['msg_id']
276 return msg['header']['msg_id']
276
277
277 def complete(self, code, cursor_pos=None):
278 def complete(self, code, cursor_pos=None):
278 """Tab complete text in the kernel's namespace.
279 """Tab complete text in the kernel's namespace.
279
280
280 Parameters
281 Parameters
281 ----------
282 ----------
282 code : str
283 code : str
283 The context in which completion is requested.
284 The context in which completion is requested.
284 Can be anything between a variable name and an entire cell.
285 Can be anything between a variable name and an entire cell.
285 cursor_pos : int, optional
286 cursor_pos : int, optional
286 The position of the cursor in the block of code where the completion was requested.
287 The position of the cursor in the block of code where the completion was requested.
287 Default: ``len(code)``
288 Default: ``len(code)``
288
289
289 Returns
290 Returns
290 -------
291 -------
291 The msg_id of the message sent.
292 The msg_id of the message sent.
292 """
293 """
293 if cursor_pos is None:
294 if cursor_pos is None:
294 cursor_pos = len(code)
295 cursor_pos = len(code)
295 content = dict(code=code, cursor_pos=cursor_pos)
296 content = dict(code=code, cursor_pos=cursor_pos)
296 msg = self.session.msg('complete_request', content)
297 msg = self.session.msg('complete_request', content)
297 self._queue_send(msg)
298 self._queue_send(msg)
298 return msg['header']['msg_id']
299 return msg['header']['msg_id']
299
300
300 def inspect(self, code, cursor_pos=None, detail_level=0):
301 def inspect(self, code, cursor_pos=None, detail_level=0):
301 """Get metadata information about an object in the kernel's namespace.
302 """Get metadata information about an object in the kernel's namespace.
302
303
303 It is up to the kernel to determine the appropriate object to inspect.
304 It is up to the kernel to determine the appropriate object to inspect.
304
305
305 Parameters
306 Parameters
306 ----------
307 ----------
307 code : str
308 code : str
308 The context in which info is requested.
309 The context in which info is requested.
309 Can be anything between a variable name and an entire cell.
310 Can be anything between a variable name and an entire cell.
310 cursor_pos : int, optional
311 cursor_pos : int, optional
311 The position of the cursor in the block of code where the info was requested.
312 The position of the cursor in the block of code where the info was requested.
312 Default: ``len(code)``
313 Default: ``len(code)``
313 detail_level : int, optional
314 detail_level : int, optional
314 The level of detail for the introspection (0-2)
315 The level of detail for the introspection (0-2)
315
316
316 Returns
317 Returns
317 -------
318 -------
318 The msg_id of the message sent.
319 The msg_id of the message sent.
319 """
320 """
320 if cursor_pos is None:
321 if cursor_pos is None:
321 cursor_pos = len(code)
322 cursor_pos = len(code)
322 content = dict(code=code, cursor_pos=cursor_pos,
323 content = dict(code=code, cursor_pos=cursor_pos,
323 detail_level=detail_level,
324 detail_level=detail_level,
324 )
325 )
325 msg = self.session.msg('inspect_request', content)
326 msg = self.session.msg('inspect_request', content)
326 self._queue_send(msg)
327 self._queue_send(msg)
327 return msg['header']['msg_id']
328 return msg['header']['msg_id']
328
329
329 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
330 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
330 """Get entries from the kernel's history list.
331 """Get entries from the kernel's history list.
331
332
332 Parameters
333 Parameters
333 ----------
334 ----------
334 raw : bool
335 raw : bool
335 If True, return the raw input.
336 If True, return the raw input.
336 output : bool
337 output : bool
337 If True, then return the output as well.
338 If True, then return the output as well.
338 hist_access_type : str
339 hist_access_type : str
339 'range' (fill in session, start and stop params), 'tail' (fill in n)
340 'range' (fill in session, start and stop params), 'tail' (fill in n)
340 or 'search' (fill in pattern param).
341 or 'search' (fill in pattern param).
341
342
342 session : int
343 session : int
343 For a range request, the session from which to get lines. Session
344 For a range request, the session from which to get lines. Session
344 numbers are positive integers; negative ones count back from the
345 numbers are positive integers; negative ones count back from the
345 current session.
346 current session.
346 start : int
347 start : int
347 The first line number of a history range.
348 The first line number of a history range.
348 stop : int
349 stop : int
349 The final (excluded) line number of a history range.
350 The final (excluded) line number of a history range.
350
351
351 n : int
352 n : int
352 The number of lines of history to get for a tail request.
353 The number of lines of history to get for a tail request.
353
354
354 pattern : str
355 pattern : str
355 The glob-syntax pattern for a search request.
356 The glob-syntax pattern for a search request.
356
357
357 Returns
358 Returns
358 -------
359 -------
359 The msg_id of the message sent.
360 The msg_id of the message sent.
360 """
361 """
361 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
362 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
362 **kwargs)
363 **kwargs)
363 msg = self.session.msg('history_request', content)
364 msg = self.session.msg('history_request', content)
364 self._queue_send(msg)
365 self._queue_send(msg)
365 return msg['header']['msg_id']
366 return msg['header']['msg_id']
366
367
367 def kernel_info(self):
368 def kernel_info(self):
368 """Request kernel info."""
369 """Request kernel info."""
369 msg = self.session.msg('kernel_info_request')
370 msg = self.session.msg('kernel_info_request')
370 self._queue_send(msg)
371 self._queue_send(msg)
371 return msg['header']['msg_id']
372 return msg['header']['msg_id']
372
373
373 def _handle_kernel_info_reply(self, msg):
374 def _handle_kernel_info_reply(self, msg):
374 """handle kernel info reply
375 """handle kernel info reply
375
376
376 sets protocol adaptation version
377 sets protocol adaptation version
377 """
378 """
378 adapt_version = int(msg['content']['protocol_version'].split('.')[0])
379 adapt_version = int(msg['content']['protocol_version'].split('.')[0])
379 if adapt_version != major_protocol_version:
380 if adapt_version != major_protocol_version:
380 self.session.adapt_version = adapt_version
381 self.session.adapt_version = adapt_version
381
382
382 def shutdown(self, restart=False):
383 def shutdown(self, restart=False):
383 """Request an immediate kernel shutdown.
384 """Request an immediate kernel shutdown.
384
385
385 Upon receipt of the (empty) reply, client code can safely assume that
386 Upon receipt of the (empty) reply, client code can safely assume that
386 the kernel has shut down and it's safe to forcefully terminate it if
387 the kernel has shut down and it's safe to forcefully terminate it if
387 it's still alive.
388 it's still alive.
388
389
389 The kernel will send the reply via a function registered with Python's
390 The kernel will send the reply via a function registered with Python's
390 atexit module, ensuring it's truly done as the kernel is done with all
391 atexit module, ensuring it's truly done as the kernel is done with all
391 normal operation.
392 normal operation.
392 """
393 """
393 # Send quit message to kernel. Once we implement kernel-side setattr,
394 # Send quit message to kernel. Once we implement kernel-side setattr,
394 # this should probably be done that way, but for now this will do.
395 # this should probably be done that way, but for now this will do.
395 msg = self.session.msg('shutdown_request', {'restart':restart})
396 msg = self.session.msg('shutdown_request', {'restart':restart})
396 self._queue_send(msg)
397 self._queue_send(msg)
397 return msg['header']['msg_id']
398 return msg['header']['msg_id']
398
399
400 def is_complete(self, code):
401 msg = self.session.msg('is_complete_request', {'code': code})
402 self._queue_send(msg)
403 return msg['header']['msg_id']
399
404
400
405
401 class IOPubChannel(ZMQSocketChannel):
406 class IOPubChannel(ZMQSocketChannel):
402 """The iopub channel which listens for messages that the kernel publishes.
407 """The iopub channel which listens for messages that the kernel publishes.
403
408
404 This channel is where all output is published to frontends.
409 This channel is where all output is published to frontends.
405 """
410 """
406
411
407 def __init__(self, context, session, address):
412 def __init__(self, context, session, address):
408 super(IOPubChannel, self).__init__(context, session, address)
413 super(IOPubChannel, self).__init__(context, session, address)
409 self.ioloop = ioloop.IOLoop()
414 self.ioloop = ioloop.IOLoop()
410
415
411 def run(self):
416 def run(self):
412 """The thread's main activity. Call start() instead."""
417 """The thread's main activity. Call start() instead."""
413 self.socket = self.context.socket(zmq.SUB)
418 self.socket = self.context.socket(zmq.SUB)
414 self.socket.linger = 1000
419 self.socket.linger = 1000
415 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
420 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
416 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
421 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
417 self.socket.connect(self.address)
422 self.socket.connect(self.address)
418 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
423 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
419 self.stream.on_recv(self._handle_recv)
424 self.stream.on_recv(self._handle_recv)
420 self._run_loop()
425 self._run_loop()
421
426
422 def call_handlers(self, msg):
427 def call_handlers(self, msg):
423 """This method is called in the ioloop thread when a message arrives.
428 """This method is called in the ioloop thread when a message arrives.
424
429
425 Subclasses should override this method to handle incoming messages.
430 Subclasses should override this method to handle incoming messages.
426 It is important to remember that this method is called in the thread
431 It is important to remember that this method is called in the thread
427 so that some logic must be done to ensure that the application leve
432 so that some logic must be done to ensure that the application leve
428 handlers are called in the application thread.
433 handlers are called in the application thread.
429 """
434 """
430 raise NotImplementedError('call_handlers must be defined in a subclass.')
435 raise NotImplementedError('call_handlers must be defined in a subclass.')
431
436
432 def flush(self, timeout=1.0):
437 def flush(self, timeout=1.0):
433 """Immediately processes all pending messages on the iopub channel.
438 """Immediately processes all pending messages on the iopub channel.
434
439
435 Callers should use this method to ensure that :meth:`call_handlers`
440 Callers should use this method to ensure that :meth:`call_handlers`
436 has been called for all messages that have been received on the
441 has been called for all messages that have been received on the
437 0MQ SUB socket of this channel.
442 0MQ SUB socket of this channel.
438
443
439 This method is thread safe.
444 This method is thread safe.
440
445
441 Parameters
446 Parameters
442 ----------
447 ----------
443 timeout : float, optional
448 timeout : float, optional
444 The maximum amount of time to spend flushing, in seconds. The
449 The maximum amount of time to spend flushing, in seconds. The
445 default is one second.
450 default is one second.
446 """
451 """
447 # We do the IOLoop callback process twice to ensure that the IOLoop
452 # We do the IOLoop callback process twice to ensure that the IOLoop
448 # gets to perform at least one full poll.
453 # gets to perform at least one full poll.
449 stop_time = time.time() + timeout
454 stop_time = time.time() + timeout
450 for i in range(2):
455 for i in range(2):
451 self._flushed = False
456 self._flushed = False
452 self.ioloop.add_callback(self._flush)
457 self.ioloop.add_callback(self._flush)
453 while not self._flushed and time.time() < stop_time:
458 while not self._flushed and time.time() < stop_time:
454 time.sleep(0.01)
459 time.sleep(0.01)
455
460
456 def _flush(self):
461 def _flush(self):
457 """Callback for :method:`self.flush`."""
462 """Callback for :method:`self.flush`."""
458 self.stream.flush()
463 self.stream.flush()
459 self._flushed = True
464 self._flushed = True
460
465
461
466
462 class StdInChannel(ZMQSocketChannel):
467 class StdInChannel(ZMQSocketChannel):
463 """The stdin channel to handle raw_input requests that the kernel makes."""
468 """The stdin channel to handle raw_input requests that the kernel makes."""
464
469
465 msg_queue = None
470 msg_queue = None
466 proxy_methods = ['input']
471 proxy_methods = ['input']
467
472
468 def __init__(self, context, session, address):
473 def __init__(self, context, session, address):
469 super(StdInChannel, self).__init__(context, session, address)
474 super(StdInChannel, self).__init__(context, session, address)
470 self.ioloop = ioloop.IOLoop()
475 self.ioloop = ioloop.IOLoop()
471
476
472 def run(self):
477 def run(self):
473 """The thread's main activity. Call start() instead."""
478 """The thread's main activity. Call start() instead."""
474 self.socket = self.context.socket(zmq.DEALER)
479 self.socket = self.context.socket(zmq.DEALER)
475 self.socket.linger = 1000
480 self.socket.linger = 1000
476 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
481 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
477 self.socket.connect(self.address)
482 self.socket.connect(self.address)
478 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
483 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
479 self.stream.on_recv(self._handle_recv)
484 self.stream.on_recv(self._handle_recv)
480 self._run_loop()
485 self._run_loop()
481
486
482 def call_handlers(self, msg):
487 def call_handlers(self, msg):
483 """This method is called in the ioloop thread when a message arrives.
488 """This method is called in the ioloop thread when a message arrives.
484
489
485 Subclasses should override this method to handle incoming messages.
490 Subclasses should override this method to handle incoming messages.
486 It is important to remember that this method is called in the thread
491 It is important to remember that this method is called in the thread
487 so that some logic must be done to ensure that the application leve
492 so that some logic must be done to ensure that the application leve
488 handlers are called in the application thread.
493 handlers are called in the application thread.
489 """
494 """
490 raise NotImplementedError('call_handlers must be defined in a subclass.')
495 raise NotImplementedError('call_handlers must be defined in a subclass.')
491
496
492 def input(self, string):
497 def input(self, string):
493 """Send a string of raw input to the kernel."""
498 """Send a string of raw input to the kernel."""
494 content = dict(value=string)
499 content = dict(value=string)
495 msg = self.session.msg('input_reply', content)
500 msg = self.session.msg('input_reply', content)
496 self._queue_send(msg)
501 self._queue_send(msg)
497
502
498
503
499 class HBChannel(ZMQSocketChannel):
504 class HBChannel(ZMQSocketChannel):
500 """The heartbeat channel which monitors the kernel heartbeat.
505 """The heartbeat channel which monitors the kernel heartbeat.
501
506
502 Note that the heartbeat channel is paused by default. As long as you start
507 Note that the heartbeat channel is paused by default. As long as you start
503 this channel, the kernel manager will ensure that it is paused and un-paused
508 this channel, the kernel manager will ensure that it is paused and un-paused
504 as appropriate.
509 as appropriate.
505 """
510 """
506
511
507 time_to_dead = 3.0
512 time_to_dead = 3.0
508 socket = None
513 socket = None
509 poller = None
514 poller = None
510 _running = None
515 _running = None
511 _pause = None
516 _pause = None
512 _beating = None
517 _beating = None
513
518
514 def __init__(self, context, session, address):
519 def __init__(self, context, session, address):
515 super(HBChannel, self).__init__(context, session, address)
520 super(HBChannel, self).__init__(context, session, address)
516 self._running = False
521 self._running = False
517 self._pause =True
522 self._pause =True
518 self.poller = zmq.Poller()
523 self.poller = zmq.Poller()
519
524
520 def _create_socket(self):
525 def _create_socket(self):
521 if self.socket is not None:
526 if self.socket is not None:
522 # close previous socket, before opening a new one
527 # close previous socket, before opening a new one
523 self.poller.unregister(self.socket)
528 self.poller.unregister(self.socket)
524 self.socket.close()
529 self.socket.close()
525 self.socket = self.context.socket(zmq.REQ)
530 self.socket = self.context.socket(zmq.REQ)
526 self.socket.linger = 1000
531 self.socket.linger = 1000
527 self.socket.connect(self.address)
532 self.socket.connect(self.address)
528
533
529 self.poller.register(self.socket, zmq.POLLIN)
534 self.poller.register(self.socket, zmq.POLLIN)
530
535
531 def _poll(self, start_time):
536 def _poll(self, start_time):
532 """poll for heartbeat replies until we reach self.time_to_dead.
537 """poll for heartbeat replies until we reach self.time_to_dead.
533
538
534 Ignores interrupts, and returns the result of poll(), which
539 Ignores interrupts, and returns the result of poll(), which
535 will be an empty list if no messages arrived before the timeout,
540 will be an empty list if no messages arrived before the timeout,
536 or the event tuple if there is a message to receive.
541 or the event tuple if there is a message to receive.
537 """
542 """
538
543
539 until_dead = self.time_to_dead - (time.time() - start_time)
544 until_dead = self.time_to_dead - (time.time() - start_time)
540 # ensure poll at least once
545 # ensure poll at least once
541 until_dead = max(until_dead, 1e-3)
546 until_dead = max(until_dead, 1e-3)
542 events = []
547 events = []
543 while True:
548 while True:
544 try:
549 try:
545 events = self.poller.poll(1000 * until_dead)
550 events = self.poller.poll(1000 * until_dead)
546 except ZMQError as e:
551 except ZMQError as e:
547 if e.errno == errno.EINTR:
552 if e.errno == errno.EINTR:
548 # ignore interrupts during heartbeat
553 # ignore interrupts during heartbeat
549 # this may never actually happen
554 # this may never actually happen
550 until_dead = self.time_to_dead - (time.time() - start_time)
555 until_dead = self.time_to_dead - (time.time() - start_time)
551 until_dead = max(until_dead, 1e-3)
556 until_dead = max(until_dead, 1e-3)
552 pass
557 pass
553 else:
558 else:
554 raise
559 raise
555 except Exception:
560 except Exception:
556 if self._exiting:
561 if self._exiting:
557 break
562 break
558 else:
563 else:
559 raise
564 raise
560 else:
565 else:
561 break
566 break
562 return events
567 return events
563
568
564 def run(self):
569 def run(self):
565 """The thread's main activity. Call start() instead."""
570 """The thread's main activity. Call start() instead."""
566 self._create_socket()
571 self._create_socket()
567 self._running = True
572 self._running = True
568 self._beating = True
573 self._beating = True
569
574
570 while self._running:
575 while self._running:
571 if self._pause:
576 if self._pause:
572 # just sleep, and skip the rest of the loop
577 # just sleep, and skip the rest of the loop
573 time.sleep(self.time_to_dead)
578 time.sleep(self.time_to_dead)
574 continue
579 continue
575
580
576 since_last_heartbeat = 0.0
581 since_last_heartbeat = 0.0
577 # io.rprint('Ping from HB channel') # dbg
582 # io.rprint('Ping from HB channel') # dbg
578 # no need to catch EFSM here, because the previous event was
583 # no need to catch EFSM here, because the previous event was
579 # either a recv or connect, which cannot be followed by EFSM
584 # either a recv or connect, which cannot be followed by EFSM
580 self.socket.send(b'ping')
585 self.socket.send(b'ping')
581 request_time = time.time()
586 request_time = time.time()
582 ready = self._poll(request_time)
587 ready = self._poll(request_time)
583 if ready:
588 if ready:
584 self._beating = True
589 self._beating = True
585 # the poll above guarantees we have something to recv
590 # the poll above guarantees we have something to recv
586 self.socket.recv()
591 self.socket.recv()
587 # sleep the remainder of the cycle
592 # sleep the remainder of the cycle
588 remainder = self.time_to_dead - (time.time() - request_time)
593 remainder = self.time_to_dead - (time.time() - request_time)
589 if remainder > 0:
594 if remainder > 0:
590 time.sleep(remainder)
595 time.sleep(remainder)
591 continue
596 continue
592 else:
597 else:
593 # nothing was received within the time limit, signal heart failure
598 # nothing was received within the time limit, signal heart failure
594 self._beating = False
599 self._beating = False
595 since_last_heartbeat = time.time() - request_time
600 since_last_heartbeat = time.time() - request_time
596 self.call_handlers(since_last_heartbeat)
601 self.call_handlers(since_last_heartbeat)
597 # and close/reopen the socket, because the REQ/REP cycle has been broken
602 # and close/reopen the socket, because the REQ/REP cycle has been broken
598 self._create_socket()
603 self._create_socket()
599 continue
604 continue
600
605
601 def pause(self):
606 def pause(self):
602 """Pause the heartbeat."""
607 """Pause the heartbeat."""
603 self._pause = True
608 self._pause = True
604
609
605 def unpause(self):
610 def unpause(self):
606 """Unpause the heartbeat."""
611 """Unpause the heartbeat."""
607 self._pause = False
612 self._pause = False
608
613
609 def is_beating(self):
614 def is_beating(self):
610 """Is the heartbeat running and responsive (and not paused)."""
615 """Is the heartbeat running and responsive (and not paused)."""
611 if self.is_alive() and not self._pause and self._beating:
616 if self.is_alive() and not self._pause and self._beating:
612 return True
617 return True
613 else:
618 else:
614 return False
619 return False
615
620
616 def stop(self):
621 def stop(self):
617 """Stop the channel's event loop and join its thread."""
622 """Stop the channel's event loop and join its thread."""
618 self._running = False
623 self._running = False
619 super(HBChannel, self).stop()
624 super(HBChannel, self).stop()
620
625
621 def call_handlers(self, since_last_heartbeat):
626 def call_handlers(self, since_last_heartbeat):
622 """This method is called in the ioloop thread when a message arrives.
627 """This method is called in the ioloop thread when a message arrives.
623
628
624 Subclasses should override this method to handle incoming messages.
629 Subclasses should override this method to handle incoming messages.
625 It is important to remember that this method is called in the thread
630 It is important to remember that this method is called in the thread
626 so that some logic must be done to ensure that the application level
631 so that some logic must be done to ensure that the application level
627 handlers are called in the application thread.
632 handlers are called in the application thread.
628 """
633 """
629 raise NotImplementedError('call_handlers must be defined in a subclass.')
634 raise NotImplementedError('call_handlers must be defined in a subclass.')
630
635
631
636
632 #---------------------------------------------------------------------#-----------------------------------------------------------------------------
637 #---------------------------------------------------------------------#-----------------------------------------------------------------------------
633 # ABC Registration
638 # ABC Registration
634 #-----------------------------------------------------------------------------
639 #-----------------------------------------------------------------------------
635
640
636 ShellChannelABC.register(ShellChannel)
641 ShellChannelABC.register(ShellChannel)
637 IOPubChannelABC.register(IOPubChannel)
642 IOPubChannelABC.register(IOPubChannel)
638 HBChannelABC.register(HBChannel)
643 HBChannelABC.register(HBChannel)
639 StdInChannelABC.register(StdInChannel)
644 StdInChannelABC.register(StdInChannel)
@@ -1,207 +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():
209 with kernel() as kc:
210 # There are more test cases for this in core - here we just check
211 # that the kernel exposes the interface correctly.
212 kc.is_complete('2+2')
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['status'] == 'complete'
215
216 # SyntaxError should mean it's complete
217 kc.is_complete('raise = 2')
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['status'] == 'invalid'
220
221 kc.is_complete('a = [1,\n2,')
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert reply['content']['status'] == 'incomplete'
224 assert reply['content']['indent'] == '' No newline at end of file
@@ -1,409 +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):
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()
173
174
163 # IOPub messages
175 # IOPub messages
164
176
165 class ExecuteInput(Reference):
177 class ExecuteInput(Reference):
166 code = Unicode()
178 code = Unicode()
167 execution_count = Integer()
179 execution_count = Integer()
168
180
169
181
170 Error = ExecuteReplyError
182 Error = ExecuteReplyError
171
183
172
184
173 class Stream(Reference):
185 class Stream(Reference):
174 name = Enum((u'stdout', u'stderr'))
186 name = Enum((u'stdout', u'stderr'))
175 text = Unicode()
187 text = Unicode()
176
188
177
189
178 class DisplayData(MimeBundle):
190 class DisplayData(MimeBundle):
179 pass
191 pass
180
192
181
193
182 class ExecuteResult(MimeBundle):
194 class ExecuteResult(MimeBundle):
183 execution_count = Integer()
195 execution_count = Integer()
184
196
185
197
186 references = {
198 references = {
187 'execute_reply' : ExecuteReply(),
199 'execute_reply' : ExecuteReply(),
188 'inspect_reply' : InspectReply(),
200 'inspect_reply' : InspectReply(),
189 'status' : Status(),
201 'status' : Status(),
190 'complete_reply' : CompleteReply(),
202 'complete_reply' : CompleteReply(),
191 'kernel_info_reply': KernelInfoReply(),
203 'kernel_info_reply': KernelInfoReply(),
204 'is_complete_reply': IsCompleteReply(),
192 'execute_input' : ExecuteInput(),
205 'execute_input' : ExecuteInput(),
193 'execute_result' : ExecuteResult(),
206 'execute_result' : ExecuteResult(),
194 'error' : Error(),
207 'error' : Error(),
195 'stream' : Stream(),
208 'stream' : Stream(),
196 'display_data' : DisplayData(),
209 'display_data' : DisplayData(),
197 'header' : RHeader(),
210 'header' : RHeader(),
198 }
211 }
199 """
212 """
200 Specifications of `content` part of the reply messages.
213 Specifications of `content` part of the reply messages.
201 """
214 """
202
215
203
216
204 def validate_message(msg, msg_type=None, parent=None):
217 def validate_message(msg, msg_type=None, parent=None):
205 """validate a message
218 """validate a message
206
219
207 This is a generator, and must be iterated through to actually
220 This is a generator, and must be iterated through to actually
208 trigger each test.
221 trigger each test.
209
222
210 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
211 are compared with the given values.
224 are compared with the given values.
212 """
225 """
213 RMessage().check(msg)
226 RMessage().check(msg)
214 if msg_type:
227 if msg_type:
215 nt.assert_equal(msg['msg_type'], msg_type)
228 nt.assert_equal(msg['msg_type'], msg_type)
216 if parent:
229 if parent:
217 nt.assert_equal(msg['parent_header']['msg_id'], parent)
230 nt.assert_equal(msg['parent_header']['msg_id'], parent)
218 content = msg['content']
231 content = msg['content']
219 ref = references[msg['msg_type']]
232 ref = references[msg['msg_type']]
220 ref.check(content)
233 ref.check(content)
221
234
222
235
223 #-----------------------------------------------------------------------------
236 #-----------------------------------------------------------------------------
224 # Tests
237 # Tests
225 #-----------------------------------------------------------------------------
238 #-----------------------------------------------------------------------------
226
239
227 # Shell channel
240 # Shell channel
228
241
229 def test_execute():
242 def test_execute():
230 flush_channels()
243 flush_channels()
231
244
232 msg_id = KC.execute(code='x=1')
245 msg_id = KC.execute(code='x=1')
233 reply = KC.get_shell_msg(timeout=TIMEOUT)
246 reply = KC.get_shell_msg(timeout=TIMEOUT)
234 validate_message(reply, 'execute_reply', msg_id)
247 validate_message(reply, 'execute_reply', msg_id)
235
248
236
249
237 def test_execute_silent():
250 def test_execute_silent():
238 flush_channels()
251 flush_channels()
239 msg_id, reply = execute(code='x=1', silent=True)
252 msg_id, reply = execute(code='x=1', silent=True)
240
253
241 # flush status=idle
254 # flush status=idle
242 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
255 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
243 validate_message(status, 'status', msg_id)
256 validate_message(status, 'status', msg_id)
244 nt.assert_equal(status['content']['execution_state'], 'idle')
257 nt.assert_equal(status['content']['execution_state'], 'idle')
245
258
246 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)
247 count = reply['execution_count']
260 count = reply['execution_count']
248
261
249 msg_id, reply = execute(code='x=2', silent=True)
262 msg_id, reply = execute(code='x=2', silent=True)
250
263
251 # flush status=idle
264 # flush status=idle
252 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
265 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
253 validate_message(status, 'status', msg_id)
266 validate_message(status, 'status', msg_id)
254 nt.assert_equal(status['content']['execution_state'], 'idle')
267 nt.assert_equal(status['content']['execution_state'], 'idle')
255
268
256 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)
257 count_2 = reply['execution_count']
270 count_2 = reply['execution_count']
258 nt.assert_equal(count_2, count)
271 nt.assert_equal(count_2, count)
259
272
260
273
261 def test_execute_error():
274 def test_execute_error():
262 flush_channels()
275 flush_channels()
263
276
264 msg_id, reply = execute(code='1/0')
277 msg_id, reply = execute(code='1/0')
265 nt.assert_equal(reply['status'], 'error')
278 nt.assert_equal(reply['status'], 'error')
266 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
279 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
267
280
268 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
281 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
269 validate_message(error, 'error', msg_id)
282 validate_message(error, 'error', msg_id)
270
283
271
284
272 def test_execute_inc():
285 def test_execute_inc():
273 """execute request should increment execution_count"""
286 """execute request should increment execution_count"""
274 flush_channels()
287 flush_channels()
275
288
276 msg_id, reply = execute(code='x=1')
289 msg_id, reply = execute(code='x=1')
277 count = reply['execution_count']
290 count = reply['execution_count']
278
291
279 flush_channels()
292 flush_channels()
280
293
281 msg_id, reply = execute(code='x=2')
294 msg_id, reply = execute(code='x=2')
282 count_2 = reply['execution_count']
295 count_2 = reply['execution_count']
283 nt.assert_equal(count_2, count+1)
296 nt.assert_equal(count_2, count+1)
284
297
285
298
286 def test_user_expressions():
299 def test_user_expressions():
287 flush_channels()
300 flush_channels()
288
301
289 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'))
290 user_expressions = reply['user_expressions']
303 user_expressions = reply['user_expressions']
291 nt.assert_equal(user_expressions, {u'foo': {
304 nt.assert_equal(user_expressions, {u'foo': {
292 u'status': u'ok',
305 u'status': u'ok',
293 u'data': {u'text/plain': u'2'},
306 u'data': {u'text/plain': u'2'},
294 u'metadata': {},
307 u'metadata': {},
295 }})
308 }})
296
309
297
310
298 def test_user_expressions_fail():
311 def test_user_expressions_fail():
299 flush_channels()
312 flush_channels()
300
313
301 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'))
302 user_expressions = reply['user_expressions']
315 user_expressions = reply['user_expressions']
303 foo = user_expressions['foo']
316 foo = user_expressions['foo']
304 nt.assert_equal(foo['status'], 'error')
317 nt.assert_equal(foo['status'], 'error')
305 nt.assert_equal(foo['ename'], 'NameError')
318 nt.assert_equal(foo['ename'], 'NameError')
306
319
307
320
308 def test_oinfo():
321 def test_oinfo():
309 flush_channels()
322 flush_channels()
310
323
311 msg_id = KC.inspect('a')
324 msg_id = KC.inspect('a')
312 reply = KC.get_shell_msg(timeout=TIMEOUT)
325 reply = KC.get_shell_msg(timeout=TIMEOUT)
313 validate_message(reply, 'inspect_reply', msg_id)
326 validate_message(reply, 'inspect_reply', msg_id)
314
327
315
328
316 def test_oinfo_found():
329 def test_oinfo_found():
317 flush_channels()
330 flush_channels()
318
331
319 msg_id, reply = execute(code='a=5')
332 msg_id, reply = execute(code='a=5')
320
333
321 msg_id = KC.inspect('a')
334 msg_id = KC.inspect('a')
322 reply = KC.get_shell_msg(timeout=TIMEOUT)
335 reply = KC.get_shell_msg(timeout=TIMEOUT)
323 validate_message(reply, 'inspect_reply', msg_id)
336 validate_message(reply, 'inspect_reply', msg_id)
324 content = reply['content']
337 content = reply['content']
325 assert content['found']
338 assert content['found']
326 text = content['data']['text/plain']
339 text = content['data']['text/plain']
327 nt.assert_in('Type:', text)
340 nt.assert_in('Type:', text)
328 nt.assert_in('Docstring:', text)
341 nt.assert_in('Docstring:', text)
329
342
330
343
331 def test_oinfo_detail():
344 def test_oinfo_detail():
332 flush_channels()
345 flush_channels()
333
346
334 msg_id, reply = execute(code='ip=get_ipython()')
347 msg_id, reply = execute(code='ip=get_ipython()')
335
348
336 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)
337 reply = KC.get_shell_msg(timeout=TIMEOUT)
350 reply = KC.get_shell_msg(timeout=TIMEOUT)
338 validate_message(reply, 'inspect_reply', msg_id)
351 validate_message(reply, 'inspect_reply', msg_id)
339 content = reply['content']
352 content = reply['content']
340 assert content['found']
353 assert content['found']
341 text = content['data']['text/plain']
354 text = content['data']['text/plain']
342 nt.assert_in('Definition:', text)
355 nt.assert_in('Definition:', text)
343 nt.assert_in('Source:', text)
356 nt.assert_in('Source:', text)
344
357
345
358
346 def test_oinfo_not_found():
359 def test_oinfo_not_found():
347 flush_channels()
360 flush_channels()
348
361
349 msg_id = KC.inspect('dne')
362 msg_id = KC.inspect('dne')
350 reply = KC.get_shell_msg(timeout=TIMEOUT)
363 reply = KC.get_shell_msg(timeout=TIMEOUT)
351 validate_message(reply, 'inspect_reply', msg_id)
364 validate_message(reply, 'inspect_reply', msg_id)
352 content = reply['content']
365 content = reply['content']
353 nt.assert_false(content['found'])
366 nt.assert_false(content['found'])
354
367
355
368
356 def test_complete():
369 def test_complete():
357 flush_channels()
370 flush_channels()
358
371
359 msg_id, reply = execute(code="alpha = albert = 5")
372 msg_id, reply = execute(code="alpha = albert = 5")
360
373
361 msg_id = KC.complete('al', 2)
374 msg_id = KC.complete('al', 2)
362 reply = KC.get_shell_msg(timeout=TIMEOUT)
375 reply = KC.get_shell_msg(timeout=TIMEOUT)
363 validate_message(reply, 'complete_reply', msg_id)
376 validate_message(reply, 'complete_reply', msg_id)
364 matches = reply['content']['matches']
377 matches = reply['content']['matches']
365 for name in ('alpha', 'albert'):
378 for name in ('alpha', 'albert'):
366 nt.assert_in(name, matches)
379 nt.assert_in(name, matches)
367
380
368
381
369 def test_kernel_info_request():
382 def test_kernel_info_request():
370 flush_channels()
383 flush_channels()
371
384
372 msg_id = KC.kernel_info()
385 msg_id = KC.kernel_info()
373 reply = KC.get_shell_msg(timeout=TIMEOUT)
386 reply = KC.get_shell_msg(timeout=TIMEOUT)
374 validate_message(reply, 'kernel_info_reply', msg_id)
387 validate_message(reply, 'kernel_info_reply', msg_id)
375
388
376
389
377 def test_single_payload():
390 def test_single_payload():
378 flush_channels()
391 flush_channels()
379 msg_id, reply = execute(code="for i in range(3):\n"+
392 msg_id, reply = execute(code="for i in range(3):\n"+
380 " x=range?\n")
393 " x=range?\n")
381 payload = reply['payload']
394 payload = reply['payload']
382 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"]
383 nt.assert_equal(len(next_input_pls), 1)
396 nt.assert_equal(len(next_input_pls), 1)
384
397
398 def test_is_complete():
399 flush_channels()
400
401 msg_id = KC.is_complete("a = 1")
402 reply = KC.get_shell_msg(timeout=TIMEOUT)
403 validate_message(reply, 'is_complete_reply', msg_id)
385
404
386 # IOPub channel
405 # IOPub channel
387
406
388
407
389 def test_stream():
408 def test_stream():
390 flush_channels()
409 flush_channels()
391
410
392 msg_id, reply = execute("print('hi')")
411 msg_id, reply = execute("print('hi')")
393
412
394 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
413 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
395 validate_message(stdout, 'stream', msg_id)
414 validate_message(stdout, 'stream', msg_id)
396 content = stdout['content']
415 content = stdout['content']
397 nt.assert_equal(content['text'], u'hi\n')
416 nt.assert_equal(content['text'], u'hi\n')
398
417
399
418
400 def test_display_data():
419 def test_display_data():
401 flush_channels()
420 flush_channels()
402
421
403 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)")
404
423
405 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
424 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
406 validate_message(display, 'display_data', parent=msg_id)
425 validate_message(display, 'display_data', parent=msg_id)
407 data = display['content']['data']
426 data = display['content']['data']
408 nt.assert_equal(data['text/plain'], u'1')
427 nt.assert_equal(data['text/plain'], u'1')
409
428
@@ -1,305 +1,312 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 ..comm import CommManager
13 from ..comm import CommManager
14 from .kernelbase import Kernel as KernelBase
14 from .kernelbase import Kernel as KernelBase
15 from .serialize import serialize_object, unpack_apply_message
15 from .serialize import serialize_object, unpack_apply_message
16 from .zmqshell import ZMQInteractiveShell
16 from .zmqshell import ZMQInteractiveShell
17
17
18 class IPythonKernel(KernelBase):
18 class IPythonKernel(KernelBase):
19 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
19 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
20 shell_class = Type(ZMQInteractiveShell)
20 shell_class = Type(ZMQInteractiveShell)
21
21
22 user_module = Any()
22 user_module = Any()
23 def _user_module_changed(self, name, old, new):
23 def _user_module_changed(self, name, old, new):
24 if self.shell is not None:
24 if self.shell is not None:
25 self.shell.user_module = new
25 self.shell.user_module = new
26
26
27 user_ns = Instance(dict, args=None, allow_none=True)
27 user_ns = Instance(dict, args=None, allow_none=True)
28 def _user_ns_changed(self, name, old, new):
28 def _user_ns_changed(self, name, old, new):
29 if self.shell is not None:
29 if self.shell is not None:
30 self.shell.user_ns = new
30 self.shell.user_ns = new
31 self.shell.init_user_ns()
31 self.shell.init_user_ns()
32
32
33 # A reference to the Python builtin 'raw_input' function.
33 # A reference to the Python builtin 'raw_input' function.
34 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
34 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
35 _sys_raw_input = Any()
35 _sys_raw_input = Any()
36 _sys_eval_input = Any()
36 _sys_eval_input = Any()
37
37
38 def __init__(self, **kwargs):
38 def __init__(self, **kwargs):
39 super(IPythonKernel, self).__init__(**kwargs)
39 super(IPythonKernel, self).__init__(**kwargs)
40
40
41 # Initialize the InteractiveShell subclass
41 # Initialize the InteractiveShell subclass
42 self.shell = self.shell_class.instance(parent=self,
42 self.shell = self.shell_class.instance(parent=self,
43 profile_dir = self.profile_dir,
43 profile_dir = self.profile_dir,
44 user_module = self.user_module,
44 user_module = self.user_module,
45 user_ns = self.user_ns,
45 user_ns = self.user_ns,
46 kernel = self,
46 kernel = self,
47 )
47 )
48 self.shell.displayhook.session = self.session
48 self.shell.displayhook.session = self.session
49 self.shell.displayhook.pub_socket = self.iopub_socket
49 self.shell.displayhook.pub_socket = self.iopub_socket
50 self.shell.displayhook.topic = self._topic('execute_result')
50 self.shell.displayhook.topic = self._topic('execute_result')
51 self.shell.display_pub.session = self.session
51 self.shell.display_pub.session = self.session
52 self.shell.display_pub.pub_socket = self.iopub_socket
52 self.shell.display_pub.pub_socket = self.iopub_socket
53 self.shell.data_pub.session = self.session
53 self.shell.data_pub.session = self.session
54 self.shell.data_pub.pub_socket = self.iopub_socket
54 self.shell.data_pub.pub_socket = self.iopub_socket
55
55
56 # TMP - hack while developing
56 # TMP - hack while developing
57 self.shell._reply_content = None
57 self.shell._reply_content = None
58
58
59 self.comm_manager = CommManager(shell=self.shell, parent=self,
59 self.comm_manager = CommManager(shell=self.shell, parent=self,
60 kernel=self)
60 kernel=self)
61 self.shell.configurables.append(self.comm_manager)
61 self.shell.configurables.append(self.comm_manager)
62 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
62 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
63 for msg_type in comm_msg_types:
63 for msg_type in comm_msg_types:
64 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
64 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
65
65
66 # Kernel info fields
66 # Kernel info fields
67 implementation = 'ipython'
67 implementation = 'ipython'
68 implementation_version = release.version
68 implementation_version = release.version
69 language = 'python'
69 language = 'python'
70 language_version = sys.version.split()[0]
70 language_version = sys.version.split()[0]
71 @property
71 @property
72 def banner(self):
72 def banner(self):
73 return self.shell.banner
73 return self.shell.banner
74
74
75 def start(self):
75 def start(self):
76 self.shell.exit_now = False
76 self.shell.exit_now = False
77 super(IPythonKernel, self).start()
77 super(IPythonKernel, self).start()
78
78
79 def set_parent(self, ident, parent):
79 def set_parent(self, ident, parent):
80 """Overridden from parent to tell the display hook and output streams
80 """Overridden from parent to tell the display hook and output streams
81 about the parent message.
81 about the parent message.
82 """
82 """
83 super(IPythonKernel, self).set_parent(ident, parent)
83 super(IPythonKernel, self).set_parent(ident, parent)
84 self.shell.set_parent(parent)
84 self.shell.set_parent(parent)
85
85
86 def _forward_input(self, allow_stdin=False):
86 def _forward_input(self, allow_stdin=False):
87 """Forward raw_input and getpass to the current frontend.
87 """Forward raw_input and getpass to the current frontend.
88
88
89 via input_request
89 via input_request
90 """
90 """
91 self._allow_stdin = allow_stdin
91 self._allow_stdin = allow_stdin
92
92
93 if PY3:
93 if PY3:
94 self._sys_raw_input = builtin_mod.input
94 self._sys_raw_input = builtin_mod.input
95 builtin_mod.input = self.raw_input
95 builtin_mod.input = self.raw_input
96 else:
96 else:
97 self._sys_raw_input = builtin_mod.raw_input
97 self._sys_raw_input = builtin_mod.raw_input
98 self._sys_eval_input = builtin_mod.input
98 self._sys_eval_input = builtin_mod.input
99 builtin_mod.raw_input = self.raw_input
99 builtin_mod.raw_input = self.raw_input
100 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
100 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
101 self._save_getpass = getpass.getpass
101 self._save_getpass = getpass.getpass
102 getpass.getpass = self.getpass
102 getpass.getpass = self.getpass
103
103
104 def _restore_input(self):
104 def _restore_input(self):
105 """Restore raw_input, getpass"""
105 """Restore raw_input, getpass"""
106 if PY3:
106 if PY3:
107 builtin_mod.input = self._sys_raw_input
107 builtin_mod.input = self._sys_raw_input
108 else:
108 else:
109 builtin_mod.raw_input = self._sys_raw_input
109 builtin_mod.raw_input = self._sys_raw_input
110 builtin_mod.input = self._sys_eval_input
110 builtin_mod.input = self._sys_eval_input
111
111
112 getpass.getpass = self._save_getpass
112 getpass.getpass = self._save_getpass
113
113
114 @property
114 @property
115 def execution_count(self):
115 def execution_count(self):
116 return self.shell.execution_count
116 return self.shell.execution_count
117
117
118 @execution_count.setter
118 @execution_count.setter
119 def execution_count(self, value):
119 def execution_count(self, value):
120 # Ignore the incrememnting done by KernelBase, in favour of our shell's
120 # Ignore the incrememnting done by KernelBase, in favour of our shell's
121 # execution counter.
121 # execution counter.
122 pass
122 pass
123
123
124 def do_execute(self, code, silent, store_history=True,
124 def do_execute(self, code, silent, store_history=True,
125 user_expressions=None, allow_stdin=False):
125 user_expressions=None, allow_stdin=False):
126 shell = self.shell # we'll need this a lot here
126 shell = self.shell # we'll need this a lot here
127
127
128 self._forward_input(allow_stdin)
128 self._forward_input(allow_stdin)
129
129
130 reply_content = {}
130 reply_content = {}
131 # FIXME: the shell calls the exception handler itself.
131 # FIXME: the shell calls the exception handler itself.
132 shell._reply_content = None
132 shell._reply_content = None
133 try:
133 try:
134 shell.run_cell(code, store_history=store_history, silent=silent)
134 shell.run_cell(code, store_history=store_history, silent=silent)
135 except:
135 except:
136 status = u'error'
136 status = u'error'
137 # FIXME: this code right now isn't being used yet by default,
137 # FIXME: this code right now isn't being used yet by default,
138 # because the run_cell() call above directly fires off exception
138 # because the run_cell() call above directly fires off exception
139 # reporting. This code, therefore, is only active in the scenario
139 # reporting. This code, therefore, is only active in the scenario
140 # where runlines itself has an unhandled exception. We need to
140 # where runlines itself has an unhandled exception. We need to
141 # uniformize this, for all exception construction to come from a
141 # uniformize this, for all exception construction to come from a
142 # single location in the codbase.
142 # single location in the codbase.
143 etype, evalue, tb = sys.exc_info()
143 etype, evalue, tb = sys.exc_info()
144 tb_list = traceback.format_exception(etype, evalue, tb)
144 tb_list = traceback.format_exception(etype, evalue, tb)
145 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
145 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
146 else:
146 else:
147 status = u'ok'
147 status = u'ok'
148 finally:
148 finally:
149 self._restore_input()
149 self._restore_input()
150
150
151 reply_content[u'status'] = status
151 reply_content[u'status'] = status
152
152
153 # Return the execution counter so clients can display prompts
153 # Return the execution counter so clients can display prompts
154 reply_content['execution_count'] = shell.execution_count - 1
154 reply_content['execution_count'] = shell.execution_count - 1
155
155
156 # FIXME - fish exception info out of shell, possibly left there by
156 # FIXME - fish exception info out of shell, possibly left there by
157 # runlines. We'll need to clean up this logic later.
157 # runlines. We'll need to clean up this logic later.
158 if shell._reply_content is not None:
158 if shell._reply_content is not None:
159 reply_content.update(shell._reply_content)
159 reply_content.update(shell._reply_content)
160 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
160 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
161 reply_content['engine_info'] = e_info
161 reply_content['engine_info'] = e_info
162 # reset after use
162 # reset after use
163 shell._reply_content = None
163 shell._reply_content = None
164
164
165 if 'traceback' in reply_content:
165 if 'traceback' in reply_content:
166 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
166 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
167
167
168
168
169 # At this point, we can tell whether the main code execution succeeded
169 # At this point, we can tell whether the main code execution succeeded
170 # or not. If it did, we proceed to evaluate user_expressions
170 # or not. If it did, we proceed to evaluate user_expressions
171 if reply_content['status'] == 'ok':
171 if reply_content['status'] == 'ok':
172 reply_content[u'user_expressions'] = \
172 reply_content[u'user_expressions'] = \
173 shell.user_expressions(user_expressions or {})
173 shell.user_expressions(user_expressions or {})
174 else:
174 else:
175 # If there was an error, don't even try to compute expressions
175 # If there was an error, don't even try to compute expressions
176 reply_content[u'user_expressions'] = {}
176 reply_content[u'user_expressions'] = {}
177
177
178 # Payloads should be retrieved regardless of outcome, so we can both
178 # Payloads should be retrieved regardless of outcome, so we can both
179 # recover partial output (that could have been generated early in a
179 # recover partial output (that could have been generated early in a
180 # block, before an error) and clear the payload system always.
180 # block, before an error) and clear the payload system always.
181 reply_content[u'payload'] = shell.payload_manager.read_payload()
181 reply_content[u'payload'] = shell.payload_manager.read_payload()
182 # Be agressive about clearing the payload because we don't want
182 # Be agressive about clearing the payload because we don't want
183 # it to sit in memory until the next execute_request comes in.
183 # it to sit in memory until the next execute_request comes in.
184 shell.payload_manager.clear_payload()
184 shell.payload_manager.clear_payload()
185
185
186 return reply_content
186 return reply_content
187
187
188 def do_complete(self, code, cursor_pos):
188 def do_complete(self, code, cursor_pos):
189 txt, matches = self.shell.complete('', code, cursor_pos)
189 txt, matches = self.shell.complete('', code, cursor_pos)
190 return {'matches' : matches,
190 return {'matches' : matches,
191 'cursor_end' : cursor_pos,
191 'cursor_end' : cursor_pos,
192 'cursor_start' : cursor_pos - len(txt),
192 'cursor_start' : cursor_pos - len(txt),
193 'metadata' : {},
193 'metadata' : {},
194 'status' : 'ok'}
194 'status' : 'ok'}
195
195
196 def do_inspect(self, code, cursor_pos, detail_level=0):
196 def do_inspect(self, code, cursor_pos, detail_level=0):
197 name = token_at_cursor(code, cursor_pos)
197 name = token_at_cursor(code, cursor_pos)
198 info = self.shell.object_inspect(name)
198 info = self.shell.object_inspect(name)
199
199
200 reply_content = {'status' : 'ok'}
200 reply_content = {'status' : 'ok'}
201 reply_content['data'] = data = {}
201 reply_content['data'] = data = {}
202 reply_content['metadata'] = {}
202 reply_content['metadata'] = {}
203 reply_content['found'] = info['found']
203 reply_content['found'] = info['found']
204 if info['found']:
204 if info['found']:
205 info_text = self.shell.object_inspect_text(
205 info_text = self.shell.object_inspect_text(
206 name,
206 name,
207 detail_level=detail_level,
207 detail_level=detail_level,
208 )
208 )
209 data['text/plain'] = info_text
209 data['text/plain'] = info_text
210
210
211 return reply_content
211 return reply_content
212
212
213 def do_history(self, hist_access_type, output, raw, session=None, start=None,
213 def do_history(self, hist_access_type, output, raw, session=None, start=None,
214 stop=None, n=None, pattern=None, unique=False):
214 stop=None, n=None, pattern=None, unique=False):
215 if hist_access_type == 'tail':
215 if hist_access_type == 'tail':
216 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
216 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
217 include_latest=True)
217 include_latest=True)
218
218
219 elif hist_access_type == 'range':
219 elif hist_access_type == 'range':
220 hist = self.shell.history_manager.get_range(session, start, stop,
220 hist = self.shell.history_manager.get_range(session, start, stop,
221 raw=raw, output=output)
221 raw=raw, output=output)
222
222
223 elif hist_access_type == 'search':
223 elif hist_access_type == 'search':
224 hist = self.shell.history_manager.search(
224 hist = self.shell.history_manager.search(
225 pattern, raw=raw, output=output, n=n, unique=unique)
225 pattern, raw=raw, output=output, n=n, unique=unique)
226 else:
226 else:
227 hist = []
227 hist = []
228
228
229 return {'history' : list(hist)}
229 return {'history' : list(hist)}
230
230
231 def do_shutdown(self, restart):
231 def do_shutdown(self, restart):
232 self.shell.exit_now = True
232 self.shell.exit_now = True
233 return dict(status='ok', restart=restart)
233 return dict(status='ok', restart=restart)
234
234
235 def do_is_complete(self, code):
236 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
237 r = {'status': status}
238 if status == 'incomplete':
239 r['indent'] = ' ' * indent_spaces
240 return r
241
235 def do_apply(self, content, bufs, msg_id, reply_metadata):
242 def do_apply(self, content, bufs, msg_id, reply_metadata):
236 shell = self.shell
243 shell = self.shell
237 try:
244 try:
238 working = shell.user_ns
245 working = shell.user_ns
239
246
240 prefix = "_"+str(msg_id).replace("-","")+"_"
247 prefix = "_"+str(msg_id).replace("-","")+"_"
241
248
242 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
249 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
243
250
244 fname = getattr(f, '__name__', 'f')
251 fname = getattr(f, '__name__', 'f')
245
252
246 fname = prefix+"f"
253 fname = prefix+"f"
247 argname = prefix+"args"
254 argname = prefix+"args"
248 kwargname = prefix+"kwargs"
255 kwargname = prefix+"kwargs"
249 resultname = prefix+"result"
256 resultname = prefix+"result"
250
257
251 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
258 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
252 # print ns
259 # print ns
253 working.update(ns)
260 working.update(ns)
254 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
261 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
255 try:
262 try:
256 exec(code, shell.user_global_ns, shell.user_ns)
263 exec(code, shell.user_global_ns, shell.user_ns)
257 result = working.get(resultname)
264 result = working.get(resultname)
258 finally:
265 finally:
259 for key in ns:
266 for key in ns:
260 working.pop(key)
267 working.pop(key)
261
268
262 result_buf = serialize_object(result,
269 result_buf = serialize_object(result,
263 buffer_threshold=self.session.buffer_threshold,
270 buffer_threshold=self.session.buffer_threshold,
264 item_threshold=self.session.item_threshold,
271 item_threshold=self.session.item_threshold,
265 )
272 )
266
273
267 except:
274 except:
268 # invoke IPython traceback formatting
275 # invoke IPython traceback formatting
269 shell.showtraceback()
276 shell.showtraceback()
270 # FIXME - fish exception info out of shell, possibly left there by
277 # FIXME - fish exception info out of shell, possibly left there by
271 # run_code. We'll need to clean up this logic later.
278 # run_code. We'll need to clean up this logic later.
272 reply_content = {}
279 reply_content = {}
273 if shell._reply_content is not None:
280 if shell._reply_content is not None:
274 reply_content.update(shell._reply_content)
281 reply_content.update(shell._reply_content)
275 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
282 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
276 reply_content['engine_info'] = e_info
283 reply_content['engine_info'] = e_info
277 # reset after use
284 # reset after use
278 shell._reply_content = None
285 shell._reply_content = None
279
286
280 self.send_response(self.iopub_socket, u'error', reply_content,
287 self.send_response(self.iopub_socket, u'error', reply_content,
281 ident=self._topic('error'))
288 ident=self._topic('error'))
282 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
289 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
283 result_buf = []
290 result_buf = []
284
291
285 if reply_content['ename'] == 'UnmetDependency':
292 if reply_content['ename'] == 'UnmetDependency':
286 reply_metadata['dependencies_met'] = False
293 reply_metadata['dependencies_met'] = False
287 else:
294 else:
288 reply_content = {'status' : 'ok'}
295 reply_content = {'status' : 'ok'}
289
296
290 return reply_content, result_buf
297 return reply_content, result_buf
291
298
292 def do_clear(self):
299 def do_clear(self):
293 self.shell.reset(False)
300 self.shell.reset(False)
294 return dict(status='ok')
301 return dict(status='ok')
295
302
296
303
297 # This exists only for backwards compatibility - use IPythonKernel instead
304 # This exists only for backwards compatibility - use IPythonKernel instead
298
305
299 @undoc
306 @undoc
300 class Kernel(IPythonKernel):
307 class Kernel(IPythonKernel):
301 def __init__(self, *args, **kwargs):
308 def __init__(self, *args, **kwargs):
302 import warnings
309 import warnings
303 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
310 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
304 DeprecationWarning)
311 DeprecationWarning)
305 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
312 super(Kernel, self).__init__(*args, **kwargs)
@@ -1,676 +1,692 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 SingletonConfigurable
22 from IPython.config.configurable import SingletonConfigurable
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(SingletonConfigurable):
35 class Kernel(SingletonConfigurable):
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',
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
483 def is_complete_request(self, stream, ident, parent):
484 content = parent['content']
485 code = content['code']
486
487 reply_content = self.do_is_complete(code)
488 reply_content = json_clean(reply_content)
489 reply_msg = self.session.send(stream, 'is_complete_reply',
490 reply_content, parent, ident)
491 self.log.debug("%s", reply_msg)
492
493 def do_is_complete(self, code):
494 """Override in subclasses to find completions.
495 """
496 return {'status' : 'unknown',
497 }
482
498
483 #---------------------------------------------------------------------------
499 #---------------------------------------------------------------------------
484 # Engine methods
500 # Engine methods
485 #---------------------------------------------------------------------------
501 #---------------------------------------------------------------------------
486
502
487 def apply_request(self, stream, ident, parent):
503 def apply_request(self, stream, ident, parent):
488 try:
504 try:
489 content = parent[u'content']
505 content = parent[u'content']
490 bufs = parent[u'buffers']
506 bufs = parent[u'buffers']
491 msg_id = parent['header']['msg_id']
507 msg_id = parent['header']['msg_id']
492 except:
508 except:
493 self.log.error("Got bad msg: %s", parent, exc_info=True)
509 self.log.error("Got bad msg: %s", parent, exc_info=True)
494 return
510 return
495
511
496 md = self._make_metadata(parent['metadata'])
512 md = self._make_metadata(parent['metadata'])
497
513
498 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)
499
515
500 # put 'ok'/'error' status in header, for scheduler introspection:
516 # put 'ok'/'error' status in header, for scheduler introspection:
501 md['status'] = reply_content['status']
517 md['status'] = reply_content['status']
502
518
503 # flush i/o
519 # flush i/o
504 sys.stdout.flush()
520 sys.stdout.flush()
505 sys.stderr.flush()
521 sys.stderr.flush()
506
522
507 self.session.send(stream, u'apply_reply', reply_content,
523 self.session.send(stream, u'apply_reply', reply_content,
508 parent=parent, ident=ident,buffers=result_buf, metadata=md)
524 parent=parent, ident=ident,buffers=result_buf, metadata=md)
509
525
510 def do_apply(self, content, bufs, msg_id, reply_metadata):
526 def do_apply(self, content, bufs, msg_id, reply_metadata):
511 """Override in subclasses to support the IPython parallel framework.
527 """Override in subclasses to support the IPython parallel framework.
512 """
528 """
513 raise NotImplementedError
529 raise NotImplementedError
514
530
515 #---------------------------------------------------------------------------
531 #---------------------------------------------------------------------------
516 # Control messages
532 # Control messages
517 #---------------------------------------------------------------------------
533 #---------------------------------------------------------------------------
518
534
519 def abort_request(self, stream, ident, parent):
535 def abort_request(self, stream, ident, parent):
520 """abort a specific msg by id"""
536 """abort a specific msg by id"""
521 msg_ids = parent['content'].get('msg_ids', None)
537 msg_ids = parent['content'].get('msg_ids', None)
522 if isinstance(msg_ids, string_types):
538 if isinstance(msg_ids, string_types):
523 msg_ids = [msg_ids]
539 msg_ids = [msg_ids]
524 if not msg_ids:
540 if not msg_ids:
525 self._abort_queues()
541 self._abort_queues()
526 for mid in msg_ids:
542 for mid in msg_ids:
527 self.aborted.add(str(mid))
543 self.aborted.add(str(mid))
528
544
529 content = dict(status='ok')
545 content = dict(status='ok')
530 reply_msg = self.session.send(stream, 'abort_reply', content=content,
546 reply_msg = self.session.send(stream, 'abort_reply', content=content,
531 parent=parent, ident=ident)
547 parent=parent, ident=ident)
532 self.log.debug("%s", reply_msg)
548 self.log.debug("%s", reply_msg)
533
549
534 def clear_request(self, stream, idents, parent):
550 def clear_request(self, stream, idents, parent):
535 """Clear our namespace."""
551 """Clear our namespace."""
536 content = self.do_clear()
552 content = self.do_clear()
537 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
553 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
538 content = content)
554 content = content)
539
555
540 def do_clear(self):
556 def do_clear(self):
541 """Override in subclasses to clear the namespace
557 """Override in subclasses to clear the namespace
542
558
543 This is only required for IPython.parallel.
559 This is only required for IPython.parallel.
544 """
560 """
545 raise NotImplementedError
561 raise NotImplementedError
546
562
547 #---------------------------------------------------------------------------
563 #---------------------------------------------------------------------------
548 # Protected interface
564 # Protected interface
549 #---------------------------------------------------------------------------
565 #---------------------------------------------------------------------------
550
566
551 def _topic(self, topic):
567 def _topic(self, topic):
552 """prefixed topic for IOPub messages"""
568 """prefixed topic for IOPub messages"""
553 if self.int_id >= 0:
569 if self.int_id >= 0:
554 base = "engine.%i" % self.int_id
570 base = "engine.%i" % self.int_id
555 else:
571 else:
556 base = "kernel.%s" % self.ident
572 base = "kernel.%s" % self.ident
557
573
558 return py3compat.cast_bytes("%s.%s" % (base, topic))
574 return py3compat.cast_bytes("%s.%s" % (base, topic))
559
575
560 def _abort_queues(self):
576 def _abort_queues(self):
561 for stream in self.shell_streams:
577 for stream in self.shell_streams:
562 if stream:
578 if stream:
563 self._abort_queue(stream)
579 self._abort_queue(stream)
564
580
565 def _abort_queue(self, stream):
581 def _abort_queue(self, stream):
566 poller = zmq.Poller()
582 poller = zmq.Poller()
567 poller.register(stream.socket, zmq.POLLIN)
583 poller.register(stream.socket, zmq.POLLIN)
568 while True:
584 while True:
569 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
585 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
570 if msg is None:
586 if msg is None:
571 return
587 return
572
588
573 self.log.info("Aborting:")
589 self.log.info("Aborting:")
574 self.log.info("%s", msg)
590 self.log.info("%s", msg)
575 msg_type = msg['header']['msg_type']
591 msg_type = msg['header']['msg_type']
576 reply_type = msg_type.split('_')[0] + '_reply'
592 reply_type = msg_type.split('_')[0] + '_reply'
577
593
578 status = {'status' : 'aborted'}
594 status = {'status' : 'aborted'}
579 md = {'engine' : self.ident}
595 md = {'engine' : self.ident}
580 md.update(status)
596 md.update(status)
581 reply_msg = self.session.send(stream, reply_type, metadata=md,
597 reply_msg = self.session.send(stream, reply_type, metadata=md,
582 content=status, parent=msg, ident=idents)
598 content=status, parent=msg, ident=idents)
583 self.log.debug("%s", reply_msg)
599 self.log.debug("%s", reply_msg)
584 # 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
585 # be set shorter for true asynchronous clients.
601 # be set shorter for true asynchronous clients.
586 poller.poll(50)
602 poller.poll(50)
587
603
588
604
589 def _no_raw_input(self):
605 def _no_raw_input(self):
590 """Raise StdinNotImplentedError if active frontend doesn't support
606 """Raise StdinNotImplentedError if active frontend doesn't support
591 stdin."""
607 stdin."""
592 raise StdinNotImplementedError("raw_input was called, but this "
608 raise StdinNotImplementedError("raw_input was called, but this "
593 "frontend does not support stdin.")
609 "frontend does not support stdin.")
594
610
595 def getpass(self, prompt=''):
611 def getpass(self, prompt=''):
596 """Forward getpass to frontends
612 """Forward getpass to frontends
597
613
598 Raises
614 Raises
599 ------
615 ------
600 StdinNotImplentedError if active frontend doesn't support stdin.
616 StdinNotImplentedError if active frontend doesn't support stdin.
601 """
617 """
602 if not self._allow_stdin:
618 if not self._allow_stdin:
603 raise StdinNotImplementedError(
619 raise StdinNotImplementedError(
604 "getpass was called, but this frontend does not support input requests."
620 "getpass was called, but this frontend does not support input requests."
605 )
621 )
606 return self._input_request(prompt,
622 return self._input_request(prompt,
607 self._parent_ident,
623 self._parent_ident,
608 self._parent_header,
624 self._parent_header,
609 password=True,
625 password=True,
610 )
626 )
611
627
612 def raw_input(self, prompt=''):
628 def raw_input(self, prompt=''):
613 """Forward raw_input to frontends
629 """Forward raw_input to frontends
614
630
615 Raises
631 Raises
616 ------
632 ------
617 StdinNotImplentedError if active frontend doesn't support stdin.
633 StdinNotImplentedError if active frontend doesn't support stdin.
618 """
634 """
619 if not self._allow_stdin:
635 if not self._allow_stdin:
620 raise StdinNotImplementedError(
636 raise StdinNotImplementedError(
621 "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."
622 )
638 )
623 return self._input_request(prompt,
639 return self._input_request(prompt,
624 self._parent_ident,
640 self._parent_ident,
625 self._parent_header,
641 self._parent_header,
626 password=False,
642 password=False,
627 )
643 )
628
644
629 def _input_request(self, prompt, ident, parent, password=False):
645 def _input_request(self, prompt, ident, parent, password=False):
630 # Flush output before making the request.
646 # Flush output before making the request.
631 sys.stderr.flush()
647 sys.stderr.flush()
632 sys.stdout.flush()
648 sys.stdout.flush()
633 # flush the stdin socket, to purge stale replies
649 # flush the stdin socket, to purge stale replies
634 while True:
650 while True:
635 try:
651 try:
636 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
652 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
637 except zmq.ZMQError as e:
653 except zmq.ZMQError as e:
638 if e.errno == zmq.EAGAIN:
654 if e.errno == zmq.EAGAIN:
639 break
655 break
640 else:
656 else:
641 raise
657 raise
642
658
643 # Send the input request.
659 # Send the input request.
644 content = json_clean(dict(prompt=prompt, password=password))
660 content = json_clean(dict(prompt=prompt, password=password))
645 self.session.send(self.stdin_socket, u'input_request', content, parent,
661 self.session.send(self.stdin_socket, u'input_request', content, parent,
646 ident=ident)
662 ident=ident)
647
663
648 # Await a response.
664 # Await a response.
649 while True:
665 while True:
650 try:
666 try:
651 ident, reply = self.session.recv(self.stdin_socket, 0)
667 ident, reply = self.session.recv(self.stdin_socket, 0)
652 except Exception:
668 except Exception:
653 self.log.warn("Invalid Message:", exc_info=True)
669 self.log.warn("Invalid Message:", exc_info=True)
654 except KeyboardInterrupt:
670 except KeyboardInterrupt:
655 # re-raise KeyboardInterrupt, to truncate traceback
671 # re-raise KeyboardInterrupt, to truncate traceback
656 raise KeyboardInterrupt
672 raise KeyboardInterrupt
657 else:
673 else:
658 break
674 break
659 try:
675 try:
660 value = py3compat.unicode_to_str(reply['content']['value'])
676 value = py3compat.unicode_to_str(reply['content']['value'])
661 except:
677 except:
662 self.log.error("Bad input_reply: %s", parent)
678 self.log.error("Bad input_reply: %s", parent)
663 value = ''
679 value = ''
664 if value == '\x04':
680 if value == '\x04':
665 # EOF
681 # EOF
666 raise EOFError
682 raise EOFError
667 return value
683 return value
668
684
669 def _at_shutdown(self):
685 def _at_shutdown(self):
670 """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.
671 """
687 """
672 # io.rprint("Kernel at_shutdown") # dbg
688 # io.rprint("Kernel at_shutdown") # dbg
673 if self._shutdown_message is not None:
689 if self._shutdown_message is not None:
674 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'))
675 self.log.debug("%s", self._shutdown_message)
691 self.log.debug("%s", self._shutdown_message)
676 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
692 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
@@ -1,1070 +1,1115 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.0.
12 The current version of the specification is 5.0.
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:
577
578 Code completeness
579 -----------------
580
581 .. versionadded:: 5.0
582
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
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.
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
599 Frontends may have ways to override this, forcing the code to be sent for
600 execution or forcing a continuation prompt.
601
602 Message type: ``is_complete_request``::
603
604 content = {
605 # The code entered so far as a multiline string
606 'code' : str,
607 }
608
609 Message type: ``is_complete_reply``::
610
611 content = {
612 # One of 'complete', 'incomplete', 'invalid', 'unknown'
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,
620 }
576
621
577 Connect
622 Connect
578 -------
623 -------
579
624
580 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
581 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
582 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
583 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.
584
629
585 Message type: ``connect_request``::
630 Message type: ``connect_request``::
586
631
587 content = {
632 content = {
588 }
633 }
589
634
590 Message type: ``connect_reply``::
635 Message type: ``connect_reply``::
591
636
592 content = {
637 content = {
593 '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.
594 'iopub_port' : int, # The port the PUB socket is listening on.
639 'iopub_port' : int, # The port the PUB socket is listening on.
595 '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.
596 'hb_port' : int, # The port the heartbeat socket is listening on.
641 'hb_port' : int, # The port the heartbeat socket is listening on.
597 }
642 }
598
643
599 .. _msging_kernel_info:
644 .. _msging_kernel_info:
600
645
601 Kernel info
646 Kernel info
602 -----------
647 -----------
603
648
604 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
605 make a request of the kernel's information.
650 make a request of the kernel's information.
606 This message can be used to fetch core information of the
651 This message can be used to fetch core information of the
607 kernel, including language (e.g., Python), language version number and
652 kernel, including language (e.g., Python), language version number and
608 IPython version number, and the IPython message spec version number.
653 IPython version number, and the IPython message spec version number.
609
654
610 Message type: ``kernel_info_request``::
655 Message type: ``kernel_info_request``::
611
656
612 content = {
657 content = {
613 }
658 }
614
659
615 Message type: ``kernel_info_reply``::
660 Message type: ``kernel_info_reply``::
616
661
617 content = {
662 content = {
618 # Version of messaging protocol.
663 # Version of messaging protocol.
619 # The first integer indicates major version. It is incremented when
664 # The first integer indicates major version. It is incremented when
620 # there is any backward incompatible change.
665 # there is any backward incompatible change.
621 # The second integer indicates minor version. It is incremented when
666 # The second integer indicates minor version. It is incremented when
622 # there is any backward compatible change.
667 # there is any backward compatible change.
623 'protocol_version': 'X.Y.Z',
668 'protocol_version': 'X.Y.Z',
624
669
625 # The kernel implementation name
670 # The kernel implementation name
626 # (e.g. 'ipython' for the IPython kernel)
671 # (e.g. 'ipython' for the IPython kernel)
627 'implementation': str,
672 'implementation': str,
628
673
629 # Implementation version number.
674 # Implementation version number.
630 # The version number of the kernel's implementation
675 # The version number of the kernel's implementation
631 # (e.g. IPython.__version__ for the IPython kernel)
676 # (e.g. IPython.__version__ for the IPython kernel)
632 'implementation_version': 'X.Y.Z',
677 'implementation_version': 'X.Y.Z',
633
678
634 # Programming language in which kernel is implemented.
679 # Programming language in which kernel is implemented.
635 # Kernel included in IPython returns 'python'.
680 # Kernel included in IPython returns 'python'.
636 'language': str,
681 'language': str,
637
682
638 # Language version number.
683 # Language version number.
639 # 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
640 # included in IPython.
685 # included in IPython.
641 'language_version': 'X.Y.Z',
686 'language_version': 'X.Y.Z',
642
687
643 # A banner of information about the kernel,
688 # A banner of information about the kernel,
644 # which may be desplayed in console environments.
689 # which may be desplayed in console environments.
645 'banner' : str,
690 'banner' : str,
646 }
691 }
647
692
648 .. versionchanged:: 5.0
693 .. versionchanged:: 5.0
649
694
650 Versions changed from lists of integers to strings.
695 Versions changed from lists of integers to strings.
651
696
652 .. versionchanged:: 5.0
697 .. versionchanged:: 5.0
653
698
654 ``ipython_version`` is removed.
699 ``ipython_version`` is removed.
655
700
656 .. versionchanged:: 5.0
701 .. versionchanged:: 5.0
657
702
658 ``implementation``, ``implementation_version``, and ``banner`` keys are added.
703 ``implementation``, ``implementation_version``, and ``banner`` keys are added.
659
704
660 .. _msging_shutdown:
705 .. _msging_shutdown:
661
706
662 Kernel shutdown
707 Kernel shutdown
663 ---------------
708 ---------------
664
709
665 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
666 multiple cases:
711 multiple cases:
667
712
668 - 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
669 control.
714 control.
670 - 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).
671 - 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
672 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
673 losing client-side state like history or inlined figures.
718 losing client-side state like history or inlined figures.
674
719
675 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
676 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
677 completed shutdown safely.
722 completed shutdown safely.
678
723
679 Upon their own shutdown, client applications will typically execute a last
724 Upon their own shutdown, client applications will typically execute a last
680 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
681 avoid leaving stray processes in the user's machine.
726 avoid leaving stray processes in the user's machine.
682
727
683 Message type: ``shutdown_request``::
728 Message type: ``shutdown_request``::
684
729
685 content = {
730 content = {
686 'restart' : bool # whether the shutdown is final, or precedes a restart
731 'restart' : bool # whether the shutdown is final, or precedes a restart
687 }
732 }
688
733
689 Message type: ``shutdown_reply``::
734 Message type: ``shutdown_reply``::
690
735
691 content = {
736 content = {
692 'restart' : bool # whether the shutdown is final, or precedes a restart
737 'restart' : bool # whether the shutdown is final, or precedes a restart
693 }
738 }
694
739
695 .. Note::
740 .. Note::
696
741
697 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
698 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
699 process is unlikely to respond in any useful way to messages.
744 process is unlikely to respond in any useful way to messages.
700
745
701
746
702 Messages on the PUB/SUB socket
747 Messages on the PUB/SUB socket
703 ==============================
748 ==============================
704
749
705 Streams (stdout, stderr, etc)
750 Streams (stdout, stderr, etc)
706 ------------------------------
751 ------------------------------
707
752
708 Message type: ``stream``::
753 Message type: ``stream``::
709
754
710 content = {
755 content = {
711 # The name of the stream is one of 'stdout', 'stderr'
756 # The name of the stream is one of 'stdout', 'stderr'
712 'name' : str,
757 'name' : str,
713
758
714 # The text is an arbitrary string to be written to that stream
759 # The text is an arbitrary string to be written to that stream
715 'text' : str,
760 'text' : str,
716 }
761 }
717
762
718 .. versionchanged:: 5.0
763 .. versionchanged:: 5.0
719
764
720 'data' key renamed to 'text' for conistency with the notebook format.
765 'data' key renamed to 'text' for conistency with the notebook format.
721
766
722 Display Data
767 Display Data
723 ------------
768 ------------
724
769
725 This type of message is used to bring back data that should be displayed (text,
770 This type of message is used to bring back data that should be displayed (text,
726 html, svg, etc.) in the frontends. This data is published to all frontends.
771 html, svg, etc.) in the frontends. This data is published to all frontends.
727 Each message can have multiple representations of the data; it is up to the
772 Each message can have multiple representations of the data; it is up to the
728 frontend to decide which to use and how. A single message should contain all
773 frontend to decide which to use and how. A single message should contain all
729 possible representations of the same information. Each representation should
774 possible representations of the same information. Each representation should
730 be a JSON'able data structure, and should be a valid MIME type.
775 be a JSON'able data structure, and should be a valid MIME type.
731
776
732 Some questions remain about this design:
777 Some questions remain about this design:
733
778
734 * Do we use this message type for execute_result/displayhook? Probably not, because
779 * Do we use this message type for execute_result/displayhook? Probably not, because
735 the displayhook also has to handle the Out prompt display. On the other hand
780 the displayhook also has to handle the Out prompt display. On the other hand
736 we could put that information into the metadata section.
781 we could put that information into the metadata section.
737
782
738 .. _display_data:
783 .. _display_data:
739
784
740 Message type: ``display_data``::
785 Message type: ``display_data``::
741
786
742 content = {
787 content = {
743
788
744 # Who create the data
789 # Who create the data
745 'source' : str,
790 'source' : str,
746
791
747 # The data dict contains key/value pairs, where the keys are MIME
792 # The data dict contains key/value pairs, where the keys are MIME
748 # types and the values are the raw data of the representation in that
793 # types and the values are the raw data of the representation in that
749 # format.
794 # format.
750 'data' : dict,
795 'data' : dict,
751
796
752 # Any metadata that describes the data
797 # Any metadata that describes the data
753 'metadata' : dict
798 'metadata' : dict
754 }
799 }
755
800
756
801
757 The ``metadata`` contains any metadata that describes the output.
802 The ``metadata`` contains any metadata that describes the output.
758 Global keys are assumed to apply to the output as a whole.
803 Global keys are assumed to apply to the output as a whole.
759 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
804 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
760 which are interpreted as applying only to output of that type.
805 which are interpreted as applying only to output of that type.
761 Third parties should put any data they write into a single dict
806 Third parties should put any data they write into a single dict
762 with a reasonably unique name to avoid conflicts.
807 with a reasonably unique name to avoid conflicts.
763
808
764 The only metadata keys currently defined in IPython are the width and height
809 The only metadata keys currently defined in IPython are the width and height
765 of images::
810 of images::
766
811
767 metadata = {
812 metadata = {
768 'image/png' : {
813 'image/png' : {
769 'width': 640,
814 'width': 640,
770 'height': 480
815 'height': 480
771 }
816 }
772 }
817 }
773
818
774
819
775 .. versionchanged:: 5.0
820 .. versionchanged:: 5.0
776
821
777 `application/json` data should be unpacked JSON data,
822 `application/json` data should be unpacked JSON data,
778 not double-serialized as a JSON string.
823 not double-serialized as a JSON string.
779
824
780
825
781 Raw Data Publication
826 Raw Data Publication
782 --------------------
827 --------------------
783
828
784 ``display_data`` lets you publish *representations* of data, such as images and html.
829 ``display_data`` lets you publish *representations* of data, such as images and html.
785 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
830 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
786
831
787 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
832 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
788
833
789 .. sourcecode:: python
834 .. sourcecode:: python
790
835
791 from IPython.kernel.zmq.datapub import publish_data
836 from IPython.kernel.zmq.datapub import publish_data
792 ns = dict(x=my_array)
837 ns = dict(x=my_array)
793 publish_data(ns)
838 publish_data(ns)
794
839
795
840
796 Message type: ``data_pub``::
841 Message type: ``data_pub``::
797
842
798 content = {
843 content = {
799 # the keys of the data dict, after it has been unserialized
844 # the keys of the data dict, after it has been unserialized
800 'keys' : ['a', 'b']
845 'keys' : ['a', 'b']
801 }
846 }
802 # the namespace dict will be serialized in the message buffers,
847 # the namespace dict will be serialized in the message buffers,
803 # which will have a length of at least one
848 # which will have a length of at least one
804 buffers = [b'pdict', ...]
849 buffers = [b'pdict', ...]
805
850
806
851
807 The interpretation of a sequence of data_pub messages for a given parent request should be
852 The interpretation of a sequence of data_pub messages for a given parent request should be
808 to update a single namespace with subsequent results.
853 to update a single namespace with subsequent results.
809
854
810 .. note::
855 .. note::
811
856
812 No frontends directly handle data_pub messages at this time.
857 No frontends directly handle data_pub messages at this time.
813 It is currently only used by the client/engines in :mod:`IPython.parallel`,
858 It is currently only used by the client/engines in :mod:`IPython.parallel`,
814 where engines may publish *data* to the Client,
859 where engines may publish *data* to the Client,
815 of which the Client can then publish *representations* via ``display_data``
860 of which the Client can then publish *representations* via ``display_data``
816 to various frontends.
861 to various frontends.
817
862
818 Code inputs
863 Code inputs
819 -----------
864 -----------
820
865
821 To let all frontends know what code is being executed at any given time, these
866 To let all frontends know what code is being executed at any given time, these
822 messages contain a re-broadcast of the ``code`` portion of an
867 messages contain a re-broadcast of the ``code`` portion of an
823 :ref:`execute_request <execute>`, along with the :ref:`execution_count
868 :ref:`execute_request <execute>`, along with the :ref:`execution_count
824 <execution_counter>`.
869 <execution_counter>`.
825
870
826 Message type: ``execute_input``::
871 Message type: ``execute_input``::
827
872
828 content = {
873 content = {
829 'code' : str, # Source code to be executed, one or more lines
874 'code' : str, # Source code to be executed, one or more lines
830
875
831 # The counter for this execution is also provided so that clients can
876 # The counter for this execution is also provided so that clients can
832 # display it, since IPython automatically creates variables called _iN
877 # display it, since IPython automatically creates variables called _iN
833 # (for input prompt In[N]).
878 # (for input prompt In[N]).
834 'execution_count' : int
879 'execution_count' : int
835 }
880 }
836
881
837 .. versionchanged:: 5.0
882 .. versionchanged:: 5.0
838
883
839 ``pyin`` is renamed to ``execute_input``.
884 ``pyin`` is renamed to ``execute_input``.
840
885
841
886
842 Execution results
887 Execution results
843 -----------------
888 -----------------
844
889
845 Results of an execution are published as an ``execute_result``.
890 Results of an execution are published as an ``execute_result``.
846 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
891 These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key.
847
892
848 Results can have multiple simultaneous formats depending on its
893 Results can have multiple simultaneous formats depending on its
849 configuration. A plain text representation should always be provided
894 configuration. A plain text representation should always be provided
850 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
895 in the ``text/plain`` mime-type. Frontends are free to display any or all of these
851 according to its capabilities.
896 according to its capabilities.
852 Frontends should ignore mime-types they do not understand. The data itself is
897 Frontends should ignore mime-types they do not understand. The data itself is
853 any JSON object and depends on the format. It is often, but not always a string.
898 any JSON object and depends on the format. It is often, but not always a string.
854
899
855 Message type: ``execute_result``::
900 Message type: ``execute_result``::
856
901
857 content = {
902 content = {
858
903
859 # The counter for this execution is also provided so that clients can
904 # The counter for this execution is also provided so that clients can
860 # display it, since IPython automatically creates variables called _N
905 # display it, since IPython automatically creates variables called _N
861 # (for prompt N).
906 # (for prompt N).
862 'execution_count' : int,
907 'execution_count' : int,
863
908
864 # data and metadata are identical to a display_data message.
909 # data and metadata are identical to a display_data message.
865 # the object being displayed is that passed to the display hook,
910 # the object being displayed is that passed to the display hook,
866 # i.e. the *result* of the execution.
911 # i.e. the *result* of the execution.
867 'data' : dict,
912 'data' : dict,
868 'metadata' : dict,
913 'metadata' : dict,
869 }
914 }
870
915
871 Execution errors
916 Execution errors
872 ----------------
917 ----------------
873
918
874 When an error occurs during code execution
919 When an error occurs during code execution
875
920
876 Message type: ``error``::
921 Message type: ``error``::
877
922
878 content = {
923 content = {
879 # Similar content to the execute_reply messages for the 'error' case,
924 # Similar content to the execute_reply messages for the 'error' case,
880 # except the 'status' field is omitted.
925 # except the 'status' field is omitted.
881 }
926 }
882
927
883 .. versionchanged:: 5.0
928 .. versionchanged:: 5.0
884
929
885 ``pyerr`` renamed to ``error``
930 ``pyerr`` renamed to ``error``
886
931
887 Kernel status
932 Kernel status
888 -------------
933 -------------
889
934
890 This message type is used by frontends to monitor the status of the kernel.
935 This message type is used by frontends to monitor the status of the kernel.
891
936
892 Message type: ``status``::
937 Message type: ``status``::
893
938
894 content = {
939 content = {
895 # When the kernel starts to handle a message, it will enter the 'busy'
940 # When the kernel starts to handle a message, it will enter the 'busy'
896 # state and when it finishes, it will enter the 'idle' state.
941 # state and when it finishes, it will enter the 'idle' state.
897 # The kernel will publish state 'starting' exactly once at process startup.
942 # The kernel will publish state 'starting' exactly once at process startup.
898 execution_state : ('busy', 'idle', 'starting')
943 execution_state : ('busy', 'idle', 'starting')
899 }
944 }
900
945
901 .. versionchanged:: 5.0
946 .. versionchanged:: 5.0
902
947
903 Busy and idle messages should be sent before/after handling every message,
948 Busy and idle messages should be sent before/after handling every message,
904 not just execution.
949 not just execution.
905
950
906 Clear output
951 Clear output
907 ------------
952 ------------
908
953
909 This message type is used to clear the output that is visible on the frontend.
954 This message type is used to clear the output that is visible on the frontend.
910
955
911 Message type: ``clear_output``::
956 Message type: ``clear_output``::
912
957
913 content = {
958 content = {
914
959
915 # Wait to clear the output until new output is available. Clears the
960 # Wait to clear the output until new output is available. Clears the
916 # existing output immediately before the new output is displayed.
961 # existing output immediately before the new output is displayed.
917 # Useful for creating simple animations with minimal flickering.
962 # Useful for creating simple animations with minimal flickering.
918 'wait' : bool,
963 'wait' : bool,
919 }
964 }
920
965
921 .. versionchanged:: 4.1
966 .. versionchanged:: 4.1
922
967
923 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
968 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
924 and ``wait`` is added.
969 and ``wait`` is added.
925 The selective clearing keys are ignored in v4 and the default behavior remains the same,
970 The selective clearing keys are ignored in v4 and the default behavior remains the same,
926 so v4 clear_output messages will be safely handled by a v4.1 frontend.
971 so v4 clear_output messages will be safely handled by a v4.1 frontend.
927
972
928
973
929 Messages on the stdin ROUTER/DEALER sockets
974 Messages on the stdin ROUTER/DEALER sockets
930 ===========================================
975 ===========================================
931
976
932 This is a socket where the request/reply pattern goes in the opposite direction:
977 This is a socket where the request/reply pattern goes in the opposite direction:
933 from the kernel to a *single* frontend, and its purpose is to allow
978 from the kernel to a *single* frontend, and its purpose is to allow
934 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
979 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
935 to be fulfilled by the client. The request should be made to the frontend that
980 to be fulfilled by the client. The request should be made to the frontend that
936 made the execution request that prompted ``raw_input`` to be called. For now we
981 made the execution request that prompted ``raw_input`` to be called. For now we
937 will keep these messages as simple as possible, since they only mean to convey
982 will keep these messages as simple as possible, since they only mean to convey
938 the ``raw_input(prompt)`` call.
983 the ``raw_input(prompt)`` call.
939
984
940 Message type: ``input_request``::
985 Message type: ``input_request``::
941
986
942 content = {
987 content = {
943 # the text to show at the prompt
988 # the text to show at the prompt
944 'prompt' : str,
989 'prompt' : str,
945 # Is the request for a password?
990 # Is the request for a password?
946 # If so, the frontend shouldn't echo input.
991 # If so, the frontend shouldn't echo input.
947 'password' : bool
992 'password' : bool
948 }
993 }
949
994
950 Message type: ``input_reply``::
995 Message type: ``input_reply``::
951
996
952 content = { 'value' : str }
997 content = { 'value' : str }
953
998
954
999
955 When ``password`` is True, the frontend should not echo the input as it is entered.
1000 When ``password`` is True, the frontend should not echo the input as it is entered.
956
1001
957 .. versionchanged:: 5.0
1002 .. versionchanged:: 5.0
958
1003
959 ``password`` key added.
1004 ``password`` key added.
960
1005
961 .. note::
1006 .. note::
962
1007
963 The stdin socket of the client is required to have the same zmq IDENTITY
1008 The stdin socket of the client is required to have the same zmq IDENTITY
964 as the client's shell socket.
1009 as the client's shell socket.
965 Because of this, the ``input_request`` must be sent with the same IDENTITY
1010 Because of this, the ``input_request`` must be sent with the same IDENTITY
966 routing prefix as the ``execute_reply`` in order for the frontend to receive
1011 routing prefix as the ``execute_reply`` in order for the frontend to receive
967 the message.
1012 the message.
968
1013
969 .. note::
1014 .. note::
970
1015
971 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
1016 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
972 practice the kernel should behave like an interactive program. When a
1017 practice the kernel should behave like an interactive program. When a
973 program is opened on the console, the keyboard effectively takes over the
1018 program is opened on the console, the keyboard effectively takes over the
974 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1019 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
975 Since the IPython kernel effectively behaves like a console program (albeit
1020 Since the IPython kernel effectively behaves like a console program (albeit
976 one whose "keyboard" is actually living in a separate process and
1021 one whose "keyboard" is actually living in a separate process and
977 transported over the zmq connection), raw ``stdin`` isn't expected to be
1022 transported over the zmq connection), raw ``stdin`` isn't expected to be
978 available.
1023 available.
979
1024
980 .. _kernel_heartbeat:
1025 .. _kernel_heartbeat:
981
1026
982 Heartbeat for kernels
1027 Heartbeat for kernels
983 =====================
1028 =====================
984
1029
985 Clients send ping messages on a REQ socket, which are echoed right back
1030 Clients send ping messages on a REQ socket, which are echoed right back
986 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
1031 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
987
1032
988
1033
989 Custom Messages
1034 Custom Messages
990 ===============
1035 ===============
991
1036
992 .. versionadded:: 4.1
1037 .. versionadded:: 4.1
993
1038
994 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
1039 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
995 and Kernel-side components, and allow them to communicate with each other.
1040 and Kernel-side components, and allow them to communicate with each other.
996 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
1041 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
997 and can communicate in either direction.
1042 and can communicate in either direction.
998
1043
999 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1044 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1000 and no messages expect a reply.
1045 and no messages expect a reply.
1001 The Kernel listens for these messages on the Shell channel,
1046 The Kernel listens for these messages on the Shell channel,
1002 and the Frontend listens for them on the IOPub channel.
1047 and the Frontend listens for them on the IOPub channel.
1003
1048
1004 Opening a Comm
1049 Opening a Comm
1005 --------------
1050 --------------
1006
1051
1007 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1052 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1008
1053
1009 {
1054 {
1010 'comm_id' : 'u-u-i-d',
1055 'comm_id' : 'u-u-i-d',
1011 'target_name' : 'my_comm',
1056 'target_name' : 'my_comm',
1012 'data' : {}
1057 'data' : {}
1013 }
1058 }
1014
1059
1015 Every Comm has an ID and a target name.
1060 Every Comm has an ID and a target name.
1016 The code handling the message on the receiving side is responsible for maintaining a mapping
1061 The code handling the message on the receiving side is responsible for maintaining a mapping
1017 of target_name keys to constructors.
1062 of target_name keys to constructors.
1018 After a ``comm_open`` message has been sent,
1063 After a ``comm_open`` message has been sent,
1019 there should be a corresponding Comm instance on both sides.
1064 there should be a corresponding Comm instance on both sides.
1020 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1065 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1021
1066
1022 If the ``target_name`` key is not found on the receiving side,
1067 If the ``target_name`` key is not found on the receiving side,
1023 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1068 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1024
1069
1025 Comm Messages
1070 Comm Messages
1026 -------------
1071 -------------
1027
1072
1028 Comm messages are one-way communications to update comm state,
1073 Comm messages are one-way communications to update comm state,
1029 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1074 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1030
1075
1031 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1076 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1032
1077
1033 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1078 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1034
1079
1035 Message type: ``comm_msg``::
1080 Message type: ``comm_msg``::
1036
1081
1037 {
1082 {
1038 'comm_id' : 'u-u-i-d',
1083 'comm_id' : 'u-u-i-d',
1039 'data' : {}
1084 'data' : {}
1040 }
1085 }
1041
1086
1042 Tearing Down Comms
1087 Tearing Down Comms
1043 ------------------
1088 ------------------
1044
1089
1045 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1090 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1046 This is done with a ``comm_close`` message.
1091 This is done with a ``comm_close`` message.
1047
1092
1048 Message type: ``comm_close``::
1093 Message type: ``comm_close``::
1049
1094
1050 {
1095 {
1051 'comm_id' : 'u-u-i-d',
1096 'comm_id' : 'u-u-i-d',
1052 'data' : {}
1097 'data' : {}
1053 }
1098 }
1054
1099
1055 Output Side Effects
1100 Output Side Effects
1056 -------------------
1101 -------------------
1057
1102
1058 Since comm messages can execute arbitrary user code,
1103 Since comm messages can execute arbitrary user code,
1059 handlers should set the parent header and publish status busy / idle,
1104 handlers should set the parent header and publish status busy / idle,
1060 just like an execute request.
1105 just like an execute request.
1061
1106
1062
1107
1063 To Do
1108 To Do
1064 =====
1109 =====
1065
1110
1066 Missing things include:
1111 Missing things include:
1067
1112
1068 * Important: finish thinking through the payload concept and API.
1113 * Important: finish thinking through the payload concept and API.
1069
1114
1070 .. include:: ../links.txt
1115 .. include:: ../links.txt
@@ -1,153 +1,164 b''
1 Making simple Python wrapper kernels
1 Making simple Python wrapper kernels
2 ====================================
2 ====================================
3
3
4 .. versionadded:: 3.0
4 .. versionadded:: 3.0
5
5
6 You can now re-use the kernel machinery in IPython to easily make new kernels.
6 You can now re-use the kernel machinery in IPython to easily make new kernels.
7 This is useful for languages that have Python bindings, such as `Octave
7 This is useful for languages that have Python bindings, such as `Octave
8 <http://www.gnu.org/software/octave/>`_ (via
8 <http://www.gnu.org/software/octave/>`_ (via
9 `Oct2Py <http://blink1073.github.io/oct2py/docs/index.html>`_), or languages
9 `Oct2Py <http://blink1073.github.io/oct2py/docs/index.html>`_), or languages
10 where the REPL can be controlled in a tty using `pexpect <http://pexpect.readthedocs.org/en/latest/>`_,
10 where the REPL can be controlled in a tty using `pexpect <http://pexpect.readthedocs.org/en/latest/>`_,
11 such as bash.
11 such as bash.
12
12
13 .. seealso::
13 .. seealso::
14
14
15 `bash_kernel <https://github.com/takluyver/bash_kernel>`_
15 `bash_kernel <https://github.com/takluyver/bash_kernel>`_
16 A simple kernel for bash, written using this machinery
16 A simple kernel for bash, written using this machinery
17
17
18 Required steps
18 Required steps
19 --------------
19 --------------
20
20
21 Subclass :class:`IPython.kernel.zmq.kernelbase.Kernel`, and implement the
21 Subclass :class:`IPython.kernel.zmq.kernelbase.Kernel`, and implement the
22 following methods and attributes:
22 following methods and attributes:
23
23
24 .. class:: MyKernel
24 .. class:: MyKernel
25
25
26 .. attribute:: implementation
26 .. attribute:: implementation
27 implementation_version
27 implementation_version
28 language
28 language
29 language_version
29 language_version
30 banner
30 banner
31
31
32 Information for :ref:`msging_kernel_info` replies. 'Implementation' refers
32 Information for :ref:`msging_kernel_info` replies. 'Implementation' refers
33 to the kernel (e.g. IPython), and 'language' refers to the language it
33 to the kernel (e.g. IPython), and 'language' refers to the language it
34 interprets (e.g. Python). The 'banner' is displayed to the user in console
34 interprets (e.g. Python). The 'banner' is displayed to the user in console
35 UIs before the first prompt. All of these values are strings.
35 UIs before the first prompt. All of these values are strings.
36
36
37 .. method:: do_execute(code, silent, store_history=True, user_expressions=None, allow_stdin=False)
37 .. method:: do_execute(code, silent, store_history=True, user_expressions=None, allow_stdin=False)
38
38
39 Execute user code.
39 Execute user code.
40
40
41 :param str code: The code to be executed.
41 :param str code: The code to be executed.
42 :param bool silent: Whether to display output.
42 :param bool silent: Whether to display output.
43 :param bool store_history: Whether to record this code in history and
43 :param bool store_history: Whether to record this code in history and
44 increase the execution count. If silent is True, this is implicitly
44 increase the execution count. If silent is True, this is implicitly
45 False.
45 False.
46 :param dict user_expressions: Mapping of names to expressions to evaluate
46 :param dict user_expressions: Mapping of names to expressions to evaluate
47 after the code has run. You can ignore this if you need to.
47 after the code has run. You can ignore this if you need to.
48 :param bool allow_stdin: Whether the frontend can provide input on request
48 :param bool allow_stdin: Whether the frontend can provide input on request
49 (e.g. for Python's :func:`raw_input`).
49 (e.g. for Python's :func:`raw_input`).
50
50
51 Your method should return a dict containing the fields described in
51 Your method should return a dict containing the fields described in
52 :ref:`execution_results`. To display output, it can send messages
52 :ref:`execution_results`. To display output, it can send messages
53 using :meth:`~IPython.kernel.zmq.kernelbase.Kernel.send_response`.
53 using :meth:`~IPython.kernel.zmq.kernelbase.Kernel.send_response`.
54 See :doc:`messaging` for details of the different message types.
54 See :doc:`messaging` for details of the different message types.
55
55
56 To launch your kernel, add this at the end of your module::
56 To launch your kernel, add this at the end of your module::
57
57
58 if __name__ == '__main__':
58 if __name__ == '__main__':
59 from IPython.kernel.zmq.kernelapp import IPKernelApp
59 from IPython.kernel.zmq.kernelapp import IPKernelApp
60 IPKernelApp.launch_instance(kernel_class=MyKernel)
60 IPKernelApp.launch_instance(kernel_class=MyKernel)
61
61
62 Example
62 Example
63 -------
63 -------
64
64
65 ``echokernel.py`` will simply echo any input it's given to stdout::
65 ``echokernel.py`` will simply echo any input it's given to stdout::
66
66
67 from IPython.kernel.zmq.kernelbase import Kernel
67 from IPython.kernel.zmq.kernelbase import Kernel
68
68
69 class EchoKernel(Kernel):
69 class EchoKernel(Kernel):
70 implementation = 'Echo'
70 implementation = 'Echo'
71 implementation_version = '1.0'
71 implementation_version = '1.0'
72 language = 'no-op'
72 language = 'no-op'
73 language_version = '0.1'
73 language_version = '0.1'
74 banner = "Echo kernel - as useful as a parrot"
74 banner = "Echo kernel - as useful as a parrot"
75
75
76 def do_execute(self, code, silent, store_history=True, user_expressions=None,
76 def do_execute(self, code, silent, store_history=True, user_expressions=None,
77 allow_stdin=False):
77 allow_stdin=False):
78 if not silent:
78 if not silent:
79 stream_content = {'name': 'stdout', 'data':code}
79 stream_content = {'name': 'stdout', 'data':code}
80 self.send_response(self.iopub_socket, 'stream', stream_content)
80 self.send_response(self.iopub_socket, 'stream', stream_content)
81
81
82 return {'status': 'ok',
82 return {'status': 'ok',
83 # The base class increments the execution count
83 # The base class increments the execution count
84 'execution_count': self.execution_count,
84 'execution_count': self.execution_count,
85 'payload': [],
85 'payload': [],
86 'user_expressions': {},
86 'user_expressions': {},
87 }
87 }
88
88
89 if __name__ == '__main__':
89 if __name__ == '__main__':
90 from IPython.kernel.zmq.kernelapp import IPKernelApp
90 from IPython.kernel.zmq.kernelapp import IPKernelApp
91 IPKernelApp.launch_instance(kernel_class=EchoKernel)
91 IPKernelApp.launch_instance(kernel_class=EchoKernel)
92
92
93 Here's the Kernel spec ``kernel.json`` file for this::
93 Here's the Kernel spec ``kernel.json`` file for this::
94
94
95 {"argv":["python","-m","echokernel", "-f", "{connection_file}"],
95 {"argv":["python","-m","echokernel", "-f", "{connection_file}"],
96 "display_name":"Echo",
96 "display_name":"Echo",
97 "language":"no-op"
97 "language":"no-op"
98 }
98 }
99
99
100
100
101 Optional steps
101 Optional steps
102 --------------
102 --------------
103
103
104 You can override a number of other methods to improve the functionality of your
104 You can override a number of other methods to improve the functionality of your
105 kernel. All of these methods should return a dictionary as described in the
105 kernel. All of these methods should return a dictionary as described in the
106 relevant section of the :doc:`messaging spec <messaging>`.
106 relevant section of the :doc:`messaging spec <messaging>`.
107
107
108 .. class:: MyKernel
108 .. class:: MyKernel
109
109
110 .. method:: do_complete(code, cusor_pos)
110 .. method:: do_complete(code, cusor_pos)
111
111
112 Code completion
112 Code completion
113
113
114 :param str code: The code already present
114 :param str code: The code already present
115 :param int cursor_pos: The position in the code where completion is requested
115 :param int cursor_pos: The position in the code where completion is requested
116
116
117 .. seealso::
117 .. seealso::
118
118
119 :ref:`msging_completion` messages
119 :ref:`msging_completion` messages
120
120
121 .. method:: do_inspect(code, cusor_pos, detail_level=0)
121 .. method:: do_inspect(code, cusor_pos, detail_level=0)
122
122
123 Object introspection
123 Object introspection
124
124
125 :param str code: The code
125 :param str code: The code
126 :param int cursor_pos: The position in the code where introspection is requested
126 :param int cursor_pos: The position in the code where introspection is requested
127 :param int detail_level: 0 or 1 for more or less detail. In IPython, 1 gets
127 :param int detail_level: 0 or 1 for more or less detail. In IPython, 1 gets
128 the source code.
128 the source code.
129
129
130 .. seealso::
130 .. seealso::
131
131
132 :ref:`msging_inspection` messages
132 :ref:`msging_inspection` messages
133
133
134 .. method:: do_history(hist_access_type, output, raw, session=None, start=None, stop=None, n=None, pattern=None, unique=False)
134 .. method:: do_history(hist_access_type, output, raw, session=None, start=None, stop=None, n=None, pattern=None, unique=False)
135
135
136 History access. Only the relevant parameters for the type of history
136 History access. Only the relevant parameters for the type of history
137 request concerned will be passed, so your method definition must have defaults
137 request concerned will be passed, so your method definition must have defaults
138 for all the arguments shown with defaults here.
138 for all the arguments shown with defaults here.
139
139
140 .. seealso::
140 .. seealso::
141
141
142 :ref:`msging_history` messages
142 :ref:`msging_history` messages
143
143
144 .. method:: do_is_complete(code)
145
146 Is code entered in a console-like interface complete and ready to execute,
147 or should a continuation prompt be shown?
148
149 :param str code: The code entered so far - possibly multiple lines
150
151 .. seealso::
152
153 :ref:`msging_is_complete` messages
154
144 .. method:: do_shutdown(restart)
155 .. method:: do_shutdown(restart)
145
156
146 Shutdown the kernel. You only need to handle your own clean up - the kernel
157 Shutdown the kernel. You only need to handle your own clean up - the kernel
147 machinery will take care of cleaning up its own things before stopping.
158 machinery will take care of cleaning up its own things before stopping.
148
159
149 :param bool restart: Whether the kernel will be started again afterwards
160 :param bool restart: Whether the kernel will be started again afterwards
150
161
151 .. seealso::
162 .. seealso::
152
163
153 :ref:`msging_shutdown` messages
164 :ref:`msging_shutdown` messages
General Comments 0
You need to be logged in to leave comments. Login now