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