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