##// END OF EJS Templates
Rename version_rep/req to kernel_info_rep/req
Takafumi Arakaki -
Show More
@@ -1,949 +1,949 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports
19 19 import __builtin__
20 20 import atexit
21 21 import sys
22 22 import time
23 23 import traceback
24 24 import logging
25 25 import uuid
26 26
27 27 from datetime import datetime
28 28 from signal import (
29 29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
30 30 )
31 31
32 32 # System library imports
33 33 import zmq
34 34 from zmq.eventloop import ioloop
35 35 from zmq.eventloop.zmqstream import ZMQStream
36 36
37 37 # Local imports
38 38 import IPython
39 39 from IPython.config.configurable import Configurable
40 40 from IPython.config.application import boolean_flag, catch_config_error
41 41 from IPython.core.application import ProfileDir
42 42 from IPython.core.error import StdinNotImplementedError
43 43 from IPython.core.shellapp import (
44 44 InteractiveShellApp, shell_flags, shell_aliases
45 45 )
46 46 from IPython.utils import io
47 47 from IPython.utils import py3compat
48 48 from IPython.utils.frame import extract_module_locals
49 49 from IPython.utils.jsonutil import json_clean
50 50 from IPython.utils.traitlets import (
51 51 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
52 52 )
53 53
54 54 from entry_point import base_launch_kernel
55 55 from kernelapp import KernelApp, kernel_flags, kernel_aliases
56 56 from serialize import serialize_object, unpack_apply_message
57 57 from session import Session, Message, protocol_version
58 58 from zmqshell import ZMQInteractiveShell
59 59
60 60
61 61 #-----------------------------------------------------------------------------
62 62 # Main kernel class
63 63 #-----------------------------------------------------------------------------
64 64
65 65 ipython_version = list(IPython.version_info)
66 66 language_version = list(sys.version_info[:3])
67 67
68 68
69 69 class Kernel(Configurable):
70 70
71 71 #---------------------------------------------------------------------------
72 72 # Kernel interface
73 73 #---------------------------------------------------------------------------
74 74
75 75 # attribute to override with a GUI
76 76 eventloop = Any(None)
77 77 def _eventloop_changed(self, name, old, new):
78 78 """schedule call to eventloop from IOLoop"""
79 79 loop = ioloop.IOLoop.instance()
80 80 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
81 81
82 82 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
83 83 session = Instance(Session)
84 84 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
85 85 shell_streams = List()
86 86 control_stream = Instance(ZMQStream)
87 87 iopub_socket = Instance(zmq.Socket)
88 88 stdin_socket = Instance(zmq.Socket)
89 89 log = Instance(logging.Logger)
90 90
91 91 user_module = Any()
92 92 def _user_module_changed(self, name, old, new):
93 93 if self.shell is not None:
94 94 self.shell.user_module = new
95 95
96 96 user_ns = Dict(default_value=None)
97 97 def _user_ns_changed(self, name, old, new):
98 98 if self.shell is not None:
99 99 self.shell.user_ns = new
100 100 self.shell.init_user_ns()
101 101
102 102 # identities:
103 103 int_id = Integer(-1)
104 104 ident = Unicode()
105 105
106 106 def _ident_default(self):
107 107 return unicode(uuid.uuid4())
108 108
109 109
110 110 # Private interface
111 111
112 112 # Time to sleep after flushing the stdout/err buffers in each execute
113 113 # cycle. While this introduces a hard limit on the minimal latency of the
114 114 # execute cycle, it helps prevent output synchronization problems for
115 115 # clients.
116 116 # Units are in seconds. The minimum zmq latency on local host is probably
117 117 # ~150 microseconds, set this to 500us for now. We may need to increase it
118 118 # a little if it's not enough after more interactive testing.
119 119 _execute_sleep = Float(0.0005, config=True)
120 120
121 121 # Frequency of the kernel's event loop.
122 122 # Units are in seconds, kernel subclasses for GUI toolkits may need to
123 123 # adapt to milliseconds.
124 124 _poll_interval = Float(0.05, config=True)
125 125
126 126 # If the shutdown was requested over the network, we leave here the
127 127 # necessary reply message so it can be sent by our registered atexit
128 128 # handler. This ensures that the reply is only sent to clients truly at
129 129 # the end of our shutdown process (which happens after the underlying
130 130 # IPython shell's own shutdown).
131 131 _shutdown_message = None
132 132
133 133 # This is a dict of port number that the kernel is listening on. It is set
134 134 # by record_ports and used by connect_request.
135 135 _recorded_ports = Dict()
136 136
137 137 # set of aborted msg_ids
138 138 aborted = Set()
139 139
140 140
141 141 def __init__(self, **kwargs):
142 142 super(Kernel, self).__init__(**kwargs)
143 143
144 144 # Initialize the InteractiveShell subclass
145 145 self.shell = ZMQInteractiveShell.instance(config=self.config,
146 146 profile_dir = self.profile_dir,
147 147 user_module = self.user_module,
148 148 user_ns = self.user_ns,
149 149 )
150 150 self.shell.displayhook.session = self.session
151 151 self.shell.displayhook.pub_socket = self.iopub_socket
152 152 self.shell.displayhook.topic = self._topic('pyout')
153 153 self.shell.display_pub.session = self.session
154 154 self.shell.display_pub.pub_socket = self.iopub_socket
155 155 self.shell.data_pub.session = self.session
156 156 self.shell.data_pub.pub_socket = self.iopub_socket
157 157
158 158 # TMP - hack while developing
159 159 self.shell._reply_content = None
160 160
161 161 # Build dict of handlers for message types
162 162 msg_types = [ 'execute_request', 'complete_request',
163 163 'object_info_request', 'history_request',
164 'version_request',
164 'kernel_info_request',
165 165 'connect_request', 'shutdown_request',
166 166 'apply_request',
167 167 ]
168 168 self.shell_handlers = {}
169 169 for msg_type in msg_types:
170 170 self.shell_handlers[msg_type] = getattr(self, msg_type)
171 171
172 172 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
173 173 self.control_handlers = {}
174 174 for msg_type in control_msg_types:
175 175 self.control_handlers[msg_type] = getattr(self, msg_type)
176 176
177 177 def dispatch_control(self, msg):
178 178 """dispatch control requests"""
179 179 idents,msg = self.session.feed_identities(msg, copy=False)
180 180 try:
181 181 msg = self.session.unserialize(msg, content=True, copy=False)
182 182 except:
183 183 self.log.error("Invalid Control Message", exc_info=True)
184 184 return
185 185
186 186 self.log.debug("Control received: %s", msg)
187 187
188 188 header = msg['header']
189 189 msg_id = header['msg_id']
190 190 msg_type = header['msg_type']
191 191
192 192 handler = self.control_handlers.get(msg_type, None)
193 193 if handler is None:
194 194 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
195 195 else:
196 196 try:
197 197 handler(self.control_stream, idents, msg)
198 198 except Exception:
199 199 self.log.error("Exception in control handler:", exc_info=True)
200 200
201 201 def dispatch_shell(self, stream, msg):
202 202 """dispatch shell requests"""
203 203 # flush control requests first
204 204 if self.control_stream:
205 205 self.control_stream.flush()
206 206
207 207 idents,msg = self.session.feed_identities(msg, copy=False)
208 208 try:
209 209 msg = self.session.unserialize(msg, content=True, copy=False)
210 210 except:
211 211 self.log.error("Invalid Message", exc_info=True)
212 212 return
213 213
214 214 header = msg['header']
215 215 msg_id = header['msg_id']
216 216 msg_type = msg['header']['msg_type']
217 217
218 218 # Print some info about this message and leave a '--->' marker, so it's
219 219 # easier to trace visually the message chain when debugging. Each
220 220 # handler prints its message at the end.
221 221 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
222 222 self.log.debug(' Content: %s\n --->\n ', msg['content'])
223 223
224 224 if msg_id in self.aborted:
225 225 self.aborted.remove(msg_id)
226 226 # is it safe to assume a msg_id will not be resubmitted?
227 227 reply_type = msg_type.split('_')[0] + '_reply'
228 228 status = {'status' : 'aborted'}
229 229 md = {'engine' : self.ident}
230 230 md.update(status)
231 231 reply_msg = self.session.send(stream, reply_type, metadata=md,
232 232 content=status, parent=msg, ident=idents)
233 233 return
234 234
235 235 handler = self.shell_handlers.get(msg_type, None)
236 236 if handler is None:
237 237 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
238 238 else:
239 239 # ensure default_int_handler during handler call
240 240 sig = signal(SIGINT, default_int_handler)
241 241 try:
242 242 handler(stream, idents, msg)
243 243 except Exception:
244 244 self.log.error("Exception in message handler:", exc_info=True)
245 245 finally:
246 246 signal(SIGINT, sig)
247 247
248 248 def enter_eventloop(self):
249 249 """enter eventloop"""
250 250 self.log.info("entering eventloop")
251 251 # restore default_int_handler
252 252 signal(SIGINT, default_int_handler)
253 253 while self.eventloop is not None:
254 254 try:
255 255 self.eventloop(self)
256 256 except KeyboardInterrupt:
257 257 # Ctrl-C shouldn't crash the kernel
258 258 self.log.error("KeyboardInterrupt caught in kernel")
259 259 continue
260 260 else:
261 261 # eventloop exited cleanly, this means we should stop (right?)
262 262 self.eventloop = None
263 263 break
264 264 self.log.info("exiting eventloop")
265 265
266 266 def start(self):
267 267 """register dispatchers for streams"""
268 268 self.shell.exit_now = False
269 269 if self.control_stream:
270 270 self.control_stream.on_recv(self.dispatch_control, copy=False)
271 271
272 272 def make_dispatcher(stream):
273 273 def dispatcher(msg):
274 274 return self.dispatch_shell(stream, msg)
275 275 return dispatcher
276 276
277 277 for s in self.shell_streams:
278 278 s.on_recv(make_dispatcher(s), copy=False)
279 279
280 280 def do_one_iteration(self):
281 281 """step eventloop just once"""
282 282 if self.control_stream:
283 283 self.control_stream.flush()
284 284 for stream in self.shell_streams:
285 285 # handle at most one request per iteration
286 286 stream.flush(zmq.POLLIN, 1)
287 287 stream.flush(zmq.POLLOUT)
288 288
289 289
290 290 def record_ports(self, ports):
291 291 """Record the ports that this kernel is using.
292 292
293 293 The creator of the Kernel instance must call this methods if they
294 294 want the :meth:`connect_request` method to return the port numbers.
295 295 """
296 296 self._recorded_ports = ports
297 297
298 298 #---------------------------------------------------------------------------
299 299 # Kernel request handlers
300 300 #---------------------------------------------------------------------------
301 301
302 302 def _make_metadata(self, other=None):
303 303 """init metadata dict, for execute/apply_reply"""
304 304 new_md = {
305 305 'dependencies_met' : True,
306 306 'engine' : self.ident,
307 307 'started': datetime.now(),
308 308 }
309 309 if other:
310 310 new_md.update(other)
311 311 return new_md
312 312
313 313 def _publish_pyin(self, code, parent, execution_count):
314 314 """Publish the code request on the pyin stream."""
315 315
316 316 self.session.send(self.iopub_socket, u'pyin',
317 317 {u'code':code, u'execution_count': execution_count},
318 318 parent=parent, ident=self._topic('pyin')
319 319 )
320 320
321 321 def _publish_status(self, status, parent=None):
322 322 """send status (busy/idle) on IOPub"""
323 323 self.session.send(self.iopub_socket,
324 324 u'status',
325 325 {u'execution_state': status},
326 326 parent=parent,
327 327 ident=self._topic('status'),
328 328 )
329 329
330 330
331 331 def execute_request(self, stream, ident, parent):
332 332 """handle an execute_request"""
333 333
334 334 self._publish_status(u'busy', parent)
335 335
336 336 try:
337 337 content = parent[u'content']
338 338 code = content[u'code']
339 339 silent = content[u'silent']
340 340 store_history = content.get(u'store_history', not silent)
341 341 except:
342 342 self.log.error("Got bad msg: ")
343 343 self.log.error("%s", parent)
344 344 return
345 345
346 346 md = self._make_metadata(parent['metadata'])
347 347
348 348 shell = self.shell # we'll need this a lot here
349 349
350 350 # Replace raw_input. Note that is not sufficient to replace
351 351 # raw_input in the user namespace.
352 352 if content.get('allow_stdin', False):
353 353 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
354 354 else:
355 355 raw_input = lambda prompt='' : self._no_raw_input()
356 356
357 357 if py3compat.PY3:
358 358 __builtin__.input = raw_input
359 359 else:
360 360 __builtin__.raw_input = raw_input
361 361
362 362 # Set the parent message of the display hook and out streams.
363 363 shell.displayhook.set_parent(parent)
364 364 shell.display_pub.set_parent(parent)
365 365 shell.data_pub.set_parent(parent)
366 366 sys.stdout.set_parent(parent)
367 367 sys.stderr.set_parent(parent)
368 368
369 369 # Re-broadcast our input for the benefit of listening clients, and
370 370 # start computing output
371 371 if not silent:
372 372 self._publish_pyin(code, parent, shell.execution_count)
373 373
374 374 reply_content = {}
375 375 try:
376 376 # FIXME: the shell calls the exception handler itself.
377 377 shell.run_cell(code, store_history=store_history, silent=silent)
378 378 except:
379 379 status = u'error'
380 380 # FIXME: this code right now isn't being used yet by default,
381 381 # because the run_cell() call above directly fires off exception
382 382 # reporting. This code, therefore, is only active in the scenario
383 383 # where runlines itself has an unhandled exception. We need to
384 384 # uniformize this, for all exception construction to come from a
385 385 # single location in the codbase.
386 386 etype, evalue, tb = sys.exc_info()
387 387 tb_list = traceback.format_exception(etype, evalue, tb)
388 388 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
389 389 else:
390 390 status = u'ok'
391 391
392 392 reply_content[u'status'] = status
393 393
394 394 # Return the execution counter so clients can display prompts
395 395 reply_content['execution_count'] = shell.execution_count - 1
396 396
397 397 # FIXME - fish exception info out of shell, possibly left there by
398 398 # runlines. We'll need to clean up this logic later.
399 399 if shell._reply_content is not None:
400 400 reply_content.update(shell._reply_content)
401 401 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
402 402 reply_content['engine_info'] = e_info
403 403 # reset after use
404 404 shell._reply_content = None
405 405
406 406 # At this point, we can tell whether the main code execution succeeded
407 407 # or not. If it did, we proceed to evaluate user_variables/expressions
408 408 if reply_content['status'] == 'ok':
409 409 reply_content[u'user_variables'] = \
410 410 shell.user_variables(content.get(u'user_variables', []))
411 411 reply_content[u'user_expressions'] = \
412 412 shell.user_expressions(content.get(u'user_expressions', {}))
413 413 else:
414 414 # If there was an error, don't even try to compute variables or
415 415 # expressions
416 416 reply_content[u'user_variables'] = {}
417 417 reply_content[u'user_expressions'] = {}
418 418
419 419 # Payloads should be retrieved regardless of outcome, so we can both
420 420 # recover partial output (that could have been generated early in a
421 421 # block, before an error) and clear the payload system always.
422 422 reply_content[u'payload'] = shell.payload_manager.read_payload()
423 423 # Be agressive about clearing the payload because we don't want
424 424 # it to sit in memory until the next execute_request comes in.
425 425 shell.payload_manager.clear_payload()
426 426
427 427 # Flush output before sending the reply.
428 428 sys.stdout.flush()
429 429 sys.stderr.flush()
430 430 # FIXME: on rare occasions, the flush doesn't seem to make it to the
431 431 # clients... This seems to mitigate the problem, but we definitely need
432 432 # to better understand what's going on.
433 433 if self._execute_sleep:
434 434 time.sleep(self._execute_sleep)
435 435
436 436 # Send the reply.
437 437 reply_content = json_clean(reply_content)
438 438
439 439 md['status'] = reply_content['status']
440 440 if reply_content['status'] == 'error' and \
441 441 reply_content['ename'] == 'UnmetDependency':
442 442 md['dependencies_met'] = False
443 443
444 444 reply_msg = self.session.send(stream, u'execute_reply',
445 445 reply_content, parent, metadata=md,
446 446 ident=ident)
447 447
448 448 self.log.debug("%s", reply_msg)
449 449
450 450 if not silent and reply_msg['content']['status'] == u'error':
451 451 self._abort_queues()
452 452
453 453 self._publish_status(u'idle', parent)
454 454
455 455 def complete_request(self, stream, ident, parent):
456 456 txt, matches = self._complete(parent)
457 457 matches = {'matches' : matches,
458 458 'matched_text' : txt,
459 459 'status' : 'ok'}
460 460 matches = json_clean(matches)
461 461 completion_msg = self.session.send(stream, 'complete_reply',
462 462 matches, parent, ident)
463 463 self.log.debug("%s", completion_msg)
464 464
465 465 def object_info_request(self, stream, ident, parent):
466 466 content = parent['content']
467 467 object_info = self.shell.object_inspect(content['oname'],
468 468 detail_level = content.get('detail_level', 0)
469 469 )
470 470 # Before we send this object over, we scrub it for JSON usage
471 471 oinfo = json_clean(object_info)
472 472 msg = self.session.send(stream, 'object_info_reply',
473 473 oinfo, parent, ident)
474 474 self.log.debug("%s", msg)
475 475
476 476 def history_request(self, stream, ident, parent):
477 477 # We need to pull these out, as passing **kwargs doesn't work with
478 478 # unicode keys before Python 2.6.5.
479 479 hist_access_type = parent['content']['hist_access_type']
480 480 raw = parent['content']['raw']
481 481 output = parent['content']['output']
482 482 if hist_access_type == 'tail':
483 483 n = parent['content']['n']
484 484 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
485 485 include_latest=True)
486 486
487 487 elif hist_access_type == 'range':
488 488 session = parent['content']['session']
489 489 start = parent['content']['start']
490 490 stop = parent['content']['stop']
491 491 hist = self.shell.history_manager.get_range(session, start, stop,
492 492 raw=raw, output=output)
493 493
494 494 elif hist_access_type == 'search':
495 495 n = parent['content'].get('n')
496 496 pattern = parent['content']['pattern']
497 497 hist = self.shell.history_manager.search(pattern, raw=raw,
498 498 output=output, n=n)
499 499
500 500 else:
501 501 hist = []
502 502 hist = list(hist)
503 503 content = {'history' : hist}
504 504 content = json_clean(content)
505 505 msg = self.session.send(stream, 'history_reply',
506 506 content, parent, ident)
507 507 self.log.debug("Sending history reply with %i entries", len(hist))
508 508
509 509 def connect_request(self, stream, ident, parent):
510 510 if self._recorded_ports is not None:
511 511 content = self._recorded_ports.copy()
512 512 else:
513 513 content = {}
514 514 msg = self.session.send(stream, 'connect_reply',
515 515 content, parent, ident)
516 516 self.log.debug("%s", msg)
517 517
518 def version_request(self, stream, ident, parent):
518 def kernel_info_request(self, stream, ident, parent):
519 519 vinfo = {
520 520 'protocol_version': protocol_version,
521 521 'ipython_version': ipython_version,
522 522 'language_version': language_version,
523 523 'language': 'python',
524 524 }
525 msg = self.session.send(stream, 'version_reply',
525 msg = self.session.send(stream, 'kernel_info_reply',
526 526 vinfo, parent, ident)
527 527 self.log.debug("%s", msg)
528 528
529 529 def shutdown_request(self, stream, ident, parent):
530 530 self.shell.exit_now = True
531 531 content = dict(status='ok')
532 532 content.update(parent['content'])
533 533 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
534 534 # same content, but different msg_id for broadcasting on IOPub
535 535 self._shutdown_message = self.session.msg(u'shutdown_reply',
536 536 content, parent
537 537 )
538 538
539 539 self._at_shutdown()
540 540 # call sys.exit after a short delay
541 541 loop = ioloop.IOLoop.instance()
542 542 loop.add_timeout(time.time()+0.1, loop.stop)
543 543
544 544 #---------------------------------------------------------------------------
545 545 # Engine methods
546 546 #---------------------------------------------------------------------------
547 547
548 548 def apply_request(self, stream, ident, parent):
549 549 try:
550 550 content = parent[u'content']
551 551 bufs = parent[u'buffers']
552 552 msg_id = parent['header']['msg_id']
553 553 except:
554 554 self.log.error("Got bad msg: %s", parent, exc_info=True)
555 555 return
556 556
557 557 self._publish_status(u'busy', parent)
558 558
559 559 # Set the parent message of the display hook and out streams.
560 560 shell = self.shell
561 561 shell.displayhook.set_parent(parent)
562 562 shell.display_pub.set_parent(parent)
563 563 shell.data_pub.set_parent(parent)
564 564 sys.stdout.set_parent(parent)
565 565 sys.stderr.set_parent(parent)
566 566
567 567 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
568 568 # self.iopub_socket.send(pyin_msg)
569 569 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
570 570 md = self._make_metadata(parent['metadata'])
571 571 try:
572 572 working = shell.user_ns
573 573
574 574 prefix = "_"+str(msg_id).replace("-","")+"_"
575 575
576 576 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
577 577
578 578 fname = getattr(f, '__name__', 'f')
579 579
580 580 fname = prefix+"f"
581 581 argname = prefix+"args"
582 582 kwargname = prefix+"kwargs"
583 583 resultname = prefix+"result"
584 584
585 585 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
586 586 # print ns
587 587 working.update(ns)
588 588 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
589 589 try:
590 590 exec code in shell.user_global_ns, shell.user_ns
591 591 result = working.get(resultname)
592 592 finally:
593 593 for key in ns.iterkeys():
594 594 working.pop(key)
595 595
596 596 result_buf = serialize_object(result,
597 597 buffer_threshold=self.session.buffer_threshold,
598 598 item_threshold=self.session.item_threshold,
599 599 )
600 600
601 601 except:
602 602 # invoke IPython traceback formatting
603 603 shell.showtraceback()
604 604 # FIXME - fish exception info out of shell, possibly left there by
605 605 # run_code. We'll need to clean up this logic later.
606 606 reply_content = {}
607 607 if shell._reply_content is not None:
608 608 reply_content.update(shell._reply_content)
609 609 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
610 610 reply_content['engine_info'] = e_info
611 611 # reset after use
612 612 shell._reply_content = None
613 613
614 614 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
615 615 ident=self._topic('pyerr'))
616 616 result_buf = []
617 617
618 618 if reply_content['ename'] == 'UnmetDependency':
619 619 md['dependencies_met'] = False
620 620 else:
621 621 reply_content = {'status' : 'ok'}
622 622
623 623 # put 'ok'/'error' status in header, for scheduler introspection:
624 624 md['status'] = reply_content['status']
625 625
626 626 # flush i/o
627 627 sys.stdout.flush()
628 628 sys.stderr.flush()
629 629
630 630 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
631 631 parent=parent, ident=ident,buffers=result_buf, metadata=md)
632 632
633 633 self._publish_status(u'idle', parent)
634 634
635 635 #---------------------------------------------------------------------------
636 636 # Control messages
637 637 #---------------------------------------------------------------------------
638 638
639 639 def abort_request(self, stream, ident, parent):
640 640 """abort a specifig msg by id"""
641 641 msg_ids = parent['content'].get('msg_ids', None)
642 642 if isinstance(msg_ids, basestring):
643 643 msg_ids = [msg_ids]
644 644 if not msg_ids:
645 645 self.abort_queues()
646 646 for mid in msg_ids:
647 647 self.aborted.add(str(mid))
648 648
649 649 content = dict(status='ok')
650 650 reply_msg = self.session.send(stream, 'abort_reply', content=content,
651 651 parent=parent, ident=ident)
652 652 self.log.debug("%s", reply_msg)
653 653
654 654 def clear_request(self, stream, idents, parent):
655 655 """Clear our namespace."""
656 656 self.shell.reset(False)
657 657 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
658 658 content = dict(status='ok'))
659 659
660 660
661 661 #---------------------------------------------------------------------------
662 662 # Protected interface
663 663 #---------------------------------------------------------------------------
664 664
665 665
666 666 def _wrap_exception(self, method=None):
667 667 # import here, because _wrap_exception is only used in parallel,
668 668 # and parallel has higher min pyzmq version
669 669 from IPython.parallel.error import wrap_exception
670 670 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
671 671 content = wrap_exception(e_info)
672 672 return content
673 673
674 674 def _topic(self, topic):
675 675 """prefixed topic for IOPub messages"""
676 676 if self.int_id >= 0:
677 677 base = "engine.%i" % self.int_id
678 678 else:
679 679 base = "kernel.%s" % self.ident
680 680
681 681 return py3compat.cast_bytes("%s.%s" % (base, topic))
682 682
683 683 def _abort_queues(self):
684 684 for stream in self.shell_streams:
685 685 if stream:
686 686 self._abort_queue(stream)
687 687
688 688 def _abort_queue(self, stream):
689 689 poller = zmq.Poller()
690 690 poller.register(stream.socket, zmq.POLLIN)
691 691 while True:
692 692 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
693 693 if msg is None:
694 694 return
695 695
696 696 self.log.info("Aborting:")
697 697 self.log.info("%s", msg)
698 698 msg_type = msg['header']['msg_type']
699 699 reply_type = msg_type.split('_')[0] + '_reply'
700 700
701 701 status = {'status' : 'aborted'}
702 702 md = {'engine' : self.ident}
703 703 md.update(status)
704 704 reply_msg = self.session.send(stream, reply_type, metadata=md,
705 705 content=status, parent=msg, ident=idents)
706 706 self.log.debug("%s", reply_msg)
707 707 # We need to wait a bit for requests to come in. This can probably
708 708 # be set shorter for true asynchronous clients.
709 709 poller.poll(50)
710 710
711 711
712 712 def _no_raw_input(self):
713 713 """Raise StdinNotImplentedError if active frontend doesn't support
714 714 stdin."""
715 715 raise StdinNotImplementedError("raw_input was called, but this "
716 716 "frontend does not support stdin.")
717 717
718 718 def _raw_input(self, prompt, ident, parent):
719 719 # Flush output before making the request.
720 720 sys.stderr.flush()
721 721 sys.stdout.flush()
722 722
723 723 # Send the input request.
724 724 content = json_clean(dict(prompt=prompt))
725 725 self.session.send(self.stdin_socket, u'input_request', content, parent,
726 726 ident=ident)
727 727
728 728 # Await a response.
729 729 while True:
730 730 try:
731 731 ident, reply = self.session.recv(self.stdin_socket, 0)
732 732 except Exception:
733 733 self.log.warn("Invalid Message:", exc_info=True)
734 734 else:
735 735 break
736 736 try:
737 737 value = reply['content']['value']
738 738 except:
739 739 self.log.error("Got bad raw_input reply: ")
740 740 self.log.error("%s", parent)
741 741 value = ''
742 742 if value == '\x04':
743 743 # EOF
744 744 raise EOFError
745 745 return value
746 746
747 747 def _complete(self, msg):
748 748 c = msg['content']
749 749 try:
750 750 cpos = int(c['cursor_pos'])
751 751 except:
752 752 # If we don't get something that we can convert to an integer, at
753 753 # least attempt the completion guessing the cursor is at the end of
754 754 # the text, if there's any, and otherwise of the line
755 755 cpos = len(c['text'])
756 756 if cpos==0:
757 757 cpos = len(c['line'])
758 758 return self.shell.complete(c['text'], c['line'], cpos)
759 759
760 760 def _object_info(self, context):
761 761 symbol, leftover = self._symbol_from_context(context)
762 762 if symbol is not None and not leftover:
763 763 doc = getattr(symbol, '__doc__', '')
764 764 else:
765 765 doc = ''
766 766 object_info = dict(docstring = doc)
767 767 return object_info
768 768
769 769 def _symbol_from_context(self, context):
770 770 if not context:
771 771 return None, context
772 772
773 773 base_symbol_string = context[0]
774 774 symbol = self.shell.user_ns.get(base_symbol_string, None)
775 775 if symbol is None:
776 776 symbol = __builtin__.__dict__.get(base_symbol_string, None)
777 777 if symbol is None:
778 778 return None, context
779 779
780 780 context = context[1:]
781 781 for i, name in enumerate(context):
782 782 new_symbol = getattr(symbol, name, None)
783 783 if new_symbol is None:
784 784 return symbol, context[i:]
785 785 else:
786 786 symbol = new_symbol
787 787
788 788 return symbol, []
789 789
790 790 def _at_shutdown(self):
791 791 """Actions taken at shutdown by the kernel, called by python's atexit.
792 792 """
793 793 # io.rprint("Kernel at_shutdown") # dbg
794 794 if self._shutdown_message is not None:
795 795 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
796 796 self.log.debug("%s", self._shutdown_message)
797 797 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
798 798
799 799 #-----------------------------------------------------------------------------
800 800 # Aliases and Flags for the IPKernelApp
801 801 #-----------------------------------------------------------------------------
802 802
803 803 flags = dict(kernel_flags)
804 804 flags.update(shell_flags)
805 805
806 806 addflag = lambda *args: flags.update(boolean_flag(*args))
807 807
808 808 flags['pylab'] = (
809 809 {'IPKernelApp' : {'pylab' : 'auto'}},
810 810 """Pre-load matplotlib and numpy for interactive use with
811 811 the default matplotlib backend."""
812 812 )
813 813
814 814 aliases = dict(kernel_aliases)
815 815 aliases.update(shell_aliases)
816 816
817 817 #-----------------------------------------------------------------------------
818 818 # The IPKernelApp class
819 819 #-----------------------------------------------------------------------------
820 820
821 821 class IPKernelApp(KernelApp, InteractiveShellApp):
822 822 name = 'ipkernel'
823 823
824 824 aliases = Dict(aliases)
825 825 flags = Dict(flags)
826 826 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
827 827
828 828 @catch_config_error
829 829 def initialize(self, argv=None):
830 830 super(IPKernelApp, self).initialize(argv)
831 831 self.init_path()
832 832 self.init_shell()
833 833 self.init_gui_pylab()
834 834 self.init_extensions()
835 835 self.init_code()
836 836
837 837 def init_kernel(self):
838 838
839 839 shell_stream = ZMQStream(self.shell_socket)
840 840
841 841 kernel = Kernel(config=self.config, session=self.session,
842 842 shell_streams=[shell_stream],
843 843 iopub_socket=self.iopub_socket,
844 844 stdin_socket=self.stdin_socket,
845 845 log=self.log,
846 846 profile_dir=self.profile_dir,
847 847 )
848 848 self.kernel = kernel
849 849 kernel.record_ports(self.ports)
850 850 shell = kernel.shell
851 851
852 852 def init_gui_pylab(self):
853 853 """Enable GUI event loop integration, taking pylab into account."""
854 854
855 855 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
856 856 # to ensure that any exception is printed straight to stderr.
857 857 # Normally _showtraceback associates the reply with an execution,
858 858 # which means frontends will never draw it, as this exception
859 859 # is not associated with any execute request.
860 860
861 861 shell = self.shell
862 862 _showtraceback = shell._showtraceback
863 863 try:
864 864 # replace pyerr-sending traceback with stderr
865 865 def print_tb(etype, evalue, stb):
866 866 print ("GUI event loop or pylab initialization failed",
867 867 file=io.stderr)
868 868 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
869 869 shell._showtraceback = print_tb
870 870 InteractiveShellApp.init_gui_pylab(self)
871 871 finally:
872 872 shell._showtraceback = _showtraceback
873 873
874 874 def init_shell(self):
875 875 self.shell = self.kernel.shell
876 876 self.shell.configurables.append(self)
877 877
878 878
879 879 #-----------------------------------------------------------------------------
880 880 # Kernel main and launch functions
881 881 #-----------------------------------------------------------------------------
882 882
883 883 def launch_kernel(*args, **kwargs):
884 884 """Launches a localhost IPython kernel, binding to the specified ports.
885 885
886 886 This function simply calls entry_point.base_launch_kernel with the right
887 887 first command to start an ipkernel. See base_launch_kernel for arguments.
888 888
889 889 Returns
890 890 -------
891 891 A tuple of form:
892 892 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
893 893 where kernel_process is a Popen object and the ports are integers.
894 894 """
895 895 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
896 896 *args, **kwargs)
897 897
898 898
899 899 def embed_kernel(module=None, local_ns=None, **kwargs):
900 900 """Embed and start an IPython kernel in a given scope.
901 901
902 902 Parameters
903 903 ----------
904 904 module : ModuleType, optional
905 905 The module to load into IPython globals (default: caller)
906 906 local_ns : dict, optional
907 907 The namespace to load into IPython user namespace (default: caller)
908 908
909 909 kwargs : various, optional
910 910 Further keyword args are relayed to the KernelApp constructor,
911 911 allowing configuration of the Kernel. Will only have an effect
912 912 on the first embed_kernel call for a given process.
913 913
914 914 """
915 915 # get the app if it exists, or set it up if it doesn't
916 916 if IPKernelApp.initialized():
917 917 app = IPKernelApp.instance()
918 918 else:
919 919 app = IPKernelApp.instance(**kwargs)
920 920 app.initialize([])
921 921 # Undo unnecessary sys module mangling from init_sys_modules.
922 922 # This would not be necessary if we could prevent it
923 923 # in the first place by using a different InteractiveShell
924 924 # subclass, as in the regular embed case.
925 925 main = app.kernel.shell._orig_sys_modules_main_mod
926 926 if main is not None:
927 927 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
928 928
929 929 # load the calling scope if not given
930 930 (caller_module, caller_locals) = extract_module_locals(1)
931 931 if module is None:
932 932 module = caller_module
933 933 if local_ns is None:
934 934 local_ns = caller_locals
935 935
936 936 app.kernel.user_module = module
937 937 app.kernel.user_ns = local_ns
938 938 app.shell.set_completer_frame()
939 939 app.start()
940 940
941 941 def main():
942 942 """Run an IPKernel as an application"""
943 943 app = IPKernelApp.instance()
944 944 app.initialize()
945 945 app.start()
946 946
947 947
948 948 if __name__ == '__main__':
949 949 main()
@@ -1,1048 +1,1048 b''
1 1 """Base classes to manage the interaction with a running kernel.
2 2
3 3 TODO
4 4 * Create logger to handle debugging and console messages.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2011 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 # Standard library imports.
19 19 import atexit
20 20 import errno
21 21 import json
22 22 from subprocess import Popen
23 23 import os
24 24 import signal
25 25 import sys
26 26 from threading import Thread
27 27 import time
28 28
29 29 # System library imports.
30 30 import zmq
31 31 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
32 32 # during garbage collection of threads at exit:
33 33 from zmq import ZMQError
34 34 from zmq.eventloop import ioloop, zmqstream
35 35
36 36 # Local imports.
37 37 from IPython.config.loader import Config
38 38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
39 39 from IPython.utils.traitlets import (
40 40 HasTraits, Any, Instance, Type, Unicode, Integer, Bool, CaselessStrEnum
41 41 )
42 42 from IPython.utils.py3compat import str_to_bytes
43 43 from IPython.zmq.entry_point import write_connection_file
44 44 from session import Session
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Constants and exceptions
48 48 #-----------------------------------------------------------------------------
49 49
50 50 class InvalidPortNumber(Exception):
51 51 pass
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Utility functions
55 55 #-----------------------------------------------------------------------------
56 56
57 57 # some utilities to validate message structure, these might get moved elsewhere
58 58 # if they prove to have more generic utility
59 59
60 60 def validate_string_list(lst):
61 61 """Validate that the input is a list of strings.
62 62
63 63 Raises ValueError if not."""
64 64 if not isinstance(lst, list):
65 65 raise ValueError('input %r must be a list' % lst)
66 66 for x in lst:
67 67 if not isinstance(x, basestring):
68 68 raise ValueError('element %r in list must be a string' % x)
69 69
70 70
71 71 def validate_string_dict(dct):
72 72 """Validate that the input is a dict with string keys and values.
73 73
74 74 Raises ValueError if not."""
75 75 for k,v in dct.iteritems():
76 76 if not isinstance(k, basestring):
77 77 raise ValueError('key %r in dict must be a string' % k)
78 78 if not isinstance(v, basestring):
79 79 raise ValueError('value %r in dict must be a string' % v)
80 80
81 81
82 82 #-----------------------------------------------------------------------------
83 83 # ZMQ Socket Channel classes
84 84 #-----------------------------------------------------------------------------
85 85
86 86 class ZMQSocketChannel(Thread):
87 87 """The base class for the channels that use ZMQ sockets.
88 88 """
89 89 context = None
90 90 session = None
91 91 socket = None
92 92 ioloop = None
93 93 stream = None
94 94 _address = None
95 95 _exiting = False
96 96
97 97 def __init__(self, context, session, address):
98 98 """Create a channel
99 99
100 100 Parameters
101 101 ----------
102 102 context : :class:`zmq.Context`
103 103 The ZMQ context to use.
104 104 session : :class:`session.Session`
105 105 The session to use.
106 106 address : zmq url
107 107 Standard (ip, port) tuple that the kernel is listening on.
108 108 """
109 109 super(ZMQSocketChannel, self).__init__()
110 110 self.daemon = True
111 111
112 112 self.context = context
113 113 self.session = session
114 114 if isinstance(address, tuple):
115 115 if address[1] == 0:
116 116 message = 'The port number for a channel cannot be 0.'
117 117 raise InvalidPortNumber(message)
118 118 address = "tcp://%s:%i" % address
119 119 self._address = address
120 120 atexit.register(self._notice_exit)
121 121
122 122 def _notice_exit(self):
123 123 self._exiting = True
124 124
125 125 def _run_loop(self):
126 126 """Run my loop, ignoring EINTR events in the poller"""
127 127 while True:
128 128 try:
129 129 self.ioloop.start()
130 130 except ZMQError as e:
131 131 if e.errno == errno.EINTR:
132 132 continue
133 133 else:
134 134 raise
135 135 except Exception:
136 136 if self._exiting:
137 137 break
138 138 else:
139 139 raise
140 140 else:
141 141 break
142 142
143 143 def stop(self):
144 144 """Stop the channel's activity.
145 145
146 146 This calls :method:`Thread.join` and returns when the thread
147 147 terminates. :class:`RuntimeError` will be raised if
148 148 :method:`self.start` is called again.
149 149 """
150 150 self.join()
151 151
152 152 @property
153 153 def address(self):
154 154 """Get the channel's address as a zmq url string ('tcp://127.0.0.1:5555').
155 155 """
156 156 return self._address
157 157
158 158 def _queue_send(self, msg):
159 159 """Queue a message to be sent from the IOLoop's thread.
160 160
161 161 Parameters
162 162 ----------
163 163 msg : message to send
164 164
165 165 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
166 166 thread control of the action.
167 167 """
168 168 def thread_send():
169 169 self.session.send(self.stream, msg)
170 170 self.ioloop.add_callback(thread_send)
171 171
172 172 def _handle_recv(self, msg):
173 173 """callback for stream.on_recv
174 174
175 175 unpacks message, and calls handlers with it.
176 176 """
177 177 ident,smsg = self.session.feed_identities(msg)
178 178 self.call_handlers(self.session.unserialize(smsg))
179 179
180 180
181 181
182 182 class ShellSocketChannel(ZMQSocketChannel):
183 183 """The DEALER channel for issues request/replies to the kernel.
184 184 """
185 185
186 186 command_queue = None
187 187 # flag for whether execute requests should be allowed to call raw_input:
188 188 allow_stdin = True
189 189
190 190 def __init__(self, context, session, address):
191 191 super(ShellSocketChannel, self).__init__(context, session, address)
192 192 self.ioloop = ioloop.IOLoop()
193 193
194 194 def run(self):
195 195 """The thread's main activity. Call start() instead."""
196 196 self.socket = self.context.socket(zmq.DEALER)
197 197 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
198 198 self.socket.connect(self.address)
199 199 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
200 200 self.stream.on_recv(self._handle_recv)
201 201 self._run_loop()
202 202 try:
203 203 self.socket.close()
204 204 except:
205 205 pass
206 206
207 207 def stop(self):
208 208 self.ioloop.stop()
209 209 super(ShellSocketChannel, self).stop()
210 210
211 211 def call_handlers(self, msg):
212 212 """This method is called in the ioloop thread when a message arrives.
213 213
214 214 Subclasses should override this method to handle incoming messages.
215 215 It is important to remember that this method is called in the thread
216 216 so that some logic must be done to ensure that the application leve
217 217 handlers are called in the application thread.
218 218 """
219 219 raise NotImplementedError('call_handlers must be defined in a subclass.')
220 220
221 221 def execute(self, code, silent=False, store_history=True,
222 222 user_variables=None, user_expressions=None, allow_stdin=None):
223 223 """Execute code in the kernel.
224 224
225 225 Parameters
226 226 ----------
227 227 code : str
228 228 A string of Python code.
229 229
230 230 silent : bool, optional (default False)
231 231 If set, the kernel will execute the code as quietly possible, and
232 232 will force store_history to be False.
233 233
234 234 store_history : bool, optional (default True)
235 235 If set, the kernel will store command history. This is forced
236 236 to be False if silent is True.
237 237
238 238 user_variables : list, optional
239 239 A list of variable names to pull from the user's namespace. They
240 240 will come back as a dict with these names as keys and their
241 241 :func:`repr` as values.
242 242
243 243 user_expressions : dict, optional
244 244 A dict mapping names to expressions to be evaluated in the user's
245 245 dict. The expression values are returned as strings formatted using
246 246 :func:`repr`.
247 247
248 248 allow_stdin : bool, optional (default self.allow_stdin)
249 249 Flag for whether the kernel can send stdin requests to frontends.
250 250
251 251 Some frontends (e.g. the Notebook) do not support stdin requests.
252 252 If raw_input is called from code executed from such a frontend, a
253 253 StdinNotImplementedError will be raised.
254 254
255 255 Returns
256 256 -------
257 257 The msg_id of the message sent.
258 258 """
259 259 if user_variables is None:
260 260 user_variables = []
261 261 if user_expressions is None:
262 262 user_expressions = {}
263 263 if allow_stdin is None:
264 264 allow_stdin = self.allow_stdin
265 265
266 266
267 267 # Don't waste network traffic if inputs are invalid
268 268 if not isinstance(code, basestring):
269 269 raise ValueError('code %r must be a string' % code)
270 270 validate_string_list(user_variables)
271 271 validate_string_dict(user_expressions)
272 272
273 273 # Create class for content/msg creation. Related to, but possibly
274 274 # not in Session.
275 275 content = dict(code=code, silent=silent, store_history=store_history,
276 276 user_variables=user_variables,
277 277 user_expressions=user_expressions,
278 278 allow_stdin=allow_stdin,
279 279 )
280 280 msg = self.session.msg('execute_request', content)
281 281 self._queue_send(msg)
282 282 return msg['header']['msg_id']
283 283
284 284 def complete(self, text, line, cursor_pos, block=None):
285 285 """Tab complete text in the kernel's namespace.
286 286
287 287 Parameters
288 288 ----------
289 289 text : str
290 290 The text to complete.
291 291 line : str
292 292 The full line of text that is the surrounding context for the
293 293 text to complete.
294 294 cursor_pos : int
295 295 The position of the cursor in the line where the completion was
296 296 requested.
297 297 block : str, optional
298 298 The full block of code in which the completion is being requested.
299 299
300 300 Returns
301 301 -------
302 302 The msg_id of the message sent.
303 303 """
304 304 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
305 305 msg = self.session.msg('complete_request', content)
306 306 self._queue_send(msg)
307 307 return msg['header']['msg_id']
308 308
309 309 def object_info(self, oname, detail_level=0):
310 310 """Get metadata information about an object.
311 311
312 312 Parameters
313 313 ----------
314 314 oname : str
315 315 A string specifying the object name.
316 316 detail_level : int, optional
317 317 The level of detail for the introspection (0-2)
318 318
319 319 Returns
320 320 -------
321 321 The msg_id of the message sent.
322 322 """
323 323 content = dict(oname=oname, detail_level=detail_level)
324 324 msg = self.session.msg('object_info_request', content)
325 325 self._queue_send(msg)
326 326 return msg['header']['msg_id']
327 327
328 328 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
329 329 """Get entries from the history list.
330 330
331 331 Parameters
332 332 ----------
333 333 raw : bool
334 334 If True, return the raw input.
335 335 output : bool
336 336 If True, then return the output as well.
337 337 hist_access_type : str
338 338 'range' (fill in session, start and stop params), 'tail' (fill in n)
339 339 or 'search' (fill in pattern param).
340 340
341 341 session : int
342 342 For a range request, the session from which to get lines. Session
343 343 numbers are positive integers; negative ones count back from the
344 344 current session.
345 345 start : int
346 346 The first line number of a history range.
347 347 stop : int
348 348 The final (excluded) line number of a history range.
349 349
350 350 n : int
351 351 The number of lines of history to get for a tail request.
352 352
353 353 pattern : str
354 354 The glob-syntax pattern for a search request.
355 355
356 356 Returns
357 357 -------
358 358 The msg_id of the message sent.
359 359 """
360 360 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
361 361 **kwargs)
362 362 msg = self.session.msg('history_request', content)
363 363 self._queue_send(msg)
364 364 return msg['header']['msg_id']
365 365
366 366 def version(self):
367 367 """Request kernel version info."""
368 msg = self.session.msg('version_request')
368 msg = self.session.msg('kernel_info_request')
369 369 self._queue_send(msg)
370 370 return msg['header']['msg_id']
371 371
372 372 def shutdown(self, restart=False):
373 373 """Request an immediate kernel shutdown.
374 374
375 375 Upon receipt of the (empty) reply, client code can safely assume that
376 376 the kernel has shut down and it's safe to forcefully terminate it if
377 377 it's still alive.
378 378
379 379 The kernel will send the reply via a function registered with Python's
380 380 atexit module, ensuring it's truly done as the kernel is done with all
381 381 normal operation.
382 382 """
383 383 # Send quit message to kernel. Once we implement kernel-side setattr,
384 384 # this should probably be done that way, but for now this will do.
385 385 msg = self.session.msg('shutdown_request', {'restart':restart})
386 386 self._queue_send(msg)
387 387 return msg['header']['msg_id']
388 388
389 389
390 390
391 391 class SubSocketChannel(ZMQSocketChannel):
392 392 """The SUB channel which listens for messages that the kernel publishes.
393 393 """
394 394
395 395 def __init__(self, context, session, address):
396 396 super(SubSocketChannel, self).__init__(context, session, address)
397 397 self.ioloop = ioloop.IOLoop()
398 398
399 399 def run(self):
400 400 """The thread's main activity. Call start() instead."""
401 401 self.socket = self.context.socket(zmq.SUB)
402 402 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
403 403 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
404 404 self.socket.connect(self.address)
405 405 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
406 406 self.stream.on_recv(self._handle_recv)
407 407 self._run_loop()
408 408 try:
409 409 self.socket.close()
410 410 except:
411 411 pass
412 412
413 413 def stop(self):
414 414 self.ioloop.stop()
415 415 super(SubSocketChannel, self).stop()
416 416
417 417 def call_handlers(self, msg):
418 418 """This method is called in the ioloop thread when a message arrives.
419 419
420 420 Subclasses should override this method to handle incoming messages.
421 421 It is important to remember that this method is called in the thread
422 422 so that some logic must be done to ensure that the application leve
423 423 handlers are called in the application thread.
424 424 """
425 425 raise NotImplementedError('call_handlers must be defined in a subclass.')
426 426
427 427 def flush(self, timeout=1.0):
428 428 """Immediately processes all pending messages on the SUB channel.
429 429
430 430 Callers should use this method to ensure that :method:`call_handlers`
431 431 has been called for all messages that have been received on the
432 432 0MQ SUB socket of this channel.
433 433
434 434 This method is thread safe.
435 435
436 436 Parameters
437 437 ----------
438 438 timeout : float, optional
439 439 The maximum amount of time to spend flushing, in seconds. The
440 440 default is one second.
441 441 """
442 442 # We do the IOLoop callback process twice to ensure that the IOLoop
443 443 # gets to perform at least one full poll.
444 444 stop_time = time.time() + timeout
445 445 for i in xrange(2):
446 446 self._flushed = False
447 447 self.ioloop.add_callback(self._flush)
448 448 while not self._flushed and time.time() < stop_time:
449 449 time.sleep(0.01)
450 450
451 451 def _flush(self):
452 452 """Callback for :method:`self.flush`."""
453 453 self.stream.flush()
454 454 self._flushed = True
455 455
456 456
457 457 class StdInSocketChannel(ZMQSocketChannel):
458 458 """A reply channel to handle raw_input requests that the kernel makes."""
459 459
460 460 msg_queue = None
461 461
462 462 def __init__(self, context, session, address):
463 463 super(StdInSocketChannel, self).__init__(context, session, address)
464 464 self.ioloop = ioloop.IOLoop()
465 465
466 466 def run(self):
467 467 """The thread's main activity. Call start() instead."""
468 468 self.socket = self.context.socket(zmq.DEALER)
469 469 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
470 470 self.socket.connect(self.address)
471 471 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
472 472 self.stream.on_recv(self._handle_recv)
473 473 self._run_loop()
474 474 try:
475 475 self.socket.close()
476 476 except:
477 477 pass
478 478
479 479
480 480 def stop(self):
481 481 self.ioloop.stop()
482 482 super(StdInSocketChannel, self).stop()
483 483
484 484 def call_handlers(self, msg):
485 485 """This method is called in the ioloop thread when a message arrives.
486 486
487 487 Subclasses should override this method to handle incoming messages.
488 488 It is important to remember that this method is called in the thread
489 489 so that some logic must be done to ensure that the application leve
490 490 handlers are called in the application thread.
491 491 """
492 492 raise NotImplementedError('call_handlers must be defined in a subclass.')
493 493
494 494 def input(self, string):
495 495 """Send a string of raw input to the kernel."""
496 496 content = dict(value=string)
497 497 msg = self.session.msg('input_reply', content)
498 498 self._queue_send(msg)
499 499
500 500
501 501 class HBSocketChannel(ZMQSocketChannel):
502 502 """The heartbeat channel which monitors the kernel heartbeat.
503 503
504 504 Note that the heartbeat channel is paused by default. As long as you start
505 505 this channel, the kernel manager will ensure that it is paused and un-paused
506 506 as appropriate.
507 507 """
508 508
509 509 time_to_dead = 3.0
510 510 socket = None
511 511 poller = None
512 512 _running = None
513 513 _pause = None
514 514 _beating = None
515 515
516 516 def __init__(self, context, session, address):
517 517 super(HBSocketChannel, self).__init__(context, session, address)
518 518 self._running = False
519 519 self._pause =True
520 520 self.poller = zmq.Poller()
521 521
522 522 def _create_socket(self):
523 523 if self.socket is not None:
524 524 # close previous socket, before opening a new one
525 525 self.poller.unregister(self.socket)
526 526 self.socket.close()
527 527 self.socket = self.context.socket(zmq.REQ)
528 528 self.socket.setsockopt(zmq.LINGER, 0)
529 529 self.socket.connect(self.address)
530 530
531 531 self.poller.register(self.socket, zmq.POLLIN)
532 532
533 533 def _poll(self, start_time):
534 534 """poll for heartbeat replies until we reach self.time_to_dead
535 535
536 536 Ignores interrupts, and returns the result of poll(), which
537 537 will be an empty list if no messages arrived before the timeout,
538 538 or the event tuple if there is a message to receive.
539 539 """
540 540
541 541 until_dead = self.time_to_dead - (time.time() - start_time)
542 542 # ensure poll at least once
543 543 until_dead = max(until_dead, 1e-3)
544 544 events = []
545 545 while True:
546 546 try:
547 547 events = self.poller.poll(1000 * until_dead)
548 548 except ZMQError as e:
549 549 if e.errno == errno.EINTR:
550 550 # ignore interrupts during heartbeat
551 551 # this may never actually happen
552 552 until_dead = self.time_to_dead - (time.time() - start_time)
553 553 until_dead = max(until_dead, 1e-3)
554 554 pass
555 555 else:
556 556 raise
557 557 except Exception:
558 558 if self._exiting:
559 559 break
560 560 else:
561 561 raise
562 562 else:
563 563 break
564 564 return events
565 565
566 566 def run(self):
567 567 """The thread's main activity. Call start() instead."""
568 568 self._create_socket()
569 569 self._running = True
570 570 self._beating = True
571 571
572 572 while self._running:
573 573 if self._pause:
574 574 # just sleep, and skip the rest of the loop
575 575 time.sleep(self.time_to_dead)
576 576 continue
577 577
578 578 since_last_heartbeat = 0.0
579 579 # io.rprint('Ping from HB channel') # dbg
580 580 # no need to catch EFSM here, because the previous event was
581 581 # either a recv or connect, which cannot be followed by EFSM
582 582 self.socket.send(b'ping')
583 583 request_time = time.time()
584 584 ready = self._poll(request_time)
585 585 if ready:
586 586 self._beating = True
587 587 # the poll above guarantees we have something to recv
588 588 self.socket.recv()
589 589 # sleep the remainder of the cycle
590 590 remainder = self.time_to_dead - (time.time() - request_time)
591 591 if remainder > 0:
592 592 time.sleep(remainder)
593 593 continue
594 594 else:
595 595 # nothing was received within the time limit, signal heart failure
596 596 self._beating = False
597 597 since_last_heartbeat = time.time() - request_time
598 598 self.call_handlers(since_last_heartbeat)
599 599 # and close/reopen the socket, because the REQ/REP cycle has been broken
600 600 self._create_socket()
601 601 continue
602 602 try:
603 603 self.socket.close()
604 604 except:
605 605 pass
606 606
607 607 def pause(self):
608 608 """Pause the heartbeat."""
609 609 self._pause = True
610 610
611 611 def unpause(self):
612 612 """Unpause the heartbeat."""
613 613 self._pause = False
614 614
615 615 def is_beating(self):
616 616 """Is the heartbeat running and responsive (and not paused)."""
617 617 if self.is_alive() and not self._pause and self._beating:
618 618 return True
619 619 else:
620 620 return False
621 621
622 622 def stop(self):
623 623 self._running = False
624 624 super(HBSocketChannel, self).stop()
625 625
626 626 def call_handlers(self, since_last_heartbeat):
627 627 """This method is called in the ioloop thread when a message arrives.
628 628
629 629 Subclasses should override this method to handle incoming messages.
630 630 It is important to remember that this method is called in the thread
631 631 so that some logic must be done to ensure that the application level
632 632 handlers are called in the application thread.
633 633 """
634 634 raise NotImplementedError('call_handlers must be defined in a subclass.')
635 635
636 636
637 637 #-----------------------------------------------------------------------------
638 638 # Main kernel manager class
639 639 #-----------------------------------------------------------------------------
640 640
641 641 class KernelManager(HasTraits):
642 642 """ Manages a kernel for a frontend.
643 643
644 644 The SUB channel is for the frontend to receive messages published by the
645 645 kernel.
646 646
647 647 The REQ channel is for the frontend to make requests of the kernel.
648 648
649 649 The REP channel is for the kernel to request stdin (raw_input) from the
650 650 frontend.
651 651 """
652 652 # config object for passing to child configurables
653 653 config = Instance(Config)
654 654
655 655 # The PyZMQ Context to use for communication with the kernel.
656 656 context = Instance(zmq.Context)
657 657 def _context_default(self):
658 658 return zmq.Context.instance()
659 659
660 660 # The Session to use for communication with the kernel.
661 661 session = Instance(Session)
662 662
663 663 # The kernel process with which the KernelManager is communicating.
664 664 kernel = Instance(Popen)
665 665
666 666 # The addresses for the communication channels.
667 667 connection_file = Unicode('')
668 668
669 669 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp')
670 670
671 671
672 672 ip = Unicode(LOCALHOST)
673 673 def _ip_changed(self, name, old, new):
674 674 if new == '*':
675 675 self.ip = '0.0.0.0'
676 676 shell_port = Integer(0)
677 677 iopub_port = Integer(0)
678 678 stdin_port = Integer(0)
679 679 hb_port = Integer(0)
680 680
681 681 # The classes to use for the various channels.
682 682 shell_channel_class = Type(ShellSocketChannel)
683 683 sub_channel_class = Type(SubSocketChannel)
684 684 stdin_channel_class = Type(StdInSocketChannel)
685 685 hb_channel_class = Type(HBSocketChannel)
686 686
687 687 # Protected traits.
688 688 _launch_args = Any
689 689 _shell_channel = Any
690 690 _sub_channel = Any
691 691 _stdin_channel = Any
692 692 _hb_channel = Any
693 693 _connection_file_written=Bool(False)
694 694
695 695 def __init__(self, **kwargs):
696 696 super(KernelManager, self).__init__(**kwargs)
697 697 if self.session is None:
698 698 self.session = Session(config=self.config)
699 699
700 700 def __del__(self):
701 701 self.cleanup_connection_file()
702 702
703 703
704 704 #--------------------------------------------------------------------------
705 705 # Channel management methods:
706 706 #--------------------------------------------------------------------------
707 707
708 708 def start_channels(self, shell=True, sub=True, stdin=True, hb=True):
709 709 """Starts the channels for this kernel.
710 710
711 711 This will create the channels if they do not exist and then start
712 712 them. If port numbers of 0 are being used (random ports) then you
713 713 must first call :method:`start_kernel`. If the channels have been
714 714 stopped and you call this, :class:`RuntimeError` will be raised.
715 715 """
716 716 if shell:
717 717 self.shell_channel.start()
718 718 if sub:
719 719 self.sub_channel.start()
720 720 if stdin:
721 721 self.stdin_channel.start()
722 722 self.shell_channel.allow_stdin = True
723 723 else:
724 724 self.shell_channel.allow_stdin = False
725 725 if hb:
726 726 self.hb_channel.start()
727 727
728 728 def stop_channels(self):
729 729 """Stops all the running channels for this kernel.
730 730 """
731 731 if self.shell_channel.is_alive():
732 732 self.shell_channel.stop()
733 733 if self.sub_channel.is_alive():
734 734 self.sub_channel.stop()
735 735 if self.stdin_channel.is_alive():
736 736 self.stdin_channel.stop()
737 737 if self.hb_channel.is_alive():
738 738 self.hb_channel.stop()
739 739
740 740 @property
741 741 def channels_running(self):
742 742 """Are any of the channels created and running?"""
743 743 return (self.shell_channel.is_alive() or self.sub_channel.is_alive() or
744 744 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
745 745
746 746 #--------------------------------------------------------------------------
747 747 # Kernel process management methods:
748 748 #--------------------------------------------------------------------------
749 749
750 750 def cleanup_connection_file(self):
751 751 """cleanup connection file *if we wrote it*
752 752
753 753 Will not raise if the connection file was already removed somehow.
754 754 """
755 755 if self._connection_file_written:
756 756 # cleanup connection files on full shutdown of kernel we started
757 757 self._connection_file_written = False
758 758 try:
759 759 os.remove(self.connection_file)
760 760 except (IOError, OSError):
761 761 pass
762 762
763 763 self._cleanup_ipc_files()
764 764
765 765 def _cleanup_ipc_files(self):
766 766 """cleanup ipc files if we wrote them"""
767 767 if self.transport != 'ipc':
768 768 return
769 769 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
770 770 ipcfile = "%s-%i" % (self.ip, port)
771 771 try:
772 772 os.remove(ipcfile)
773 773 except (IOError, OSError):
774 774 pass
775 775
776 776 def load_connection_file(self):
777 777 """load connection info from JSON dict in self.connection_file"""
778 778 with open(self.connection_file) as f:
779 779 cfg = json.loads(f.read())
780 780
781 781 from pprint import pprint
782 782 pprint(cfg)
783 783 self.transport = cfg.get('transport', 'tcp')
784 784 self.ip = cfg['ip']
785 785 self.shell_port = cfg['shell_port']
786 786 self.stdin_port = cfg['stdin_port']
787 787 self.iopub_port = cfg['iopub_port']
788 788 self.hb_port = cfg['hb_port']
789 789 self.session.key = str_to_bytes(cfg['key'])
790 790
791 791 def write_connection_file(self):
792 792 """write connection info to JSON dict in self.connection_file"""
793 793 if self._connection_file_written:
794 794 return
795 795 self.connection_file,cfg = write_connection_file(self.connection_file,
796 796 transport=self.transport, ip=self.ip, key=self.session.key,
797 797 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
798 798 shell_port=self.shell_port, hb_port=self.hb_port)
799 799 # write_connection_file also sets default ports:
800 800 self.shell_port = cfg['shell_port']
801 801 self.stdin_port = cfg['stdin_port']
802 802 self.iopub_port = cfg['iopub_port']
803 803 self.hb_port = cfg['hb_port']
804 804
805 805 self._connection_file_written = True
806 806
807 807 def start_kernel(self, **kw):
808 808 """Starts a kernel process and configures the manager to use it.
809 809
810 810 If random ports (port=0) are being used, this method must be called
811 811 before the channels are created.
812 812
813 813 Parameters:
814 814 -----------
815 815 launcher : callable, optional (default None)
816 816 A custom function for launching the kernel process (generally a
817 817 wrapper around ``entry_point.base_launch_kernel``). In most cases,
818 818 it should not be necessary to use this parameter.
819 819
820 820 **kw : optional
821 821 See respective options for IPython and Python kernels.
822 822 """
823 823 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
824 824 raise RuntimeError("Can only launch a kernel on a local interface. "
825 825 "Make sure that the '*_address' attributes are "
826 826 "configured properly. "
827 827 "Currently valid addresses are: %s"%LOCAL_IPS
828 828 )
829 829
830 830 # write connection file / get default ports
831 831 self.write_connection_file()
832 832
833 833 self._launch_args = kw.copy()
834 834 launch_kernel = kw.pop('launcher', None)
835 835 if launch_kernel is None:
836 836 from ipkernel import launch_kernel
837 837 self.kernel = launch_kernel(fname=self.connection_file, **kw)
838 838
839 839 def shutdown_kernel(self, restart=False):
840 840 """ Attempts to the stop the kernel process cleanly.
841 841
842 842 If the kernel cannot be stopped and the kernel is local, it is killed.
843 843 """
844 844 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
845 845 if sys.platform == 'win32':
846 846 self.kill_kernel()
847 847 return
848 848
849 849 # Pause the heart beat channel if it exists.
850 850 if self._hb_channel is not None:
851 851 self._hb_channel.pause()
852 852
853 853 # Don't send any additional kernel kill messages immediately, to give
854 854 # the kernel a chance to properly execute shutdown actions. Wait for at
855 855 # most 1s, checking every 0.1s.
856 856 self.shell_channel.shutdown(restart=restart)
857 857 for i in range(10):
858 858 if self.is_alive:
859 859 time.sleep(0.1)
860 860 else:
861 861 break
862 862 else:
863 863 # OK, we've waited long enough.
864 864 if self.has_kernel:
865 865 self.kill_kernel()
866 866
867 867 if not restart and self._connection_file_written:
868 868 # cleanup connection files on full shutdown of kernel we started
869 869 self._connection_file_written = False
870 870 try:
871 871 os.remove(self.connection_file)
872 872 except IOError:
873 873 pass
874 874
875 875 def restart_kernel(self, now=False, **kw):
876 876 """Restarts a kernel with the arguments that were used to launch it.
877 877
878 878 If the old kernel was launched with random ports, the same ports will be
879 879 used for the new kernel.
880 880
881 881 Parameters
882 882 ----------
883 883 now : bool, optional
884 884 If True, the kernel is forcefully restarted *immediately*, without
885 885 having a chance to do any cleanup action. Otherwise the kernel is
886 886 given 1s to clean up before a forceful restart is issued.
887 887
888 888 In all cases the kernel is restarted, the only difference is whether
889 889 it is given a chance to perform a clean shutdown or not.
890 890
891 891 **kw : optional
892 892 Any options specified here will replace those used to launch the
893 893 kernel.
894 894 """
895 895 if self._launch_args is None:
896 896 raise RuntimeError("Cannot restart the kernel. "
897 897 "No previous call to 'start_kernel'.")
898 898 else:
899 899 # Stop currently running kernel.
900 900 if self.has_kernel:
901 901 if now:
902 902 self.kill_kernel()
903 903 else:
904 904 self.shutdown_kernel(restart=True)
905 905
906 906 # Start new kernel.
907 907 self._launch_args.update(kw)
908 908 self.start_kernel(**self._launch_args)
909 909
910 910 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
911 911 # unless there is some delay here.
912 912 if sys.platform == 'win32':
913 913 time.sleep(0.2)
914 914
915 915 @property
916 916 def has_kernel(self):
917 917 """Returns whether a kernel process has been specified for the kernel
918 918 manager.
919 919 """
920 920 return self.kernel is not None
921 921
922 922 def kill_kernel(self):
923 923 """ Kill the running kernel.
924 924
925 925 This method blocks until the kernel process has terminated.
926 926 """
927 927 if self.has_kernel:
928 928 # Pause the heart beat channel if it exists.
929 929 if self._hb_channel is not None:
930 930 self._hb_channel.pause()
931 931
932 932 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
933 933 # TerminateProcess() on Win32).
934 934 try:
935 935 self.kernel.kill()
936 936 except OSError as e:
937 937 # In Windows, we will get an Access Denied error if the process
938 938 # has already terminated. Ignore it.
939 939 if sys.platform == 'win32':
940 940 if e.winerror != 5:
941 941 raise
942 942 # On Unix, we may get an ESRCH error if the process has already
943 943 # terminated. Ignore it.
944 944 else:
945 945 from errno import ESRCH
946 946 if e.errno != ESRCH:
947 947 raise
948 948
949 949 # Block until the kernel terminates.
950 950 self.kernel.wait()
951 951 self.kernel = None
952 952 else:
953 953 raise RuntimeError("Cannot kill kernel. No kernel is running!")
954 954
955 955 def interrupt_kernel(self):
956 956 """ Interrupts the kernel.
957 957
958 958 Unlike ``signal_kernel``, this operation is well supported on all
959 959 platforms.
960 960 """
961 961 if self.has_kernel:
962 962 if sys.platform == 'win32':
963 963 from parentpoller import ParentPollerWindows as Poller
964 964 Poller.send_interrupt(self.kernel.win32_interrupt_event)
965 965 else:
966 966 self.kernel.send_signal(signal.SIGINT)
967 967 else:
968 968 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
969 969
970 970 def signal_kernel(self, signum):
971 971 """ Sends a signal to the kernel.
972 972
973 973 Note that since only SIGTERM is supported on Windows, this function is
974 974 only useful on Unix systems.
975 975 """
976 976 if self.has_kernel:
977 977 self.kernel.send_signal(signum)
978 978 else:
979 979 raise RuntimeError("Cannot signal kernel. No kernel is running!")
980 980
981 981 @property
982 982 def is_alive(self):
983 983 """Is the kernel process still running?"""
984 984 if self.has_kernel:
985 985 if self.kernel.poll() is None:
986 986 return True
987 987 else:
988 988 return False
989 989 elif self._hb_channel is not None:
990 990 # We didn't start the kernel with this KernelManager so we
991 991 # use the heartbeat.
992 992 return self._hb_channel.is_beating()
993 993 else:
994 994 # no heartbeat and not local, we can't tell if it's running,
995 995 # so naively return True
996 996 return True
997 997
998 998 #--------------------------------------------------------------------------
999 999 # Channels used for communication with the kernel:
1000 1000 #--------------------------------------------------------------------------
1001 1001
1002 1002 def _make_url(self, port):
1003 1003 """make a zmq url with a port"""
1004 1004 if self.transport == 'tcp':
1005 1005 return "tcp://%s:%i" % (self.ip, port)
1006 1006 else:
1007 1007 return "%s://%s-%s" % (self.transport, self.ip, port)
1008 1008
1009 1009 @property
1010 1010 def shell_channel(self):
1011 1011 """Get the REQ socket channel object to make requests of the kernel."""
1012 1012 if self._shell_channel is None:
1013 1013 self._shell_channel = self.shell_channel_class(self.context,
1014 1014 self.session,
1015 1015 self._make_url(self.shell_port),
1016 1016 )
1017 1017 return self._shell_channel
1018 1018
1019 1019 @property
1020 1020 def sub_channel(self):
1021 1021 """Get the SUB socket channel object."""
1022 1022 if self._sub_channel is None:
1023 1023 self._sub_channel = self.sub_channel_class(self.context,
1024 1024 self.session,
1025 1025 self._make_url(self.iopub_port),
1026 1026 )
1027 1027 return self._sub_channel
1028 1028
1029 1029 @property
1030 1030 def stdin_channel(self):
1031 1031 """Get the REP socket channel object to handle stdin (raw_input)."""
1032 1032 if self._stdin_channel is None:
1033 1033 self._stdin_channel = self.stdin_channel_class(self.context,
1034 1034 self.session,
1035 1035 self._make_url(self.stdin_port),
1036 1036 )
1037 1037 return self._stdin_channel
1038 1038
1039 1039 @property
1040 1040 def hb_channel(self):
1041 1041 """Get the heartbeat socket channel object to check that the
1042 1042 kernel is alive."""
1043 1043 if self._hb_channel is None:
1044 1044 self._hb_channel = self.hb_channel_class(self.context,
1045 1045 self.session,
1046 1046 self._make_url(self.hb_port),
1047 1047 )
1048 1048 return self._hb_channel
@@ -1,497 +1,497 b''
1 1 """Test suite for our zeromq-based messaging specification.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2010-2011 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING.txt, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 import re
11 11 import sys
12 12 import time
13 13 from subprocess import PIPE
14 14 from Queue import Empty
15 15
16 16 import nose.tools as nt
17 17
18 18 from ..blockingkernelmanager import BlockingKernelManager
19 19
20 20
21 21 from IPython.testing import decorators as dec
22 22 from IPython.utils import io
23 23 from IPython.utils.traitlets import (
24 24 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
25 25 )
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Global setup and utilities
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def setup():
32 32 global KM
33 33 KM = BlockingKernelManager()
34 34
35 35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
36 36 KM.start_channels()
37 37
38 38 # wait for kernel to be ready
39 39 KM.shell_channel.execute("pass")
40 40 KM.shell_channel.get_msg(block=True, timeout=5)
41 41 flush_channels()
42 42
43 43
44 44 def teardown():
45 45 KM.stop_channels()
46 46 KM.shutdown_kernel()
47 47
48 48
49 49 def flush_channels():
50 50 """flush any messages waiting on the queue"""
51 51 for channel in (KM.shell_channel, KM.sub_channel):
52 52 while True:
53 53 try:
54 54 msg = channel.get_msg(block=True, timeout=0.1)
55 55 except Empty:
56 56 break
57 57 else:
58 58 list(validate_message(msg))
59 59
60 60
61 61 def execute(code='', **kwargs):
62 62 """wrapper for doing common steps for validating an execution request"""
63 63 shell = KM.shell_channel
64 64 sub = KM.sub_channel
65 65
66 66 msg_id = shell.execute(code=code, **kwargs)
67 67 reply = shell.get_msg(timeout=2)
68 68 list(validate_message(reply, 'execute_reply', msg_id))
69 69 busy = sub.get_msg(timeout=2)
70 70 list(validate_message(busy, 'status', msg_id))
71 71 nt.assert_equal(busy['content']['execution_state'], 'busy')
72 72
73 73 if not kwargs.get('silent'):
74 74 pyin = sub.get_msg(timeout=2)
75 75 list(validate_message(pyin, 'pyin', msg_id))
76 76 nt.assert_equal(pyin['content']['code'], code)
77 77
78 78 return msg_id, reply['content']
79 79
80 80 #-----------------------------------------------------------------------------
81 81 # MSG Spec References
82 82 #-----------------------------------------------------------------------------
83 83
84 84
85 85 class Reference(HasTraits):
86 86
87 87 """
88 88 Base class for message spec specification testing.
89 89
90 90 This class is the core of the message specification test. The
91 91 idea is that child classes implement trait attributes for each
92 92 message keys, so that message keys can be tested against these
93 93 traits using :meth:`check` method.
94 94
95 95 """
96 96
97 97 def check(self, d):
98 98 """validate a dict against our traits"""
99 99 for key in self.trait_names():
100 100 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
101 101 # FIXME: always allow None, probably not a good idea
102 102 if d[key] is None:
103 103 continue
104 104 try:
105 105 setattr(self, key, d[key])
106 106 except TraitError as e:
107 107 yield nt.assert_true(False, str(e))
108 108
109 109
110 110 class RMessage(Reference):
111 111 msg_id = Unicode()
112 112 msg_type = Unicode()
113 113 header = Dict()
114 114 parent_header = Dict()
115 115 content = Dict()
116 116
117 117 class RHeader(Reference):
118 118 msg_id = Unicode()
119 119 msg_type = Unicode()
120 120 session = Unicode()
121 121 username = Unicode()
122 122
123 123 class RContent(Reference):
124 124 status = Enum((u'ok', u'error'))
125 125
126 126
127 127 class ExecuteReply(Reference):
128 128 execution_count = Integer()
129 129 status = Enum((u'ok', u'error'))
130 130
131 131 def check(self, d):
132 132 for tst in Reference.check(self, d):
133 133 yield tst
134 134 if d['status'] == 'ok':
135 135 for tst in ExecuteReplyOkay().check(d):
136 136 yield tst
137 137 elif d['status'] == 'error':
138 138 for tst in ExecuteReplyError().check(d):
139 139 yield tst
140 140
141 141
142 142 class ExecuteReplyOkay(Reference):
143 143 payload = List(Dict)
144 144 user_variables = Dict()
145 145 user_expressions = Dict()
146 146
147 147
148 148 class ExecuteReplyError(Reference):
149 149 ename = Unicode()
150 150 evalue = Unicode()
151 151 traceback = List(Unicode)
152 152
153 153
154 154 class OInfoReply(Reference):
155 155 name = Unicode()
156 156 found = Bool()
157 157 ismagic = Bool()
158 158 isalias = Bool()
159 159 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
160 160 type_name = Unicode()
161 161 string_form = Unicode()
162 162 base_class = Unicode()
163 163 length = Integer()
164 164 file = Unicode()
165 165 definition = Unicode()
166 166 argspec = Dict()
167 167 init_definition = Unicode()
168 168 docstring = Unicode()
169 169 init_docstring = Unicode()
170 170 class_docstring = Unicode()
171 171 call_def = Unicode()
172 172 call_docstring = Unicode()
173 173 source = Unicode()
174 174
175 175 def check(self, d):
176 176 for tst in Reference.check(self, d):
177 177 yield tst
178 178 if d['argspec'] is not None:
179 179 for tst in ArgSpec().check(d['argspec']):
180 180 yield tst
181 181
182 182
183 183 class ArgSpec(Reference):
184 184 args = List(Unicode)
185 185 varargs = Unicode()
186 186 varkw = Unicode()
187 187 defaults = List()
188 188
189 189
190 190 class Status(Reference):
191 191 execution_state = Enum((u'busy', u'idle'))
192 192
193 193
194 194 class CompleteReply(Reference):
195 195 matches = List(Unicode)
196 196
197 197
198 198 def Version(num, trait=Integer):
199 199 return List(trait, default_value=[0] * num, minlen=num, maxlen=num)
200 200
201 201
202 class VersionReply(Reference):
202 class KernelInfoReply(Reference):
203 203
204 204 protocol_version = Version(2)
205 205 ipython_version = Version(4, Any)
206 206 language_version = Version(3)
207 207 language = Unicode()
208 208
209 209 def _ipython_version_changed(self, name, old, new):
210 210 for v in new:
211 211 nt.assert_true(
212 212 isinstance(v, int) or isinstance(v, basestring),
213 213 'expected int or string as version component, got {0!r}'
214 214 .format(v))
215 215
216 216
217 217 # IOPub messages
218 218
219 219 class PyIn(Reference):
220 220 code = Unicode()
221 221 execution_count = Integer()
222 222
223 223
224 224 PyErr = ExecuteReplyError
225 225
226 226
227 227 class Stream(Reference):
228 228 name = Enum((u'stdout', u'stderr'))
229 229 data = Unicode()
230 230
231 231
232 232 mime_pat = re.compile(r'\w+/\w+')
233 233
234 234 class DisplayData(Reference):
235 235 source = Unicode()
236 236 metadata = Dict()
237 237 data = Dict()
238 238 def _data_changed(self, name, old, new):
239 239 for k,v in new.iteritems():
240 240 nt.assert_true(mime_pat.match(k))
241 241 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
242 242
243 243
244 244 class PyOut(Reference):
245 245 execution_count = Integer()
246 246 data = Dict()
247 247 def _data_changed(self, name, old, new):
248 248 for k,v in new.iteritems():
249 249 nt.assert_true(mime_pat.match(k))
250 250 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
251 251
252 252
253 253 references = {
254 254 'execute_reply' : ExecuteReply(),
255 255 'object_info_reply' : OInfoReply(),
256 256 'status' : Status(),
257 257 'complete_reply' : CompleteReply(),
258 'version_reply': VersionReply(),
258 'kernel_info_reply': KernelInfoReply(),
259 259 'pyin' : PyIn(),
260 260 'pyout' : PyOut(),
261 261 'pyerr' : PyErr(),
262 262 'stream' : Stream(),
263 263 'display_data' : DisplayData(),
264 264 }
265 265 """
266 266 Specifications of `content` part of the reply messages.
267 267 """
268 268
269 269
270 270 def validate_message(msg, msg_type=None, parent=None):
271 271 """validate a message
272 272
273 273 This is a generator, and must be iterated through to actually
274 274 trigger each test.
275 275
276 276 If msg_type and/or parent are given, the msg_type and/or parent msg_id
277 277 are compared with the given values.
278 278 """
279 279 RMessage().check(msg)
280 280 if msg_type:
281 281 yield nt.assert_equal(msg['msg_type'], msg_type)
282 282 if parent:
283 283 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
284 284 content = msg['content']
285 285 ref = references[msg['msg_type']]
286 286 for tst in ref.check(content):
287 287 yield tst
288 288
289 289
290 290 #-----------------------------------------------------------------------------
291 291 # Tests
292 292 #-----------------------------------------------------------------------------
293 293
294 294 # Shell channel
295 295
296 296 @dec.parametric
297 297 def test_execute():
298 298 flush_channels()
299 299
300 300 shell = KM.shell_channel
301 301 msg_id = shell.execute(code='x=1')
302 302 reply = shell.get_msg(timeout=2)
303 303 for tst in validate_message(reply, 'execute_reply', msg_id):
304 304 yield tst
305 305
306 306
307 307 @dec.parametric
308 308 def test_execute_silent():
309 309 flush_channels()
310 310 msg_id, reply = execute(code='x=1', silent=True)
311 311
312 312 # flush status=idle
313 313 status = KM.sub_channel.get_msg(timeout=2)
314 314 for tst in validate_message(status, 'status', msg_id):
315 315 yield tst
316 316 nt.assert_equal(status['content']['execution_state'], 'idle')
317 317
318 318 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
319 319 count = reply['execution_count']
320 320
321 321 msg_id, reply = execute(code='x=2', silent=True)
322 322
323 323 # flush status=idle
324 324 status = KM.sub_channel.get_msg(timeout=2)
325 325 for tst in validate_message(status, 'status', msg_id):
326 326 yield tst
327 327 yield nt.assert_equal(status['content']['execution_state'], 'idle')
328 328
329 329 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
330 330 count_2 = reply['execution_count']
331 331 yield nt.assert_equal(count_2, count)
332 332
333 333
334 334 @dec.parametric
335 335 def test_execute_error():
336 336 flush_channels()
337 337
338 338 msg_id, reply = execute(code='1/0')
339 339 yield nt.assert_equal(reply['status'], 'error')
340 340 yield nt.assert_equal(reply['ename'], 'ZeroDivisionError')
341 341
342 342 pyerr = KM.sub_channel.get_msg(timeout=2)
343 343 for tst in validate_message(pyerr, 'pyerr', msg_id):
344 344 yield tst
345 345
346 346
347 347 def test_execute_inc():
348 348 """execute request should increment execution_count"""
349 349 flush_channels()
350 350
351 351 msg_id, reply = execute(code='x=1')
352 352 count = reply['execution_count']
353 353
354 354 flush_channels()
355 355
356 356 msg_id, reply = execute(code='x=2')
357 357 count_2 = reply['execution_count']
358 358 nt.assert_equal(count_2, count+1)
359 359
360 360
361 361 def test_user_variables():
362 362 flush_channels()
363 363
364 364 msg_id, reply = execute(code='x=1', user_variables=['x'])
365 365 user_variables = reply['user_variables']
366 366 nt.assert_equal(user_variables, {u'x' : u'1'})
367 367
368 368
369 369 def test_user_expressions():
370 370 flush_channels()
371 371
372 372 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
373 373 user_expressions = reply['user_expressions']
374 374 nt.assert_equal(user_expressions, {u'foo' : u'2'})
375 375
376 376
377 377 @dec.parametric
378 378 def test_oinfo():
379 379 flush_channels()
380 380
381 381 shell = KM.shell_channel
382 382
383 383 msg_id = shell.object_info('a')
384 384 reply = shell.get_msg(timeout=2)
385 385 for tst in validate_message(reply, 'object_info_reply', msg_id):
386 386 yield tst
387 387
388 388
389 389 @dec.parametric
390 390 def test_oinfo_found():
391 391 flush_channels()
392 392
393 393 shell = KM.shell_channel
394 394
395 395 msg_id, reply = execute(code='a=5')
396 396
397 397 msg_id = shell.object_info('a')
398 398 reply = shell.get_msg(timeout=2)
399 399 for tst in validate_message(reply, 'object_info_reply', msg_id):
400 400 yield tst
401 401 content = reply['content']
402 402 yield nt.assert_true(content['found'])
403 403 argspec = content['argspec']
404 404 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
405 405
406 406
407 407 @dec.parametric
408 408 def test_oinfo_detail():
409 409 flush_channels()
410 410
411 411 shell = KM.shell_channel
412 412
413 413 msg_id, reply = execute(code='ip=get_ipython()')
414 414
415 415 msg_id = shell.object_info('ip.object_inspect', detail_level=2)
416 416 reply = shell.get_msg(timeout=2)
417 417 for tst in validate_message(reply, 'object_info_reply', msg_id):
418 418 yield tst
419 419 content = reply['content']
420 420 yield nt.assert_true(content['found'])
421 421 argspec = content['argspec']
422 422 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
423 423 yield nt.assert_equal(argspec['defaults'], [0])
424 424
425 425
426 426 @dec.parametric
427 427 def test_oinfo_not_found():
428 428 flush_channels()
429 429
430 430 shell = KM.shell_channel
431 431
432 432 msg_id = shell.object_info('dne')
433 433 reply = shell.get_msg(timeout=2)
434 434 for tst in validate_message(reply, 'object_info_reply', msg_id):
435 435 yield tst
436 436 content = reply['content']
437 437 yield nt.assert_false(content['found'])
438 438
439 439
440 440 @dec.parametric
441 441 def test_complete():
442 442 flush_channels()
443 443
444 444 shell = KM.shell_channel
445 445
446 446 msg_id, reply = execute(code="alpha = albert = 5")
447 447
448 448 msg_id = shell.complete('al', 'al', 2)
449 449 reply = shell.get_msg(timeout=2)
450 450 for tst in validate_message(reply, 'complete_reply', msg_id):
451 451 yield tst
452 452 matches = reply['content']['matches']
453 453 for name in ('alpha', 'albert'):
454 454 yield nt.assert_true(name in matches, "Missing match: %r" % name)
455 455
456 456
457 457 @dec.parametric
458 def test_version_request():
458 def test_kernel_info_request():
459 459 flush_channels()
460 460
461 461 shell = KM.shell_channel
462 462
463 463 msg_id = shell.version()
464 464 reply = shell.get_msg(timeout=2)
465 for tst in validate_message(reply, 'version_reply', msg_id):
465 for tst in validate_message(reply, 'kernel_info_reply', msg_id):
466 466 yield tst
467 467
468 468
469 469 # IOPub channel
470 470
471 471
472 472 @dec.parametric
473 473 def test_stream():
474 474 flush_channels()
475 475
476 476 msg_id, reply = execute("print('hi')")
477 477
478 478 stdout = KM.sub_channel.get_msg(timeout=2)
479 479 for tst in validate_message(stdout, 'stream', msg_id):
480 480 yield tst
481 481 content = stdout['content']
482 482 yield nt.assert_equal(content['name'], u'stdout')
483 483 yield nt.assert_equal(content['data'], u'hi\n')
484 484
485 485
486 486 @dec.parametric
487 487 def test_display_data():
488 488 flush_channels()
489 489
490 490 msg_id, reply = execute("from IPython.core.display import display; display(1)")
491 491
492 492 display = KM.sub_channel.get_msg(timeout=2)
493 493 for tst in validate_message(display, 'display_data', parent=msg_id):
494 494 yield tst
495 495 data = display['content']['data']
496 496 yield nt.assert_equal(data['text/plain'], u'1')
497 497
@@ -1,1043 +1,1043 b''
1 1 .. _messaging:
2 2
3 3 ======================
4 4 Messaging in IPython
5 5 ======================
6 6
7 7
8 8 Introduction
9 9 ============
10 10
11 11 This document explains the basic communications design and messaging
12 12 specification for how the various IPython objects interact over a network
13 13 transport. The current implementation uses the ZeroMQ_ library for messaging
14 14 within and between hosts.
15 15
16 16 .. Note::
17 17
18 18 This document should be considered the authoritative description of the
19 19 IPython messaging protocol, and all developers are strongly encouraged to
20 20 keep it updated as the implementation evolves, so that we have a single
21 21 common reference for all protocol details.
22 22
23 23 The basic design is explained in the following diagram:
24 24
25 25 .. image:: figs/frontend-kernel.png
26 26 :width: 450px
27 27 :alt: IPython kernel/frontend messaging architecture.
28 28 :align: center
29 29 :target: ../_images/frontend-kernel.png
30 30
31 31 A single kernel can be simultaneously connected to one or more frontends. The
32 32 kernel has three sockets that serve the following functions:
33 33
34 34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
35 35 the kernel to request input from the active frontend when :func:`raw_input` is called.
36 36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
37 37 for the kernel while this communication is happening (illustrated in the
38 38 figure by the black outline around the central keyboard). In practice,
39 39 frontends may display such kernel requests using a special input widget or
40 40 otherwise indicating that the user is to type input for the kernel instead
41 41 of normal commands in the frontend.
42 42
43 43 2. Shell: this single ROUTER socket allows multiple incoming connections from
44 44 frontends, and this is the socket where requests for code execution, object
45 45 information, prompts, etc. are made to the kernel by any frontend. The
46 46 communication on this socket is a sequence of request/reply actions from
47 47 each frontend and the kernel.
48 48
49 49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
50 50 side effects (stdout, stderr, etc.) as well as the requests coming from any
51 51 client over the shell socket and its own requests on the stdin socket. There
52 52 are a number of actions in Python which generate side effects: :func:`print`
53 53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
54 54 a multi-client scenario, we want all frontends to be able to know what each
55 55 other has sent to the kernel (this can be useful in collaborative scenarios,
56 56 for example). This socket allows both side effects and the information
57 57 about communications taking place with one client over the shell channel
58 58 to be made available to all clients in a uniform manner.
59 59
60 60 All messages are tagged with enough information (details below) for clients
61 61 to know which messages come from their own interaction with the kernel and
62 62 which ones are from other clients, so they can display each type
63 63 appropriately.
64 64
65 65 The actual format of the messages allowed on each of these channels is
66 66 specified below. Messages are dicts of dicts with string keys and values that
67 67 are reasonably representable in JSON. Our current implementation uses JSON
68 68 explicitly as its message format, but this shouldn't be considered a permanent
69 69 feature. As we've discovered that JSON has non-trivial performance issues due
70 70 to excessive copying, we may in the future move to a pure pickle-based raw
71 71 message format. However, it should be possible to easily convert from the raw
72 72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
73 73 As long as it's easy to make a JSON version of the objects that is a faithful
74 74 representation of all the data, we can communicate with such clients.
75 75
76 76 .. Note::
77 77
78 78 Not all of these have yet been fully fleshed out, but the key ones are, see
79 79 kernel and frontend files for actual implementation details.
80 80
81 81 General Message Format
82 82 ======================
83 83
84 84 A message is defined by the following four-dictionary structure::
85 85
86 86 {
87 87 # The message header contains a pair of unique identifiers for the
88 88 # originating session and the actual message id, in addition to the
89 89 # username for the process that generated the message. This is useful in
90 90 # collaborative settings where multiple users may be interacting with the
91 91 # same kernel simultaneously, so that frontends can label the various
92 92 # messages in a meaningful way.
93 93 'header' : {
94 94 'msg_id' : uuid,
95 95 'username' : str,
96 96 'session' : uuid
97 97 # All recognized message type strings are listed below.
98 98 'msg_type' : str,
99 99 },
100 100
101 101 # In a chain of messages, the header from the parent is copied so that
102 102 # clients can track where messages come from.
103 103 'parent_header' : dict,
104 104
105 105 # The actual content of the message must be a dict, whose structure
106 106 # depends on the message type.
107 107 'content' : dict,
108 108
109 109 # Any metadata associated with the message.
110 110 'metadata' : dict,
111 111 }
112 112
113 113
114 114 Python functional API
115 115 =====================
116 116
117 117 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
118 118 should develop, at a few key points, functional forms of all the requests that
119 119 take arguments in this manner and automatically construct the necessary dict
120 120 for sending.
121 121
122 122 In addition, the Python implementation of the message specification extends
123 123 messages upon deserialization to the following form for convenience::
124 124
125 125 {
126 126 'header' : dict,
127 127 # The msg's unique identifier and type are always stored in the header,
128 128 # but the Python implementation copies them to the top level.
129 129 'msg_id' : uuid,
130 130 'msg_type' : str,
131 131 'parent_header' : dict,
132 132 'content' : dict,
133 133 'metadata' : dict,
134 134 }
135 135
136 136 All messages sent to or received by any IPython process should have this
137 137 extended structure.
138 138
139 139
140 140 Messages on the shell ROUTER/DEALER sockets
141 141 ===========================================
142 142
143 143 .. _execute:
144 144
145 145 Execute
146 146 -------
147 147
148 148 This message type is used by frontends to ask the kernel to execute code on
149 149 behalf of the user, in a namespace reserved to the user's variables (and thus
150 150 separate from the kernel's own internal code and variables).
151 151
152 152 Message type: ``execute_request``::
153 153
154 154 content = {
155 155 # Source code to be executed by the kernel, one or more lines.
156 156 'code' : str,
157 157
158 158 # A boolean flag which, if True, signals the kernel to execute
159 159 # this code as quietly as possible. This means that the kernel
160 160 # will compile the code with 'exec' instead of 'single' (so
161 161 # sys.displayhook will not fire), forces store_history to be False,
162 162 # and will *not*:
163 163 # - broadcast exceptions on the PUB socket
164 164 # - do any logging
165 165 #
166 166 # The default is False.
167 167 'silent' : bool,
168 168
169 169 # A boolean flag which, if True, signals the kernel to populate history
170 170 # The default is True if silent is False. If silent is True, store_history
171 171 # is forced to be False.
172 172 'store_history' : bool,
173 173
174 174 # A list of variable names from the user's namespace to be retrieved. What
175 175 # returns is a JSON string of the variable's repr(), not a python object.
176 176 'user_variables' : list,
177 177
178 178 # Similarly, a dict mapping names to expressions to be evaluated in the
179 179 # user's dict.
180 180 'user_expressions' : dict,
181 181
182 182 # Some frontends (e.g. the Notebook) do not support stdin requests. If
183 183 # raw_input is called from code executed from such a frontend, a
184 184 # StdinNotImplementedError will be raised.
185 185 'allow_stdin' : True,
186 186
187 187 }
188 188
189 189 The ``code`` field contains a single string (possibly multiline). The kernel
190 190 is responsible for splitting this into one or more independent execution blocks
191 191 and deciding whether to compile these in 'single' or 'exec' mode (see below for
192 192 detailed execution semantics).
193 193
194 194 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
195 195 the notion of a prompt string that allowed arbitrary code to be evaluated, and
196 196 this was put to good use by many in creating prompts that displayed system
197 197 status, path information, and even more esoteric uses like remote instrument
198 198 status aqcuired over the network. But now that IPython has a clean separation
199 199 between the kernel and the clients, the kernel has no prompt knowledge; prompts
200 200 are a frontend-side feature, and it should be even possible for different
201 201 frontends to display different prompts while interacting with the same kernel.
202 202
203 203 The kernel now provides the ability to retrieve data from the user's namespace
204 204 after the execution of the main ``code``, thanks to two fields in the
205 205 ``execute_request`` message:
206 206
207 207 - ``user_variables``: If only variables from the user's namespace are needed, a
208 208 list of variable names can be passed and a dict with these names as keys and
209 209 their :func:`repr()` as values will be returned.
210 210
211 211 - ``user_expressions``: For more complex expressions that require function
212 212 evaluations, a dict can be provided with string keys and arbitrary python
213 213 expressions as values. The return message will contain also a dict with the
214 214 same keys and the :func:`repr()` of the evaluated expressions as value.
215 215
216 216 With this information, frontends can display any status information they wish
217 217 in the form that best suits each frontend (a status line, a popup, inline for a
218 218 terminal, etc).
219 219
220 220 .. Note::
221 221
222 222 In order to obtain the current execution counter for the purposes of
223 223 displaying input prompts, frontends simply make an execution request with an
224 224 empty code string and ``silent=True``.
225 225
226 226 Execution semantics
227 227 ~~~~~~~~~~~~~~~~~~~
228 228
229 229 When the silent flag is false, the execution of use code consists of the
230 230 following phases (in silent mode, only the ``code`` field is executed):
231 231
232 232 1. Run the ``pre_runcode_hook``.
233 233
234 234 2. Execute the ``code`` field, see below for details.
235 235
236 236 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
237 237 computed. This ensures that any error in the latter don't harm the main
238 238 code execution.
239 239
240 240 4. Call any method registered with :meth:`register_post_execute`.
241 241
242 242 .. warning::
243 243
244 244 The API for running code before/after the main code block is likely to
245 245 change soon. Both the ``pre_runcode_hook`` and the
246 246 :meth:`register_post_execute` are susceptible to modification, as we find a
247 247 consistent model for both.
248 248
249 249 To understand how the ``code`` field is executed, one must know that Python
250 250 code can be compiled in one of three modes (controlled by the ``mode`` argument
251 251 to the :func:`compile` builtin):
252 252
253 253 *single*
254 254 Valid for a single interactive statement (though the source can contain
255 255 multiple lines, such as a for loop). When compiled in this mode, the
256 256 generated bytecode contains special instructions that trigger the calling of
257 257 :func:`sys.displayhook` for any expression in the block that returns a value.
258 258 This means that a single statement can actually produce multiple calls to
259 259 :func:`sys.displayhook`, if for example it contains a loop where each
260 260 iteration computes an unassigned expression would generate 10 calls::
261 261
262 262 for i in range(10):
263 263 i**2
264 264
265 265 *exec*
266 266 An arbitrary amount of source code, this is how modules are compiled.
267 267 :func:`sys.displayhook` is *never* implicitly called.
268 268
269 269 *eval*
270 270 A single expression that returns a value. :func:`sys.displayhook` is *never*
271 271 implicitly called.
272 272
273 273
274 274 The ``code`` field is split into individual blocks each of which is valid for
275 275 execution in 'single' mode, and then:
276 276
277 277 - If there is only a single block: it is executed in 'single' mode.
278 278
279 279 - If there is more than one block:
280 280
281 281 * if the last one is a single line long, run all but the last in 'exec' mode
282 282 and the very last one in 'single' mode. This makes it easy to type simple
283 283 expressions at the end to see computed values.
284 284
285 285 * if the last one is no more than two lines long, run all but the last in
286 286 'exec' mode and the very last one in 'single' mode. This makes it easy to
287 287 type simple expressions at the end to see computed values. - otherwise
288 288 (last one is also multiline), run all in 'exec' mode
289 289
290 290 * otherwise (last one is also multiline), run all in 'exec' mode as a single
291 291 unit.
292 292
293 293 Any error in retrieving the ``user_variables`` or evaluating the
294 294 ``user_expressions`` will result in a simple error message in the return fields
295 295 of the form::
296 296
297 297 [ERROR] ExceptionType: Exception message
298 298
299 299 The user can simply send the same variable name or expression for evaluation to
300 300 see a regular traceback.
301 301
302 302 Errors in any registered post_execute functions are also reported similarly,
303 303 and the failing function is removed from the post_execution set so that it does
304 304 not continue triggering failures.
305 305
306 306 Upon completion of the execution request, the kernel *always* sends a reply,
307 307 with a status code indicating what happened and additional data depending on
308 308 the outcome. See :ref:`below <execution_results>` for the possible return
309 309 codes and associated data.
310 310
311 311
312 312 Execution counter (old prompt number)
313 313 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
314 314
315 315 The kernel has a single, monotonically increasing counter of all execution
316 316 requests that are made with ``store_history=True``. This counter is used to populate
317 317 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
318 318 display it in some form to the user, which will typically (but not necessarily)
319 319 be done in the prompts. The value of this counter will be returned as the
320 320 ``execution_count`` field of all ``execute_reply`` messages.
321 321
322 322 .. _execution_results:
323 323
324 324 Execution results
325 325 ~~~~~~~~~~~~~~~~~
326 326
327 327 Message type: ``execute_reply``::
328 328
329 329 content = {
330 330 # One of: 'ok' OR 'error' OR 'abort'
331 331 'status' : str,
332 332
333 333 # The global kernel counter that increases by one with each request that
334 334 # stores history. This will typically be used by clients to display
335 335 # prompt numbers to the user. If the request did not store history, this will
336 336 # be the current value of the counter in the kernel.
337 337 'execution_count' : int,
338 338 }
339 339
340 340 When status is 'ok', the following extra fields are present::
341 341
342 342 {
343 343 # 'payload' will be a list of payload dicts.
344 344 # Each execution payload is a dict with string keys that may have been
345 345 # produced by the code being executed. It is retrieved by the kernel at
346 346 # the end of the execution and sent back to the front end, which can take
347 347 # action on it as needed. See main text for further details.
348 348 'payload' : list(dict),
349 349
350 350 # Results for the user_variables and user_expressions.
351 351 'user_variables' : dict,
352 352 'user_expressions' : dict,
353 353 }
354 354
355 355 .. admonition:: Execution payloads
356 356
357 357 The notion of an 'execution payload' is different from a return value of a
358 358 given set of code, which normally is just displayed on the pyout stream
359 359 through the PUB socket. The idea of a payload is to allow special types of
360 360 code, typically magics, to populate a data container in the IPython kernel
361 361 that will be shipped back to the caller via this channel. The kernel
362 362 has an API for this in the PayloadManager::
363 363
364 364 ip.payload_manager.write_payload(payload_dict)
365 365
366 366 which appends a dictionary to the list of payloads.
367 367
368 368
369 369 When status is 'error', the following extra fields are present::
370 370
371 371 {
372 372 'ename' : str, # Exception name, as a string
373 373 'evalue' : str, # Exception value, as a string
374 374
375 375 # The traceback will contain a list of frames, represented each as a
376 376 # string. For now we'll stick to the existing design of ultraTB, which
377 377 # controls exception level of detail statefully. But eventually we'll
378 378 # want to grow into a model where more information is collected and
379 379 # packed into the traceback object, with clients deciding how little or
380 380 # how much of it to unpack. But for now, let's start with a simple list
381 381 # of strings, since that requires only minimal changes to ultratb as
382 382 # written.
383 383 'traceback' : list,
384 384 }
385 385
386 386
387 387 When status is 'abort', there are for now no additional data fields. This
388 388 happens when the kernel was interrupted by a signal.
389 389
390 390 Kernel attribute access
391 391 -----------------------
392 392
393 393 .. warning::
394 394
395 395 This part of the messaging spec is not actually implemented in the kernel
396 396 yet.
397 397
398 398 While this protocol does not specify full RPC access to arbitrary methods of
399 399 the kernel object, the kernel does allow read (and in some cases write) access
400 400 to certain attributes.
401 401
402 402 The policy for which attributes can be read is: any attribute of the kernel, or
403 403 its sub-objects, that belongs to a :class:`Configurable` object and has been
404 404 declared at the class-level with Traits validation, is in principle accessible
405 405 as long as its name does not begin with a leading underscore. The attribute
406 406 itself will have metadata indicating whether it allows remote read and/or write
407 407 access. The message spec follows for attribute read and write requests.
408 408
409 409 Message type: ``getattr_request``::
410 410
411 411 content = {
412 412 # The (possibly dotted) name of the attribute
413 413 'name' : str,
414 414 }
415 415
416 416 When a ``getattr_request`` fails, there are two possible error types:
417 417
418 418 - AttributeError: this type of error was raised when trying to access the
419 419 given name by the kernel itself. This means that the attribute likely
420 420 doesn't exist.
421 421
422 422 - AccessError: the attribute exists but its value is not readable remotely.
423 423
424 424
425 425 Message type: ``getattr_reply``::
426 426
427 427 content = {
428 428 # One of ['ok', 'AttributeError', 'AccessError'].
429 429 'status' : str,
430 430 # If status is 'ok', a JSON object.
431 431 'value' : object,
432 432 }
433 433
434 434 Message type: ``setattr_request``::
435 435
436 436 content = {
437 437 # The (possibly dotted) name of the attribute
438 438 'name' : str,
439 439
440 440 # A JSON-encoded object, that will be validated by the Traits
441 441 # information in the kernel
442 442 'value' : object,
443 443 }
444 444
445 445 When a ``setattr_request`` fails, there are also two possible error types with
446 446 similar meanings as those of the ``getattr_request`` case, but for writing.
447 447
448 448 Message type: ``setattr_reply``::
449 449
450 450 content = {
451 451 # One of ['ok', 'AttributeError', 'AccessError'].
452 452 'status' : str,
453 453 }
454 454
455 455
456 456
457 457 Object information
458 458 ------------------
459 459
460 460 One of IPython's most used capabilities is the introspection of Python objects
461 461 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
462 462 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
463 463 enough that it warrants an explicit message type, especially because frontends
464 464 may want to get object information in response to user keystrokes (like Tab or
465 465 F1) besides from the user explicitly typing code like ``x??``.
466 466
467 467 Message type: ``object_info_request``::
468 468
469 469 content = {
470 470 # The (possibly dotted) name of the object to be searched in all
471 471 # relevant namespaces
472 472 'name' : str,
473 473
474 474 # The level of detail desired. The default (0) is equivalent to typing
475 475 # 'x?' at the prompt, 1 is equivalent to 'x??'.
476 476 'detail_level' : int,
477 477 }
478 478
479 479 The returned information will be a dictionary with keys very similar to the
480 480 field names that IPython prints at the terminal.
481 481
482 482 Message type: ``object_info_reply``::
483 483
484 484 content = {
485 485 # The name the object was requested under
486 486 'name' : str,
487 487
488 488 # Boolean flag indicating whether the named object was found or not. If
489 489 # it's false, all other fields will be empty.
490 490 'found' : bool,
491 491
492 492 # Flags for magics and system aliases
493 493 'ismagic' : bool,
494 494 'isalias' : bool,
495 495
496 496 # The name of the namespace where the object was found ('builtin',
497 497 # 'magics', 'alias', 'interactive', etc.)
498 498 'namespace' : str,
499 499
500 500 # The type name will be type.__name__ for normal Python objects, but it
501 501 # can also be a string like 'Magic function' or 'System alias'
502 502 'type_name' : str,
503 503
504 504 # The string form of the object, possibly truncated for length if
505 505 # detail_level is 0
506 506 'string_form' : str,
507 507
508 508 # For objects with a __class__ attribute this will be set
509 509 'base_class' : str,
510 510
511 511 # For objects with a __len__ attribute this will be set
512 512 'length' : int,
513 513
514 514 # If the object is a function, class or method whose file we can find,
515 515 # we give its full path
516 516 'file' : str,
517 517
518 518 # For pure Python callable objects, we can reconstruct the object
519 519 # definition line which provides its call signature. For convenience this
520 520 # is returned as a single 'definition' field, but below the raw parts that
521 521 # compose it are also returned as the argspec field.
522 522 'definition' : str,
523 523
524 524 # The individual parts that together form the definition string. Clients
525 525 # with rich display capabilities may use this to provide a richer and more
526 526 # precise representation of the definition line (e.g. by highlighting
527 527 # arguments based on the user's cursor position). For non-callable
528 528 # objects, this field is empty.
529 529 'argspec' : { # The names of all the arguments
530 530 args : list,
531 531 # The name of the varargs (*args), if any
532 532 varargs : str,
533 533 # The name of the varkw (**kw), if any
534 534 varkw : str,
535 535 # The values (as strings) of all default arguments. Note
536 536 # that these must be matched *in reverse* with the 'args'
537 537 # list above, since the first positional args have no default
538 538 # value at all.
539 539 defaults : list,
540 540 },
541 541
542 542 # For instances, provide the constructor signature (the definition of
543 543 # the __init__ method):
544 544 'init_definition' : str,
545 545
546 546 # Docstrings: for any object (function, method, module, package) with a
547 547 # docstring, we show it. But in addition, we may provide additional
548 548 # docstrings. For example, for instances we will show the constructor
549 549 # and class docstrings as well, if available.
550 550 'docstring' : str,
551 551
552 552 # For instances, provide the constructor and class docstrings
553 553 'init_docstring' : str,
554 554 'class_docstring' : str,
555 555
556 556 # If it's a callable object whose call method has a separate docstring and
557 557 # definition line:
558 558 'call_def' : str,
559 559 'call_docstring' : str,
560 560
561 561 # If detail_level was 1, we also try to find the source code that
562 562 # defines the object, if possible. The string 'None' will indicate
563 563 # that no source was found.
564 564 'source' : str,
565 565 }
566 566
567 567
568 568 Complete
569 569 --------
570 570
571 571 Message type: ``complete_request``::
572 572
573 573 content = {
574 574 # The text to be completed, such as 'a.is'
575 575 'text' : str,
576 576
577 577 # The full line, such as 'print a.is'. This allows completers to
578 578 # make decisions that may require information about more than just the
579 579 # current word.
580 580 'line' : str,
581 581
582 582 # The entire block of text where the line is. This may be useful in the
583 583 # case of multiline completions where more context may be needed. Note: if
584 584 # in practice this field proves unnecessary, remove it to lighten the
585 585 # messages.
586 586
587 587 'block' : str,
588 588
589 589 # The position of the cursor where the user hit 'TAB' on the line.
590 590 'cursor_pos' : int,
591 591 }
592 592
593 593 Message type: ``complete_reply``::
594 594
595 595 content = {
596 596 # The list of all matches to the completion request, such as
597 597 # ['a.isalnum', 'a.isalpha'] for the above example.
598 598 'matches' : list
599 599 }
600 600
601 601
602 602 History
603 603 -------
604 604
605 605 For clients to explicitly request history from a kernel. The kernel has all
606 606 the actual execution history stored in a single location, so clients can
607 607 request it from the kernel when needed.
608 608
609 609 Message type: ``history_request``::
610 610
611 611 content = {
612 612
613 613 # If True, also return output history in the resulting dict.
614 614 'output' : bool,
615 615
616 616 # If True, return the raw input history, else the transformed input.
617 617 'raw' : bool,
618 618
619 619 # So far, this can be 'range', 'tail' or 'search'.
620 620 'hist_access_type' : str,
621 621
622 622 # If hist_access_type is 'range', get a range of input cells. session can
623 623 # be a positive session number, or a negative number to count back from
624 624 # the current session.
625 625 'session' : int,
626 626 # start and stop are line numbers within that session.
627 627 'start' : int,
628 628 'stop' : int,
629 629
630 630 # If hist_access_type is 'tail' or 'search', get the last n cells.
631 631 'n' : int,
632 632
633 633 # If hist_access_type is 'search', get cells matching the specified glob
634 634 # pattern (with * and ? as wildcards).
635 635 'pattern' : str,
636 636
637 637 }
638 638
639 639 Message type: ``history_reply``::
640 640
641 641 content = {
642 642 # A list of 3 tuples, either:
643 643 # (session, line_number, input) or
644 644 # (session, line_number, (input, output)),
645 645 # depending on whether output was False or True, respectively.
646 646 'history' : list,
647 647 }
648 648
649 649
650 650 Connect
651 651 -------
652 652
653 653 When a client connects to the request/reply socket of the kernel, it can issue
654 654 a connect request to get basic information about the kernel, such as the ports
655 655 the other ZeroMQ sockets are listening on. This allows clients to only have
656 656 to know about a single port (the shell channel) to connect to a kernel.
657 657
658 658 Message type: ``connect_request``::
659 659
660 660 content = {
661 661 }
662 662
663 663 Message type: ``connect_reply``::
664 664
665 665 content = {
666 666 'shell_port' : int # The port the shell ROUTER socket is listening on.
667 667 'iopub_port' : int # The port the PUB socket is listening on.
668 668 'stdin_port' : int # The port the stdin ROUTER socket is listening on.
669 669 'hb_port' : int # The port the heartbeat socket is listening on.
670 670 }
671 671
672 672
673 Version
674 -------
673 Kernel info
674 -----------
675 675
676 676 If a client needs to know what protocol the kernel supports, it can
677 677 ask version number of the messaging protocol supported by the kernel.
678 678 This message can be used to fetch other core information of the
679 679 kernel, including language (e.g., Python), language version number and
680 680 IPython version number.
681 681
682 Message type: ``version_request``::
682 Message type: ``kernel_info_request``::
683 683
684 684 content = {
685 685 }
686 686
687 Message type: ``version_reply``::
687 Message type: ``kernel_info_reply``::
688 688
689 689 content = {
690 690 # Version of messaging protocol (mandatory).
691 691 # The first integer indicates major version. It is incremented when
692 692 # there is any backward incompatible change.
693 693 # The second integer indicates minor version. It is incremented when
694 694 # there is any backward compatible change.
695 695 'protocol_version': [int, int],
696 696
697 697 # IPython version number (optional).
698 698 # Non-python kernel backend may not have this version number.
699 699 # The last component is an extra field, which may be 'dev' or
700 700 # 'rc1' in development version. It is an empty string for
701 701 # released version.
702 702 'ipython_version': [int, int, int, str],
703 703
704 704 # Language version number (mandatory).
705 705 # It is Python version number (e.g., [2, 7, 3]) for the kernel
706 706 # included in IPython.
707 707 'language_version': [int, ...],
708 708
709 709 # Programming language in which kernel is implemented (mandatory).
710 710 # Kernel included in IPython returns 'python'.
711 711 'language': str,
712 712 }
713 713
714 714
715 715 Kernel shutdown
716 716 ---------------
717 717
718 718 The clients can request the kernel to shut itself down; this is used in
719 719 multiple cases:
720 720
721 721 - when the user chooses to close the client application via a menu or window
722 722 control.
723 723 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
724 724 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
725 725 IPythonQt client) to force a kernel restart to get a clean kernel without
726 726 losing client-side state like history or inlined figures.
727 727
728 728 The client sends a shutdown request to the kernel, and once it receives the
729 729 reply message (which is otherwise empty), it can assume that the kernel has
730 730 completed shutdown safely.
731 731
732 732 Upon their own shutdown, client applications will typically execute a last
733 733 minute sanity check and forcefully terminate any kernel that is still alive, to
734 734 avoid leaving stray processes in the user's machine.
735 735
736 736 For both shutdown request and reply, there is no actual content that needs to
737 737 be sent, so the content dict is empty.
738 738
739 739 Message type: ``shutdown_request``::
740 740
741 741 content = {
742 742 'restart' : bool # whether the shutdown is final, or precedes a restart
743 743 }
744 744
745 745 Message type: ``shutdown_reply``::
746 746
747 747 content = {
748 748 'restart' : bool # whether the shutdown is final, or precedes a restart
749 749 }
750 750
751 751 .. Note::
752 752
753 753 When the clients detect a dead kernel thanks to inactivity on the heartbeat
754 754 socket, they simply send a forceful process termination signal, since a dead
755 755 process is unlikely to respond in any useful way to messages.
756 756
757 757
758 758 Messages on the PUB/SUB socket
759 759 ==============================
760 760
761 761 Streams (stdout, stderr, etc)
762 762 ------------------------------
763 763
764 764 Message type: ``stream``::
765 765
766 766 content = {
767 767 # The name of the stream is one of 'stdin', 'stdout', 'stderr'
768 768 'name' : str,
769 769
770 770 # The data is an arbitrary string to be written to that stream
771 771 'data' : str,
772 772 }
773 773
774 774 When a kernel receives a raw_input call, it should also broadcast it on the pub
775 775 socket with the names 'stdin' and 'stdin_reply'. This will allow other clients
776 776 to monitor/display kernel interactions and possibly replay them to their user
777 777 or otherwise expose them.
778 778
779 779 Display Data
780 780 ------------
781 781
782 782 This type of message is used to bring back data that should be diplayed (text,
783 783 html, svg, etc.) in the frontends. This data is published to all frontends.
784 784 Each message can have multiple representations of the data; it is up to the
785 785 frontend to decide which to use and how. A single message should contain all
786 786 possible representations of the same information. Each representation should
787 787 be a JSON'able data structure, and should be a valid MIME type.
788 788
789 789 Some questions remain about this design:
790 790
791 791 * Do we use this message type for pyout/displayhook? Probably not, because
792 792 the displayhook also has to handle the Out prompt display. On the other hand
793 793 we could put that information into the metadata secion.
794 794
795 795 Message type: ``display_data``::
796 796
797 797 content = {
798 798
799 799 # Who create the data
800 800 'source' : str,
801 801
802 802 # The data dict contains key/value pairs, where the kids are MIME
803 803 # types and the values are the raw data of the representation in that
804 804 # format. The data dict must minimally contain the ``text/plain``
805 805 # MIME type which is used as a backup representation.
806 806 'data' : dict,
807 807
808 808 # Any metadata that describes the data
809 809 'metadata' : dict
810 810 }
811 811
812 812
813 813 Raw Data Publication
814 814 --------------------
815 815
816 816 ``display_data`` lets you publish *representations* of data, such as images and html.
817 817 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
818 818
819 819 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
820 820
821 821 .. sourcecode:: python
822 822
823 823 from IPython.zmq.datapub import publish_data
824 824 ns = dict(x=my_array)
825 825 publish_data(ns)
826 826
827 827
828 828 Message type: ``data_pub``::
829 829
830 830 content = {
831 831 # the keys of the data dict, after it has been unserialized
832 832 keys = ['a', 'b']
833 833 }
834 834 # the namespace dict will be serialized in the message buffers,
835 835 # which will have a length of at least one
836 836 buffers = ['pdict', ...]
837 837
838 838
839 839 The interpretation of a sequence of data_pub messages for a given parent request should be
840 840 to update a single namespace with subsequent results.
841 841
842 842 .. note::
843 843
844 844 No frontends directly handle data_pub messages at this time.
845 845 It is currently only used by the client/engines in :mod:`IPython.parallel`,
846 846 where engines may publish *data* to the Client,
847 847 of which the Client can then publish *representations* via ``display_data``
848 848 to various frontends.
849 849
850 850 Python inputs
851 851 -------------
852 852
853 853 These messages are the re-broadcast of the ``execute_request``.
854 854
855 855 Message type: ``pyin``::
856 856
857 857 content = {
858 858 'code' : str, # Source code to be executed, one or more lines
859 859
860 860 # The counter for this execution is also provided so that clients can
861 861 # display it, since IPython automatically creates variables called _iN
862 862 # (for input prompt In[N]).
863 863 'execution_count' : int
864 864 }
865 865
866 866 Python outputs
867 867 --------------
868 868
869 869 When Python produces output from code that has been compiled in with the
870 870 'single' flag to :func:`compile`, any expression that produces a value (such as
871 871 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
872 872 this value whatever it wants. The default behavior of ``sys.displayhook`` in
873 873 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
874 874 the value as long as it is not ``None`` (which isn't printed at all). In our
875 875 case, the kernel instantiates as ``sys.displayhook`` an object which has
876 876 similar behavior, but which instead of printing to stdout, broadcasts these
877 877 values as ``pyout`` messages for clients to display appropriately.
878 878
879 879 IPython's displayhook can handle multiple simultaneous formats depending on its
880 880 configuration. The default pretty-printed repr text is always given with the
881 881 ``data`` entry in this message. Any other formats are provided in the
882 882 ``extra_formats`` list. Frontends are free to display any or all of these
883 883 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
884 884 string, a type string, and the data. The ID is unique to the formatter
885 885 implementation that created the data. Frontends will typically ignore the ID
886 886 unless if it has requested a particular formatter. The type string tells the
887 887 frontend how to interpret the data. It is often, but not always a MIME type.
888 888 Frontends should ignore types that it does not understand. The data itself is
889 889 any JSON object and depends on the format. It is often, but not always a string.
890 890
891 891 Message type: ``pyout``::
892 892
893 893 content = {
894 894
895 895 # The counter for this execution is also provided so that clients can
896 896 # display it, since IPython automatically creates variables called _N
897 897 # (for prompt N).
898 898 'execution_count' : int,
899 899
900 900 # The data dict contains key/value pairs, where the kids are MIME
901 901 # types and the values are the raw data of the representation in that
902 902 # format. The data dict must minimally contain the ``text/plain``
903 903 # MIME type which is used as a backup representation.
904 904 'data' : dict,
905 905
906 906 }
907 907
908 908 Python errors
909 909 -------------
910 910
911 911 When an error occurs during code execution
912 912
913 913 Message type: ``pyerr``::
914 914
915 915 content = {
916 916 # Similar content to the execute_reply messages for the 'error' case,
917 917 # except the 'status' field is omitted.
918 918 }
919 919
920 920 Kernel status
921 921 -------------
922 922
923 923 This message type is used by frontends to monitor the status of the kernel.
924 924
925 925 Message type: ``status``::
926 926
927 927 content = {
928 928 # When the kernel starts to execute code, it will enter the 'busy'
929 929 # state and when it finishes, it will enter the 'idle' state.
930 930 execution_state : ('busy', 'idle')
931 931 }
932 932
933 933 Kernel crashes
934 934 --------------
935 935
936 936 When the kernel has an unexpected exception, caught by the last-resort
937 937 sys.excepthook, we should broadcast the crash handler's output before exiting.
938 938 This will allow clients to notice that a kernel died, inform the user and
939 939 propose further actions.
940 940
941 941 Message type: ``crash``::
942 942
943 943 content = {
944 944 # Similarly to the 'error' case for execute_reply messages, this will
945 945 # contain ename, etype and traceback fields.
946 946
947 947 # An additional field with supplementary information such as where to
948 948 # send the crash message
949 949 'info' : str,
950 950 }
951 951
952 952
953 953 Future ideas
954 954 ------------
955 955
956 956 Other potential message types, currently unimplemented, listed below as ideas.
957 957
958 958 Message type: ``file``::
959 959
960 960 content = {
961 961 'path' : 'cool.jpg',
962 962 'mimetype' : str,
963 963 'data' : str,
964 964 }
965 965
966 966
967 967 Messages on the stdin ROUTER/DEALER sockets
968 968 ===========================================
969 969
970 970 This is a socket where the request/reply pattern goes in the opposite direction:
971 971 from the kernel to a *single* frontend, and its purpose is to allow
972 972 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
973 973 to be fulfilled by the client. The request should be made to the frontend that
974 974 made the execution request that prompted ``raw_input`` to be called. For now we
975 975 will keep these messages as simple as possible, since they only mean to convey
976 976 the ``raw_input(prompt)`` call.
977 977
978 978 Message type: ``input_request``::
979 979
980 980 content = { 'prompt' : str }
981 981
982 982 Message type: ``input_reply``::
983 983
984 984 content = { 'value' : str }
985 985
986 986 .. Note::
987 987
988 988 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
989 989 practice the kernel should behave like an interactive program. When a
990 990 program is opened on the console, the keyboard effectively takes over the
991 991 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
992 992 Since the IPython kernel effectively behaves like a console program (albeit
993 993 one whose "keyboard" is actually living in a separate process and
994 994 transported over the zmq connection), raw ``stdin`` isn't expected to be
995 995 available.
996 996
997 997
998 998 Heartbeat for kernels
999 999 =====================
1000 1000
1001 1001 Initially we had considered using messages like those above over ZMQ for a
1002 1002 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
1003 1003 alive at all, even if it may be busy executing user code). But this has the
1004 1004 problem that if the kernel is locked inside extension code, it wouldn't execute
1005 1005 the python heartbeat code. But it turns out that we can implement a basic
1006 1006 heartbeat with pure ZMQ, without using any Python messaging at all.
1007 1007
1008 1008 The monitor sends out a single zmq message (right now, it is a str of the
1009 1009 monitor's lifetime in seconds), and gets the same message right back, prefixed
1010 1010 with the zmq identity of the DEALER socket in the heartbeat process. This can be
1011 1011 a uuid, or even a full message, but there doesn't seem to be a need for packing
1012 1012 up a message when the sender and receiver are the exact same Python object.
1013 1013
1014 1014 The model is this::
1015 1015
1016 1016 monitor.send(str(self.lifetime)) # '1.2345678910'
1017 1017
1018 1018 and the monitor receives some number of messages of the form::
1019 1019
1020 1020 ['uuid-abcd-dead-beef', '1.2345678910']
1021 1021
1022 1022 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
1023 1023 the rest is the message sent by the monitor. No Python code ever has any
1024 1024 access to the message between the monitor's send, and the monitor's recv.
1025 1025
1026 1026
1027 1027 ToDo
1028 1028 ====
1029 1029
1030 1030 Missing things include:
1031 1031
1032 1032 * Important: finish thinking through the payload concept and API.
1033 1033
1034 1034 * Important: ensure that we have a good solution for magics like %edit. It's
1035 1035 likely that with the payload concept we can build a full solution, but not
1036 1036 100% clear yet.
1037 1037
1038 1038 * Finishing the details of the heartbeat protocol.
1039 1039
1040 1040 * Signal handling: specify what kind of information kernel should broadcast (or
1041 1041 not) when it receives signals.
1042 1042
1043 1043 .. include:: ../links.rst
General Comments 0
You need to be logged in to leave comments. Login now