##// END OF EJS Templates
set busy/idle around every message...
MinRK -
Show More
@@ -1,669 +1,677 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 # Set the parent message for side effects.
141 self.set_parent(idents, msg)
142 self._publish_status(u'busy')
143
140 144 header = msg['header']
141 145 msg_type = header['msg_type']
142 146
143 147 handler = self.control_handlers.get(msg_type, None)
144 148 if handler is None:
145 149 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
146 150 else:
147 151 try:
148 152 handler(self.control_stream, idents, msg)
149 153 except Exception:
150 154 self.log.error("Exception in control handler:", exc_info=True)
151 155
156 sys.stdout.flush()
157 sys.stderr.flush()
158 self._publish_status(u'idle')
159
152 160 def dispatch_shell(self, stream, msg):
153 161 """dispatch shell requests"""
154 162 # flush control requests first
155 163 if self.control_stream:
156 164 self.control_stream.flush()
157 165
158 166 idents,msg = self.session.feed_identities(msg, copy=False)
159 167 try:
160 168 msg = self.session.unserialize(msg, content=True, copy=False)
161 169 except:
162 170 self.log.error("Invalid Message", exc_info=True)
163 171 return
164 172
165 173 # Set the parent message for side effects.
166 174 self.set_parent(idents, msg)
167 175 self._publish_status(u'busy')
168 176
169 177 header = msg['header']
170 178 msg_id = header['msg_id']
171 179 msg_type = msg['header']['msg_type']
172 180
173 181 # Print some info about this message and leave a '--->' marker, so it's
174 182 # easier to trace visually the message chain when debugging. Each
175 183 # handler prints its message at the end.
176 184 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
177 185 self.log.debug(' Content: %s\n --->\n ', msg['content'])
178 186
179 187 if msg_id in self.aborted:
180 188 self.aborted.remove(msg_id)
181 189 # is it safe to assume a msg_id will not be resubmitted?
182 190 reply_type = msg_type.split('_')[0] + '_reply'
183 191 status = {'status' : 'aborted'}
184 192 md = {'engine' : self.ident}
185 193 md.update(status)
186 194 self.session.send(stream, reply_type, metadata=md,
187 195 content=status, parent=msg, ident=idents)
188 196 return
189 197
190 198 handler = self.shell_handlers.get(msg_type, None)
191 199 if handler is None:
192 200 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
193 201 else:
194 202 # ensure default_int_handler during handler call
195 203 sig = signal(SIGINT, default_int_handler)
196 204 self.log.debug("%s: %s", msg_type, msg)
197 205 try:
198 206 handler(stream, idents, msg)
199 207 except Exception:
200 208 self.log.error("Exception in message handler:", exc_info=True)
201 209 finally:
202 210 signal(SIGINT, sig)
203 211
204 212 sys.stdout.flush()
205 213 sys.stderr.flush()
206 214 self._publish_status(u'idle')
207 215
208 216 def enter_eventloop(self):
209 217 """enter eventloop"""
210 218 self.log.info("entering eventloop %s", self.eventloop)
211 219 for stream in self.shell_streams:
212 220 # flush any pending replies,
213 221 # which may be skipped by entering the eventloop
214 222 stream.flush(zmq.POLLOUT)
215 223 # restore default_int_handler
216 224 signal(SIGINT, default_int_handler)
217 225 while self.eventloop is not None:
218 226 try:
219 227 self.eventloop(self)
220 228 except KeyboardInterrupt:
221 229 # Ctrl-C shouldn't crash the kernel
222 230 self.log.error("KeyboardInterrupt caught in kernel")
223 231 continue
224 232 else:
225 233 # eventloop exited cleanly, this means we should stop (right?)
226 234 self.eventloop = None
227 235 break
228 236 self.log.info("exiting eventloop")
229 237
230 238 def start(self):
231 239 """register dispatchers for streams"""
232 240 if self.control_stream:
233 241 self.control_stream.on_recv(self.dispatch_control, copy=False)
234 242
235 243 def make_dispatcher(stream):
236 244 def dispatcher(msg):
237 245 return self.dispatch_shell(stream, msg)
238 246 return dispatcher
239 247
240 248 for s in self.shell_streams:
241 249 s.on_recv(make_dispatcher(s), copy=False)
242 250
243 251 # publish idle status
244 252 self._publish_status('starting')
245 253
246 254 def do_one_iteration(self):
247 255 """step eventloop just once"""
248 256 if self.control_stream:
249 257 self.control_stream.flush()
250 258 for stream in self.shell_streams:
251 259 # handle at most one request per iteration
252 260 stream.flush(zmq.POLLIN, 1)
253 261 stream.flush(zmq.POLLOUT)
254 262
255 263
256 264 def record_ports(self, ports):
257 265 """Record the ports that this kernel is using.
258 266
259 267 The creator of the Kernel instance must call this methods if they
260 268 want the :meth:`connect_request` method to return the port numbers.
261 269 """
262 270 self._recorded_ports = ports
263 271
264 272 #---------------------------------------------------------------------------
265 273 # Kernel request handlers
266 274 #---------------------------------------------------------------------------
267 275
268 276 def _make_metadata(self, other=None):
269 277 """init metadata dict, for execute/apply_reply"""
270 278 new_md = {
271 279 'dependencies_met' : True,
272 280 'engine' : self.ident,
273 281 'started': datetime.now(),
274 282 }
275 283 if other:
276 284 new_md.update(other)
277 285 return new_md
278 286
279 287 def _publish_execute_input(self, code, parent, execution_count):
280 288 """Publish the code request on the iopub stream."""
281 289
282 290 self.session.send(self.iopub_socket, u'execute_input',
283 291 {u'code':code, u'execution_count': execution_count},
284 292 parent=parent, ident=self._topic('execute_input')
285 293 )
286 294
287 295 def _publish_status(self, status, parent=None):
288 296 """send status (busy/idle) on IOPub"""
289 297 self.session.send(self.iopub_socket,
290 298 u'status',
291 299 {u'execution_state': status},
292 300 parent=parent or self._parent_header,
293 301 ident=self._topic('status'),
294 302 )
295 303
296 304 def set_parent(self, ident, parent):
297 305 """Set the current parent_header
298 306
299 307 Side effects (IOPub messages) and replies are associated with
300 308 the request that caused them via the parent_header.
301 309
302 310 The parent identity is used to route input_request messages
303 311 on the stdin channel.
304 312 """
305 313 self._parent_ident = ident
306 314 self._parent_header = parent
307 315
308 316 def send_response(self, stream, msg_or_type, content=None, ident=None,
309 317 buffers=None, track=False, header=None, metadata=None):
310 318 """Send a response to the message we're currently processing.
311 319
312 320 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
313 321 except ``parent``.
314 322
315 323 This relies on :meth:`set_parent` having been called for the current
316 324 message.
317 325 """
318 326 return self.session.send(stream, msg_or_type, content, self._parent_header,
319 327 ident, buffers, track, header, metadata)
320 328
321 329 def execute_request(self, stream, ident, parent):
322 330 """handle an execute_request"""
323 331
324 332 try:
325 333 content = parent[u'content']
326 334 code = py3compat.cast_unicode_py2(content[u'code'])
327 335 silent = content[u'silent']
328 336 store_history = content.get(u'store_history', not silent)
329 337 user_expressions = content.get('user_expressions', {})
330 338 allow_stdin = content.get('allow_stdin', False)
331 339 except:
332 340 self.log.error("Got bad msg: ")
333 341 self.log.error("%s", parent)
334 342 return
335 343
336 344 md = self._make_metadata(parent['metadata'])
337 345
338 346 # Re-broadcast our input for the benefit of listening clients, and
339 347 # start computing output
340 348 if not silent:
341 349 self.execution_count += 1
342 350 self._publish_execute_input(code, parent, self.execution_count)
343 351
344 352 reply_content = self.do_execute(code, silent, store_history,
345 353 user_expressions, allow_stdin)
346 354
347 355 # Flush output before sending the reply.
348 356 sys.stdout.flush()
349 357 sys.stderr.flush()
350 358 # FIXME: on rare occasions, the flush doesn't seem to make it to the
351 359 # clients... This seems to mitigate the problem, but we definitely need
352 360 # to better understand what's going on.
353 361 if self._execute_sleep:
354 362 time.sleep(self._execute_sleep)
355 363
356 364 # Send the reply.
357 365 reply_content = json_clean(reply_content)
358 366
359 367 md['status'] = reply_content['status']
360 368 if reply_content['status'] == 'error' and \
361 369 reply_content['ename'] == 'UnmetDependency':
362 370 md['dependencies_met'] = False
363 371
364 372 reply_msg = self.session.send(stream, u'execute_reply',
365 373 reply_content, parent, metadata=md,
366 374 ident=ident)
367 375
368 376 self.log.debug("%s", reply_msg)
369 377
370 378 if not silent and reply_msg['content']['status'] == u'error':
371 379 self._abort_queues()
372 380
373 381 def do_execute(self, code, silent, store_history=True,
374 382 user_experssions=None, allow_stdin=False):
375 383 """Execute user code. Must be overridden by subclasses.
376 384 """
377 385 raise NotImplementedError
378 386
379 387 def complete_request(self, stream, ident, parent):
380 388 content = parent['content']
381 389 code = content['code']
382 390 cursor_pos = content['cursor_pos']
383 391
384 392 matches = self.do_complete(code, cursor_pos)
385 393 matches = json_clean(matches)
386 394 completion_msg = self.session.send(stream, 'complete_reply',
387 395 matches, parent, ident)
388 396 self.log.debug("%s", completion_msg)
389 397
390 398 def do_complete(self, code, cursor_pos):
391 399 """Override in subclasses to find completions.
392 400 """
393 401 return {'matches' : [],
394 402 'cursor_end' : cursor_pos,
395 403 'cursor_start' : cursor_pos,
396 404 'metadata' : {},
397 405 'status' : 'ok'}
398 406
399 407 def inspect_request(self, stream, ident, parent):
400 408 content = parent['content']
401 409
402 410 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
403 411 content.get('detail_level', 0))
404 412 # Before we send this object over, we scrub it for JSON usage
405 413 reply_content = json_clean(reply_content)
406 414 msg = self.session.send(stream, 'inspect_reply',
407 415 reply_content, parent, ident)
408 416 self.log.debug("%s", msg)
409 417
410 418 def do_inspect(self, code, cursor_pos, detail_level=0):
411 419 """Override in subclasses to allow introspection.
412 420 """
413 421 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
414 422
415 423 def history_request(self, stream, ident, parent):
416 424 content = parent['content']
417 425
418 426 reply_content = self.do_history(**content)
419 427
420 428 reply_content = json_clean(reply_content)
421 429 msg = self.session.send(stream, 'history_reply',
422 430 reply_content, parent, ident)
423 431 self.log.debug("%s", msg)
424 432
425 433 def do_history(self, hist_access_type, output, raw, session=None, start=None,
426 434 stop=None, n=None, pattern=None, unique=False):
427 435 """Override in subclasses to access history.
428 436 """
429 437 return {'history': []}
430 438
431 439 def connect_request(self, stream, ident, parent):
432 440 if self._recorded_ports is not None:
433 441 content = self._recorded_ports.copy()
434 442 else:
435 443 content = {}
436 444 msg = self.session.send(stream, 'connect_reply',
437 445 content, parent, ident)
438 446 self.log.debug("%s", msg)
439 447
440 448 @property
441 449 def kernel_info(self):
442 450 return {
443 451 'protocol_version': release.kernel_protocol_version,
444 452 'implementation': self.implementation,
445 453 'implementation_version': self.implementation_version,
446 454 'language': self.language,
447 455 'language_version': self.language_version,
448 456 'banner': self.banner,
449 457 }
450 458
451 459 def kernel_info_request(self, stream, ident, parent):
452 460 msg = self.session.send(stream, 'kernel_info_reply',
453 461 self.kernel_info, parent, ident)
454 462 self.log.debug("%s", msg)
455 463
456 464 def shutdown_request(self, stream, ident, parent):
457 465 content = self.do_shutdown(parent['content']['restart'])
458 466 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
459 467 # same content, but different msg_id for broadcasting on IOPub
460 468 self._shutdown_message = self.session.msg(u'shutdown_reply',
461 469 content, parent
462 470 )
463 471
464 472 self._at_shutdown()
465 473 # call sys.exit after a short delay
466 474 loop = ioloop.IOLoop.instance()
467 475 loop.add_timeout(time.time()+0.1, loop.stop)
468 476
469 477 def do_shutdown(self, restart):
470 478 """Override in subclasses to do things when the frontend shuts down the
471 479 kernel.
472 480 """
473 481 return {'status': 'ok', 'restart': restart}
474 482
475 483 #---------------------------------------------------------------------------
476 484 # Engine methods
477 485 #---------------------------------------------------------------------------
478 486
479 487 def apply_request(self, stream, ident, parent):
480 488 try:
481 489 content = parent[u'content']
482 490 bufs = parent[u'buffers']
483 491 msg_id = parent['header']['msg_id']
484 492 except:
485 493 self.log.error("Got bad msg: %s", parent, exc_info=True)
486 494 return
487 495
488 496 md = self._make_metadata(parent['metadata'])
489 497
490 498 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
491 499
492 500 # put 'ok'/'error' status in header, for scheduler introspection:
493 501 md['status'] = reply_content['status']
494 502
495 503 # flush i/o
496 504 sys.stdout.flush()
497 505 sys.stderr.flush()
498 506
499 507 self.session.send(stream, u'apply_reply', reply_content,
500 508 parent=parent, ident=ident,buffers=result_buf, metadata=md)
501 509
502 510 def do_apply(self, content, bufs, msg_id, reply_metadata):
503 511 """Override in subclasses to support the IPython parallel framework.
504 512 """
505 513 raise NotImplementedError
506 514
507 515 #---------------------------------------------------------------------------
508 516 # Control messages
509 517 #---------------------------------------------------------------------------
510 518
511 519 def abort_request(self, stream, ident, parent):
512 520 """abort a specifig msg by id"""
513 521 msg_ids = parent['content'].get('msg_ids', None)
514 522 if isinstance(msg_ids, string_types):
515 523 msg_ids = [msg_ids]
516 524 if not msg_ids:
517 525 self.abort_queues()
518 526 for mid in msg_ids:
519 527 self.aborted.add(str(mid))
520 528
521 529 content = dict(status='ok')
522 530 reply_msg = self.session.send(stream, 'abort_reply', content=content,
523 531 parent=parent, ident=ident)
524 532 self.log.debug("%s", reply_msg)
525 533
526 534 def clear_request(self, stream, idents, parent):
527 535 """Clear our namespace."""
528 536 content = self.do_clear()
529 537 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
530 538 content = content)
531 539
532 540 def do_clear(self):
533 541 """Override in subclasses to clear the namespace
534 542
535 543 This is only required for IPython.parallel.
536 544 """
537 545 raise NotImplementedError
538 546
539 547 #---------------------------------------------------------------------------
540 548 # Protected interface
541 549 #---------------------------------------------------------------------------
542 550
543 551 def _topic(self, topic):
544 552 """prefixed topic for IOPub messages"""
545 553 if self.int_id >= 0:
546 554 base = "engine.%i" % self.int_id
547 555 else:
548 556 base = "kernel.%s" % self.ident
549 557
550 558 return py3compat.cast_bytes("%s.%s" % (base, topic))
551 559
552 560 def _abort_queues(self):
553 561 for stream in self.shell_streams:
554 562 if stream:
555 563 self._abort_queue(stream)
556 564
557 565 def _abort_queue(self, stream):
558 566 poller = zmq.Poller()
559 567 poller.register(stream.socket, zmq.POLLIN)
560 568 while True:
561 569 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
562 570 if msg is None:
563 571 return
564 572
565 573 self.log.info("Aborting:")
566 574 self.log.info("%s", msg)
567 575 msg_type = msg['header']['msg_type']
568 576 reply_type = msg_type.split('_')[0] + '_reply'
569 577
570 578 status = {'status' : 'aborted'}
571 579 md = {'engine' : self.ident}
572 580 md.update(status)
573 581 reply_msg = self.session.send(stream, reply_type, metadata=md,
574 582 content=status, parent=msg, ident=idents)
575 583 self.log.debug("%s", reply_msg)
576 584 # We need to wait a bit for requests to come in. This can probably
577 585 # be set shorter for true asynchronous clients.
578 586 poller.poll(50)
579 587
580 588
581 589 def _no_raw_input(self):
582 590 """Raise StdinNotImplentedError if active frontend doesn't support
583 591 stdin."""
584 592 raise StdinNotImplementedError("raw_input was called, but this "
585 593 "frontend does not support stdin.")
586 594
587 595 def getpass(self, prompt=''):
588 596 """Forward getpass to frontends
589 597
590 598 Raises
591 599 ------
592 600 StdinNotImplentedError if active frontend doesn't support stdin.
593 601 """
594 602 if not self._allow_stdin:
595 603 raise StdinNotImplementedError(
596 604 "getpass was called, but this frontend does not support input requests."
597 605 )
598 606 return self._input_request(prompt,
599 607 self._parent_ident,
600 608 self._parent_header,
601 609 password=True,
602 610 )
603 611
604 612 def raw_input(self, prompt=''):
605 613 """Forward raw_input to frontends
606 614
607 615 Raises
608 616 ------
609 617 StdinNotImplentedError if active frontend doesn't support stdin.
610 618 """
611 619 if not self._allow_stdin:
612 620 raise StdinNotImplementedError(
613 621 "raw_input was called, but this frontend does not support input requests."
614 622 )
615 623 return self._input_request(prompt,
616 624 self._parent_ident,
617 625 self._parent_header,
618 626 password=False,
619 627 )
620 628
621 629 def _input_request(self, prompt, ident, parent, password=False):
622 630 # Flush output before making the request.
623 631 sys.stderr.flush()
624 632 sys.stdout.flush()
625 633 # flush the stdin socket, to purge stale replies
626 634 while True:
627 635 try:
628 636 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
629 637 except zmq.ZMQError as e:
630 638 if e.errno == zmq.EAGAIN:
631 639 break
632 640 else:
633 641 raise
634 642
635 643 # Send the input request.
636 644 content = json_clean(dict(prompt=prompt, password=password))
637 645 self.session.send(self.stdin_socket, u'input_request', content, parent,
638 646 ident=ident)
639 647
640 648 # Await a response.
641 649 while True:
642 650 try:
643 651 ident, reply = self.session.recv(self.stdin_socket, 0)
644 652 except Exception:
645 653 self.log.warn("Invalid Message:", exc_info=True)
646 654 except KeyboardInterrupt:
647 655 # re-raise KeyboardInterrupt, to truncate traceback
648 656 raise KeyboardInterrupt
649 657 else:
650 658 break
651 659 try:
652 660 value = py3compat.unicode_to_str(reply['content']['value'])
653 661 except:
654 662 self.log.error("Bad input_reply: %s", parent)
655 663 value = ''
656 664 if value == '\x04':
657 665 # EOF
658 666 raise EOFError
659 667 return value
660 668
661 669 def _at_shutdown(self):
662 670 """Actions taken at shutdown by the kernel, called by python's atexit.
663 671 """
664 672 # io.rprint("Kernel at_shutdown") # dbg
665 673 if self._shutdown_message is not None:
666 674 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
667 675 self.log.debug("%s", self._shutdown_message)
668 676 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
669 677
@@ -1,1063 +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 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 895 .. versionchanged:: 5.0
896 896
897 Busy and idle messages should be sent before/after handling every shell message,
897 Busy and idle messages should be sent before/after handling every message,
898 898 not just execution.
899 899
900 900 Clear output
901 901 ------------
902 902
903 903 This message type is used to clear the output that is visible on the frontend.
904 904
905 905 Message type: ``clear_output``::
906 906
907 907 content = {
908 908
909 909 # Wait to clear the output until new output is available. Clears the
910 910 # existing output immediately before the new output is displayed.
911 911 # Useful for creating simple animations with minimal flickering.
912 912 'wait' : bool,
913 913 }
914 914
915 915 .. versionchanged:: 4.1
916 916
917 917 ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed,
918 918 and ``wait`` is added.
919 919 The selective clearing keys are ignored in v4 and the default behavior remains the same,
920 920 so v4 clear_output messages will be safely handled by a v4.1 frontend.
921 921
922 922
923 923 Messages on the stdin ROUTER/DEALER sockets
924 924 ===========================================
925 925
926 926 This is a socket where the request/reply pattern goes in the opposite direction:
927 927 from the kernel to a *single* frontend, and its purpose is to allow
928 928 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
929 929 to be fulfilled by the client. The request should be made to the frontend that
930 930 made the execution request that prompted ``raw_input`` to be called. For now we
931 931 will keep these messages as simple as possible, since they only mean to convey
932 932 the ``raw_input(prompt)`` call.
933 933
934 934 Message type: ``input_request``::
935 935
936 936 content = {
937 937 # the text to show at the prompt
938 938 'prompt' : str,
939 939 # Is the request for a password?
940 940 # If so, the frontend shouldn't echo input.
941 941 'password' : bool
942 942 }
943 943
944 944 Message type: ``input_reply``::
945 945
946 946 content = { 'value' : str }
947 947
948 948
949 949 When ``password`` is True, the frontend should not echo the input as it is entered.
950 950
951 951 .. versionchanged:: 5.0
952 952
953 953 ``password`` key added.
954 954
955 955 .. note::
956 956
957 957 The stdin socket of the client is required to have the same zmq IDENTITY
958 958 as the client's shell socket.
959 959 Because of this, the ``input_request`` must be sent with the same IDENTITY
960 960 routing prefix as the ``execute_reply`` in order for the frontend to receive
961 961 the message.
962 962
963 963 .. note::
964 964
965 965 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
966 966 practice the kernel should behave like an interactive program. When a
967 967 program is opened on the console, the keyboard effectively takes over the
968 968 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
969 969 Since the IPython kernel effectively behaves like a console program (albeit
970 970 one whose "keyboard" is actually living in a separate process and
971 971 transported over the zmq connection), raw ``stdin`` isn't expected to be
972 972 available.
973 973
974 974
975 975 Heartbeat for kernels
976 976 =====================
977 977
978 978 Clients send ping messages on a REQ socket, which are echoed right back
979 979 from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above.
980 980
981 981
982 982 Custom Messages
983 983 ===============
984 984
985 985 .. versionadded:: 4.1
986 986
987 987 IPython 2.0 (msgspec v4.1) adds a messaging system for developers to add their own objects with Frontend
988 988 and Kernel-side components, and allow them to communicate with each other.
989 989 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
990 990 and can communicate in either direction.
991 991
992 992 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
993 993 and no messages expect a reply.
994 994 The Kernel listens for these messages on the Shell channel,
995 995 and the Frontend listens for them on the IOPub channel.
996 996
997 997 Opening a Comm
998 998 --------------
999 999
1000 1000 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1001 1001
1002 1002 {
1003 1003 'comm_id' : 'u-u-i-d',
1004 1004 'target_name' : 'my_comm',
1005 1005 'data' : {}
1006 1006 }
1007 1007
1008 1008 Every Comm has an ID and a target name.
1009 1009 The code handling the message on the receiving side is responsible for maintaining a mapping
1010 1010 of target_name keys to constructors.
1011 1011 After a ``comm_open`` message has been sent,
1012 1012 there should be a corresponding Comm instance on both sides.
1013 1013 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1014 1014
1015 1015 If the ``target_name`` key is not found on the receiving side,
1016 1016 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1017 1017
1018 1018 Comm Messages
1019 1019 -------------
1020 1020
1021 1021 Comm messages are one-way communications to update comm state,
1022 1022 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1023 1023
1024 1024 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1025 1025
1026 1026 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1027 1027
1028 1028 Message type: ``comm_msg``::
1029 1029
1030 1030 {
1031 1031 'comm_id' : 'u-u-i-d',
1032 1032 'data' : {}
1033 1033 }
1034 1034
1035 1035 Tearing Down Comms
1036 1036 ------------------
1037 1037
1038 1038 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1039 1039 This is done with a ``comm_close`` message.
1040 1040
1041 1041 Message type: ``comm_close``::
1042 1042
1043 1043 {
1044 1044 'comm_id' : 'u-u-i-d',
1045 1045 'data' : {}
1046 1046 }
1047 1047
1048 1048 Output Side Effects
1049 1049 -------------------
1050 1050
1051 1051 Since comm messages can execute arbitrary user code,
1052 1052 handlers should set the parent header and publish status busy / idle,
1053 1053 just like an execute request.
1054 1054
1055 1055
1056 1056 To Do
1057 1057 =====
1058 1058
1059 1059 Missing things include:
1060 1060
1061 1061 * Important: finish thinking through the payload concept and API.
1062 1062
1063 1063 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now