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