##// END OF EJS Templates
Add version_request/reply messaging protocol
Takafumi Arakaki -
Show More
@@ -1,932 +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 from IPython.config.configurable import Configurable
38 from IPython.config.configurable import Configurable
39 from IPython.config.application import boolean_flag, catch_config_error
39 from IPython.config.application import boolean_flag, catch_config_error
40 from IPython.core.application import ProfileDir
40 from IPython.core.application import ProfileDir
41 from IPython.core.error import StdinNotImplementedError
41 from IPython.core.error import StdinNotImplementedError
42 from IPython.core.shellapp import (
42 from IPython.core.shellapp import (
43 InteractiveShellApp, shell_flags, shell_aliases
43 InteractiveShellApp, shell_flags, shell_aliases
44 )
44 )
45 from IPython.utils import io
45 from IPython.utils import io
46 from IPython.utils import py3compat
46 from IPython.utils import py3compat
47 from IPython.utils.frame import extract_module_locals
47 from IPython.utils.frame import extract_module_locals
48 from IPython.utils.jsonutil import json_clean
48 from IPython.utils.jsonutil import json_clean
49 from IPython.utils.traitlets import (
49 from IPython.utils.traitlets import (
50 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
50 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
51 )
51 )
52
52
53 from entry_point import base_launch_kernel
53 from entry_point import base_launch_kernel
54 from kernelapp import KernelApp, kernel_flags, kernel_aliases
54 from kernelapp import KernelApp, kernel_flags, kernel_aliases
55 from serialize import serialize_object, unpack_apply_message
55 from serialize import serialize_object, unpack_apply_message
56 from session import Session, Message
56 from session import Session, Message
57 from zmqshell import ZMQInteractiveShell
57 from zmqshell import ZMQInteractiveShell
58
58
59
59
60 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
61 # Main kernel class
61 # Main kernel class
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63
63
64 # Change this when incrementing the kernel protocol version
65 version_major = 1
66 version_minor = 1
67 version = '{0}.{1}'.format(version_major, version_minor)
68
69
64 class Kernel(Configurable):
70 class Kernel(Configurable):
65
71
66 #---------------------------------------------------------------------------
72 #---------------------------------------------------------------------------
67 # Kernel interface
73 # Kernel interface
68 #---------------------------------------------------------------------------
74 #---------------------------------------------------------------------------
69
75
70 # attribute to override with a GUI
76 # attribute to override with a GUI
71 eventloop = Any(None)
77 eventloop = Any(None)
72 def _eventloop_changed(self, name, old, new):
78 def _eventloop_changed(self, name, old, new):
73 """schedule call to eventloop from IOLoop"""
79 """schedule call to eventloop from IOLoop"""
74 loop = ioloop.IOLoop.instance()
80 loop = ioloop.IOLoop.instance()
75 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
81 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
76
82
77 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
83 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
78 session = Instance(Session)
84 session = Instance(Session)
79 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
85 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
80 shell_streams = List()
86 shell_streams = List()
81 control_stream = Instance(ZMQStream)
87 control_stream = Instance(ZMQStream)
82 iopub_socket = Instance(zmq.Socket)
88 iopub_socket = Instance(zmq.Socket)
83 stdin_socket = Instance(zmq.Socket)
89 stdin_socket = Instance(zmq.Socket)
84 log = Instance(logging.Logger)
90 log = Instance(logging.Logger)
85
91
86 user_module = Any()
92 user_module = Any()
87 def _user_module_changed(self, name, old, new):
93 def _user_module_changed(self, name, old, new):
88 if self.shell is not None:
94 if self.shell is not None:
89 self.shell.user_module = new
95 self.shell.user_module = new
90
96
91 user_ns = Dict(default_value=None)
97 user_ns = Dict(default_value=None)
92 def _user_ns_changed(self, name, old, new):
98 def _user_ns_changed(self, name, old, new):
93 if self.shell is not None:
99 if self.shell is not None:
94 self.shell.user_ns = new
100 self.shell.user_ns = new
95 self.shell.init_user_ns()
101 self.shell.init_user_ns()
96
102
97 # identities:
103 # identities:
98 int_id = Integer(-1)
104 int_id = Integer(-1)
99 ident = Unicode()
105 ident = Unicode()
100
106
101 def _ident_default(self):
107 def _ident_default(self):
102 return unicode(uuid.uuid4())
108 return unicode(uuid.uuid4())
103
109
104
110
105 # Private interface
111 # Private interface
106
112
107 # Time to sleep after flushing the stdout/err buffers in each execute
113 # Time to sleep after flushing the stdout/err buffers in each execute
108 # cycle. While this introduces a hard limit on the minimal latency of the
114 # cycle. While this introduces a hard limit on the minimal latency of the
109 # execute cycle, it helps prevent output synchronization problems for
115 # execute cycle, it helps prevent output synchronization problems for
110 # clients.
116 # clients.
111 # Units are in seconds. The minimum zmq latency on local host is probably
117 # Units are in seconds. The minimum zmq latency on local host is probably
112 # ~150 microseconds, set this to 500us for now. We may need to increase it
118 # ~150 microseconds, set this to 500us for now. We may need to increase it
113 # a little if it's not enough after more interactive testing.
119 # a little if it's not enough after more interactive testing.
114 _execute_sleep = Float(0.0005, config=True)
120 _execute_sleep = Float(0.0005, config=True)
115
121
116 # Frequency of the kernel's event loop.
122 # Frequency of the kernel's event loop.
117 # Units are in seconds, kernel subclasses for GUI toolkits may need to
123 # Units are in seconds, kernel subclasses for GUI toolkits may need to
118 # adapt to milliseconds.
124 # adapt to milliseconds.
119 _poll_interval = Float(0.05, config=True)
125 _poll_interval = Float(0.05, config=True)
120
126
121 # If the shutdown was requested over the network, we leave here the
127 # If the shutdown was requested over the network, we leave here the
122 # necessary reply message so it can be sent by our registered atexit
128 # necessary reply message so it can be sent by our registered atexit
123 # handler. This ensures that the reply is only sent to clients truly at
129 # handler. This ensures that the reply is only sent to clients truly at
124 # the end of our shutdown process (which happens after the underlying
130 # the end of our shutdown process (which happens after the underlying
125 # IPython shell's own shutdown).
131 # IPython shell's own shutdown).
126 _shutdown_message = None
132 _shutdown_message = None
127
133
128 # This is a dict of port number that the kernel is listening on. It is set
134 # This is a dict of port number that the kernel is listening on. It is set
129 # by record_ports and used by connect_request.
135 # by record_ports and used by connect_request.
130 _recorded_ports = Dict()
136 _recorded_ports = Dict()
131
137
132 # set of aborted msg_ids
138 # set of aborted msg_ids
133 aborted = Set()
139 aborted = Set()
134
140
135
141
136 def __init__(self, **kwargs):
142 def __init__(self, **kwargs):
137 super(Kernel, self).__init__(**kwargs)
143 super(Kernel, self).__init__(**kwargs)
138
144
139 # Initialize the InteractiveShell subclass
145 # Initialize the InteractiveShell subclass
140 self.shell = ZMQInteractiveShell.instance(config=self.config,
146 self.shell = ZMQInteractiveShell.instance(config=self.config,
141 profile_dir = self.profile_dir,
147 profile_dir = self.profile_dir,
142 user_module = self.user_module,
148 user_module = self.user_module,
143 user_ns = self.user_ns,
149 user_ns = self.user_ns,
144 )
150 )
145 self.shell.displayhook.session = self.session
151 self.shell.displayhook.session = self.session
146 self.shell.displayhook.pub_socket = self.iopub_socket
152 self.shell.displayhook.pub_socket = self.iopub_socket
147 self.shell.displayhook.topic = self._topic('pyout')
153 self.shell.displayhook.topic = self._topic('pyout')
148 self.shell.display_pub.session = self.session
154 self.shell.display_pub.session = self.session
149 self.shell.display_pub.pub_socket = self.iopub_socket
155 self.shell.display_pub.pub_socket = self.iopub_socket
150 self.shell.data_pub.session = self.session
156 self.shell.data_pub.session = self.session
151 self.shell.data_pub.pub_socket = self.iopub_socket
157 self.shell.data_pub.pub_socket = self.iopub_socket
152
158
153 # TMP - hack while developing
159 # TMP - hack while developing
154 self.shell._reply_content = None
160 self.shell._reply_content = None
155
161
156 # Build dict of handlers for message types
162 # Build dict of handlers for message types
157 msg_types = [ 'execute_request', 'complete_request',
163 msg_types = [ 'execute_request', 'complete_request',
158 'object_info_request', 'history_request',
164 'object_info_request', 'history_request',
165 'version_request',
159 'connect_request', 'shutdown_request',
166 'connect_request', 'shutdown_request',
160 'apply_request',
167 'apply_request',
161 ]
168 ]
162 self.shell_handlers = {}
169 self.shell_handlers = {}
163 for msg_type in msg_types:
170 for msg_type in msg_types:
164 self.shell_handlers[msg_type] = getattr(self, msg_type)
171 self.shell_handlers[msg_type] = getattr(self, msg_type)
165
172
166 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
173 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
167 self.control_handlers = {}
174 self.control_handlers = {}
168 for msg_type in control_msg_types:
175 for msg_type in control_msg_types:
169 self.control_handlers[msg_type] = getattr(self, msg_type)
176 self.control_handlers[msg_type] = getattr(self, msg_type)
170
177
171 def dispatch_control(self, msg):
178 def dispatch_control(self, msg):
172 """dispatch control requests"""
179 """dispatch control requests"""
173 idents,msg = self.session.feed_identities(msg, copy=False)
180 idents,msg = self.session.feed_identities(msg, copy=False)
174 try:
181 try:
175 msg = self.session.unserialize(msg, content=True, copy=False)
182 msg = self.session.unserialize(msg, content=True, copy=False)
176 except:
183 except:
177 self.log.error("Invalid Control Message", exc_info=True)
184 self.log.error("Invalid Control Message", exc_info=True)
178 return
185 return
179
186
180 self.log.debug("Control received: %s", msg)
187 self.log.debug("Control received: %s", msg)
181
188
182 header = msg['header']
189 header = msg['header']
183 msg_id = header['msg_id']
190 msg_id = header['msg_id']
184 msg_type = header['msg_type']
191 msg_type = header['msg_type']
185
192
186 handler = self.control_handlers.get(msg_type, None)
193 handler = self.control_handlers.get(msg_type, None)
187 if handler is None:
194 if handler is None:
188 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
195 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
189 else:
196 else:
190 try:
197 try:
191 handler(self.control_stream, idents, msg)
198 handler(self.control_stream, idents, msg)
192 except Exception:
199 except Exception:
193 self.log.error("Exception in control handler:", exc_info=True)
200 self.log.error("Exception in control handler:", exc_info=True)
194
201
195 def dispatch_shell(self, stream, msg):
202 def dispatch_shell(self, stream, msg):
196 """dispatch shell requests"""
203 """dispatch shell requests"""
197 # flush control requests first
204 # flush control requests first
198 if self.control_stream:
205 if self.control_stream:
199 self.control_stream.flush()
206 self.control_stream.flush()
200
207
201 idents,msg = self.session.feed_identities(msg, copy=False)
208 idents,msg = self.session.feed_identities(msg, copy=False)
202 try:
209 try:
203 msg = self.session.unserialize(msg, content=True, copy=False)
210 msg = self.session.unserialize(msg, content=True, copy=False)
204 except:
211 except:
205 self.log.error("Invalid Message", exc_info=True)
212 self.log.error("Invalid Message", exc_info=True)
206 return
213 return
207
214
208 header = msg['header']
215 header = msg['header']
209 msg_id = header['msg_id']
216 msg_id = header['msg_id']
210 msg_type = msg['header']['msg_type']
217 msg_type = msg['header']['msg_type']
211
218
212 # Print some info about this message and leave a '--->' marker, so it's
219 # Print some info about this message and leave a '--->' marker, so it's
213 # easier to trace visually the message chain when debugging. Each
220 # easier to trace visually the message chain when debugging. Each
214 # handler prints its message at the end.
221 # handler prints its message at the end.
215 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
222 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
216 self.log.debug(' Content: %s\n --->\n ', msg['content'])
223 self.log.debug(' Content: %s\n --->\n ', msg['content'])
217
224
218 if msg_id in self.aborted:
225 if msg_id in self.aborted:
219 self.aborted.remove(msg_id)
226 self.aborted.remove(msg_id)
220 # is it safe to assume a msg_id will not be resubmitted?
227 # is it safe to assume a msg_id will not be resubmitted?
221 reply_type = msg_type.split('_')[0] + '_reply'
228 reply_type = msg_type.split('_')[0] + '_reply'
222 status = {'status' : 'aborted'}
229 status = {'status' : 'aborted'}
223 md = {'engine' : self.ident}
230 md = {'engine' : self.ident}
224 md.update(status)
231 md.update(status)
225 reply_msg = self.session.send(stream, reply_type, metadata=md,
232 reply_msg = self.session.send(stream, reply_type, metadata=md,
226 content=status, parent=msg, ident=idents)
233 content=status, parent=msg, ident=idents)
227 return
234 return
228
235
229 handler = self.shell_handlers.get(msg_type, None)
236 handler = self.shell_handlers.get(msg_type, None)
230 if handler is None:
237 if handler is None:
231 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
238 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
232 else:
239 else:
233 # ensure default_int_handler during handler call
240 # ensure default_int_handler during handler call
234 sig = signal(SIGINT, default_int_handler)
241 sig = signal(SIGINT, default_int_handler)
235 try:
242 try:
236 handler(stream, idents, msg)
243 handler(stream, idents, msg)
237 except Exception:
244 except Exception:
238 self.log.error("Exception in message handler:", exc_info=True)
245 self.log.error("Exception in message handler:", exc_info=True)
239 finally:
246 finally:
240 signal(SIGINT, sig)
247 signal(SIGINT, sig)
241
248
242 def enter_eventloop(self):
249 def enter_eventloop(self):
243 """enter eventloop"""
250 """enter eventloop"""
244 self.log.info("entering eventloop")
251 self.log.info("entering eventloop")
245 # restore default_int_handler
252 # restore default_int_handler
246 signal(SIGINT, default_int_handler)
253 signal(SIGINT, default_int_handler)
247 while self.eventloop is not None:
254 while self.eventloop is not None:
248 try:
255 try:
249 self.eventloop(self)
256 self.eventloop(self)
250 except KeyboardInterrupt:
257 except KeyboardInterrupt:
251 # Ctrl-C shouldn't crash the kernel
258 # Ctrl-C shouldn't crash the kernel
252 self.log.error("KeyboardInterrupt caught in kernel")
259 self.log.error("KeyboardInterrupt caught in kernel")
253 continue
260 continue
254 else:
261 else:
255 # eventloop exited cleanly, this means we should stop (right?)
262 # eventloop exited cleanly, this means we should stop (right?)
256 self.eventloop = None
263 self.eventloop = None
257 break
264 break
258 self.log.info("exiting eventloop")
265 self.log.info("exiting eventloop")
259
266
260 def start(self):
267 def start(self):
261 """register dispatchers for streams"""
268 """register dispatchers for streams"""
262 self.shell.exit_now = False
269 self.shell.exit_now = False
263 if self.control_stream:
270 if self.control_stream:
264 self.control_stream.on_recv(self.dispatch_control, copy=False)
271 self.control_stream.on_recv(self.dispatch_control, copy=False)
265
272
266 def make_dispatcher(stream):
273 def make_dispatcher(stream):
267 def dispatcher(msg):
274 def dispatcher(msg):
268 return self.dispatch_shell(stream, msg)
275 return self.dispatch_shell(stream, msg)
269 return dispatcher
276 return dispatcher
270
277
271 for s in self.shell_streams:
278 for s in self.shell_streams:
272 s.on_recv(make_dispatcher(s), copy=False)
279 s.on_recv(make_dispatcher(s), copy=False)
273
280
274 def do_one_iteration(self):
281 def do_one_iteration(self):
275 """step eventloop just once"""
282 """step eventloop just once"""
276 if self.control_stream:
283 if self.control_stream:
277 self.control_stream.flush()
284 self.control_stream.flush()
278 for stream in self.shell_streams:
285 for stream in self.shell_streams:
279 # handle at most one request per iteration
286 # handle at most one request per iteration
280 stream.flush(zmq.POLLIN, 1)
287 stream.flush(zmq.POLLIN, 1)
281 stream.flush(zmq.POLLOUT)
288 stream.flush(zmq.POLLOUT)
282
289
283
290
284 def record_ports(self, ports):
291 def record_ports(self, ports):
285 """Record the ports that this kernel is using.
292 """Record the ports that this kernel is using.
286
293
287 The creator of the Kernel instance must call this methods if they
294 The creator of the Kernel instance must call this methods if they
288 want the :meth:`connect_request` method to return the port numbers.
295 want the :meth:`connect_request` method to return the port numbers.
289 """
296 """
290 self._recorded_ports = ports
297 self._recorded_ports = ports
291
298
292 #---------------------------------------------------------------------------
299 #---------------------------------------------------------------------------
293 # Kernel request handlers
300 # Kernel request handlers
294 #---------------------------------------------------------------------------
301 #---------------------------------------------------------------------------
295
302
296 def _make_metadata(self, other=None):
303 def _make_metadata(self, other=None):
297 """init metadata dict, for execute/apply_reply"""
304 """init metadata dict, for execute/apply_reply"""
298 new_md = {
305 new_md = {
299 'dependencies_met' : True,
306 'dependencies_met' : True,
300 'engine' : self.ident,
307 'engine' : self.ident,
301 'started': datetime.now(),
308 'started': datetime.now(),
302 }
309 }
303 if other:
310 if other:
304 new_md.update(other)
311 new_md.update(other)
305 return new_md
312 return new_md
306
313
307 def _publish_pyin(self, code, parent, execution_count):
314 def _publish_pyin(self, code, parent, execution_count):
308 """Publish the code request on the pyin stream."""
315 """Publish the code request on the pyin stream."""
309
316
310 self.session.send(self.iopub_socket, u'pyin',
317 self.session.send(self.iopub_socket, u'pyin',
311 {u'code':code, u'execution_count': execution_count},
318 {u'code':code, u'execution_count': execution_count},
312 parent=parent, ident=self._topic('pyin')
319 parent=parent, ident=self._topic('pyin')
313 )
320 )
314
321
315 def _publish_status(self, status, parent=None):
322 def _publish_status(self, status, parent=None):
316 """send status (busy/idle) on IOPub"""
323 """send status (busy/idle) on IOPub"""
317 self.session.send(self.iopub_socket,
324 self.session.send(self.iopub_socket,
318 u'status',
325 u'status',
319 {u'execution_state': status},
326 {u'execution_state': status},
320 parent=parent,
327 parent=parent,
321 ident=self._topic('status'),
328 ident=self._topic('status'),
322 )
329 )
323
330
324
331
325 def execute_request(self, stream, ident, parent):
332 def execute_request(self, stream, ident, parent):
326 """handle an execute_request"""
333 """handle an execute_request"""
327
334
328 self._publish_status(u'busy', parent)
335 self._publish_status(u'busy', parent)
329
336
330 try:
337 try:
331 content = parent[u'content']
338 content = parent[u'content']
332 code = content[u'code']
339 code = content[u'code']
333 silent = content[u'silent']
340 silent = content[u'silent']
334 store_history = content.get(u'store_history', not silent)
341 store_history = content.get(u'store_history', not silent)
335 except:
342 except:
336 self.log.error("Got bad msg: ")
343 self.log.error("Got bad msg: ")
337 self.log.error("%s", parent)
344 self.log.error("%s", parent)
338 return
345 return
339
346
340 md = self._make_metadata(parent['metadata'])
347 md = self._make_metadata(parent['metadata'])
341
348
342 shell = self.shell # we'll need this a lot here
349 shell = self.shell # we'll need this a lot here
343
350
344 # Replace raw_input. Note that is not sufficient to replace
351 # Replace raw_input. Note that is not sufficient to replace
345 # raw_input in the user namespace.
352 # raw_input in the user namespace.
346 if content.get('allow_stdin', False):
353 if content.get('allow_stdin', False):
347 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
354 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
348 else:
355 else:
349 raw_input = lambda prompt='' : self._no_raw_input()
356 raw_input = lambda prompt='' : self._no_raw_input()
350
357
351 if py3compat.PY3:
358 if py3compat.PY3:
352 __builtin__.input = raw_input
359 __builtin__.input = raw_input
353 else:
360 else:
354 __builtin__.raw_input = raw_input
361 __builtin__.raw_input = raw_input
355
362
356 # Set the parent message of the display hook and out streams.
363 # Set the parent message of the display hook and out streams.
357 shell.displayhook.set_parent(parent)
364 shell.displayhook.set_parent(parent)
358 shell.display_pub.set_parent(parent)
365 shell.display_pub.set_parent(parent)
359 shell.data_pub.set_parent(parent)
366 shell.data_pub.set_parent(parent)
360 sys.stdout.set_parent(parent)
367 sys.stdout.set_parent(parent)
361 sys.stderr.set_parent(parent)
368 sys.stderr.set_parent(parent)
362
369
363 # Re-broadcast our input for the benefit of listening clients, and
370 # Re-broadcast our input for the benefit of listening clients, and
364 # start computing output
371 # start computing output
365 if not silent:
372 if not silent:
366 self._publish_pyin(code, parent, shell.execution_count)
373 self._publish_pyin(code, parent, shell.execution_count)
367
374
368 reply_content = {}
375 reply_content = {}
369 try:
376 try:
370 # FIXME: the shell calls the exception handler itself.
377 # FIXME: the shell calls the exception handler itself.
371 shell.run_cell(code, store_history=store_history, silent=silent)
378 shell.run_cell(code, store_history=store_history, silent=silent)
372 except:
379 except:
373 status = u'error'
380 status = u'error'
374 # FIXME: this code right now isn't being used yet by default,
381 # FIXME: this code right now isn't being used yet by default,
375 # because the run_cell() call above directly fires off exception
382 # because the run_cell() call above directly fires off exception
376 # reporting. This code, therefore, is only active in the scenario
383 # reporting. This code, therefore, is only active in the scenario
377 # where runlines itself has an unhandled exception. We need to
384 # where runlines itself has an unhandled exception. We need to
378 # uniformize this, for all exception construction to come from a
385 # uniformize this, for all exception construction to come from a
379 # single location in the codbase.
386 # single location in the codbase.
380 etype, evalue, tb = sys.exc_info()
387 etype, evalue, tb = sys.exc_info()
381 tb_list = traceback.format_exception(etype, evalue, tb)
388 tb_list = traceback.format_exception(etype, evalue, tb)
382 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
389 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
383 else:
390 else:
384 status = u'ok'
391 status = u'ok'
385
392
386 reply_content[u'status'] = status
393 reply_content[u'status'] = status
387
394
388 # Return the execution counter so clients can display prompts
395 # Return the execution counter so clients can display prompts
389 reply_content['execution_count'] = shell.execution_count - 1
396 reply_content['execution_count'] = shell.execution_count - 1
390
397
391 # FIXME - fish exception info out of shell, possibly left there by
398 # FIXME - fish exception info out of shell, possibly left there by
392 # runlines. We'll need to clean up this logic later.
399 # runlines. We'll need to clean up this logic later.
393 if shell._reply_content is not None:
400 if shell._reply_content is not None:
394 reply_content.update(shell._reply_content)
401 reply_content.update(shell._reply_content)
395 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
402 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
396 reply_content['engine_info'] = e_info
403 reply_content['engine_info'] = e_info
397 # reset after use
404 # reset after use
398 shell._reply_content = None
405 shell._reply_content = None
399
406
400 # At this point, we can tell whether the main code execution succeeded
407 # At this point, we can tell whether the main code execution succeeded
401 # or not. If it did, we proceed to evaluate user_variables/expressions
408 # or not. If it did, we proceed to evaluate user_variables/expressions
402 if reply_content['status'] == 'ok':
409 if reply_content['status'] == 'ok':
403 reply_content[u'user_variables'] = \
410 reply_content[u'user_variables'] = \
404 shell.user_variables(content.get(u'user_variables', []))
411 shell.user_variables(content.get(u'user_variables', []))
405 reply_content[u'user_expressions'] = \
412 reply_content[u'user_expressions'] = \
406 shell.user_expressions(content.get(u'user_expressions', {}))
413 shell.user_expressions(content.get(u'user_expressions', {}))
407 else:
414 else:
408 # If there was an error, don't even try to compute variables or
415 # If there was an error, don't even try to compute variables or
409 # expressions
416 # expressions
410 reply_content[u'user_variables'] = {}
417 reply_content[u'user_variables'] = {}
411 reply_content[u'user_expressions'] = {}
418 reply_content[u'user_expressions'] = {}
412
419
413 # Payloads should be retrieved regardless of outcome, so we can both
420 # Payloads should be retrieved regardless of outcome, so we can both
414 # recover partial output (that could have been generated early in a
421 # recover partial output (that could have been generated early in a
415 # block, before an error) and clear the payload system always.
422 # block, before an error) and clear the payload system always.
416 reply_content[u'payload'] = shell.payload_manager.read_payload()
423 reply_content[u'payload'] = shell.payload_manager.read_payload()
417 # Be agressive about clearing the payload because we don't want
424 # Be agressive about clearing the payload because we don't want
418 # it to sit in memory until the next execute_request comes in.
425 # it to sit in memory until the next execute_request comes in.
419 shell.payload_manager.clear_payload()
426 shell.payload_manager.clear_payload()
420
427
421 # Flush output before sending the reply.
428 # Flush output before sending the reply.
422 sys.stdout.flush()
429 sys.stdout.flush()
423 sys.stderr.flush()
430 sys.stderr.flush()
424 # FIXME: on rare occasions, the flush doesn't seem to make it to the
431 # FIXME: on rare occasions, the flush doesn't seem to make it to the
425 # clients... This seems to mitigate the problem, but we definitely need
432 # clients... This seems to mitigate the problem, but we definitely need
426 # to better understand what's going on.
433 # to better understand what's going on.
427 if self._execute_sleep:
434 if self._execute_sleep:
428 time.sleep(self._execute_sleep)
435 time.sleep(self._execute_sleep)
429
436
430 # Send the reply.
437 # Send the reply.
431 reply_content = json_clean(reply_content)
438 reply_content = json_clean(reply_content)
432
439
433 md['status'] = reply_content['status']
440 md['status'] = reply_content['status']
434 if reply_content['status'] == 'error' and \
441 if reply_content['status'] == 'error' and \
435 reply_content['ename'] == 'UnmetDependency':
442 reply_content['ename'] == 'UnmetDependency':
436 md['dependencies_met'] = False
443 md['dependencies_met'] = False
437
444
438 reply_msg = self.session.send(stream, u'execute_reply',
445 reply_msg = self.session.send(stream, u'execute_reply',
439 reply_content, parent, metadata=md,
446 reply_content, parent, metadata=md,
440 ident=ident)
447 ident=ident)
441
448
442 self.log.debug("%s", reply_msg)
449 self.log.debug("%s", reply_msg)
443
450
444 if not silent and reply_msg['content']['status'] == u'error':
451 if not silent and reply_msg['content']['status'] == u'error':
445 self._abort_queues()
452 self._abort_queues()
446
453
447 self._publish_status(u'idle', parent)
454 self._publish_status(u'idle', parent)
448
455
449 def complete_request(self, stream, ident, parent):
456 def complete_request(self, stream, ident, parent):
450 txt, matches = self._complete(parent)
457 txt, matches = self._complete(parent)
451 matches = {'matches' : matches,
458 matches = {'matches' : matches,
452 'matched_text' : txt,
459 'matched_text' : txt,
453 'status' : 'ok'}
460 'status' : 'ok'}
454 matches = json_clean(matches)
461 matches = json_clean(matches)
455 completion_msg = self.session.send(stream, 'complete_reply',
462 completion_msg = self.session.send(stream, 'complete_reply',
456 matches, parent, ident)
463 matches, parent, ident)
457 self.log.debug("%s", completion_msg)
464 self.log.debug("%s", completion_msg)
458
465
459 def object_info_request(self, stream, ident, parent):
466 def object_info_request(self, stream, ident, parent):
460 content = parent['content']
467 content = parent['content']
461 object_info = self.shell.object_inspect(content['oname'],
468 object_info = self.shell.object_inspect(content['oname'],
462 detail_level = content.get('detail_level', 0)
469 detail_level = content.get('detail_level', 0)
463 )
470 )
464 # Before we send this object over, we scrub it for JSON usage
471 # Before we send this object over, we scrub it for JSON usage
465 oinfo = json_clean(object_info)
472 oinfo = json_clean(object_info)
466 msg = self.session.send(stream, 'object_info_reply',
473 msg = self.session.send(stream, 'object_info_reply',
467 oinfo, parent, ident)
474 oinfo, parent, ident)
468 self.log.debug("%s", msg)
475 self.log.debug("%s", msg)
469
476
470 def history_request(self, stream, ident, parent):
477 def history_request(self, stream, ident, parent):
471 # We need to pull these out, as passing **kwargs doesn't work with
478 # We need to pull these out, as passing **kwargs doesn't work with
472 # unicode keys before Python 2.6.5.
479 # unicode keys before Python 2.6.5.
473 hist_access_type = parent['content']['hist_access_type']
480 hist_access_type = parent['content']['hist_access_type']
474 raw = parent['content']['raw']
481 raw = parent['content']['raw']
475 output = parent['content']['output']
482 output = parent['content']['output']
476 if hist_access_type == 'tail':
483 if hist_access_type == 'tail':
477 n = parent['content']['n']
484 n = parent['content']['n']
478 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
485 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
479 include_latest=True)
486 include_latest=True)
480
487
481 elif hist_access_type == 'range':
488 elif hist_access_type == 'range':
482 session = parent['content']['session']
489 session = parent['content']['session']
483 start = parent['content']['start']
490 start = parent['content']['start']
484 stop = parent['content']['stop']
491 stop = parent['content']['stop']
485 hist = self.shell.history_manager.get_range(session, start, stop,
492 hist = self.shell.history_manager.get_range(session, start, stop,
486 raw=raw, output=output)
493 raw=raw, output=output)
487
494
488 elif hist_access_type == 'search':
495 elif hist_access_type == 'search':
489 n = parent['content'].get('n')
496 n = parent['content'].get('n')
490 pattern = parent['content']['pattern']
497 pattern = parent['content']['pattern']
491 hist = self.shell.history_manager.search(pattern, raw=raw,
498 hist = self.shell.history_manager.search(pattern, raw=raw,
492 output=output, n=n)
499 output=output, n=n)
493
500
494 else:
501 else:
495 hist = []
502 hist = []
496 hist = list(hist)
503 hist = list(hist)
497 content = {'history' : hist}
504 content = {'history' : hist}
498 content = json_clean(content)
505 content = json_clean(content)
499 msg = self.session.send(stream, 'history_reply',
506 msg = self.session.send(stream, 'history_reply',
500 content, parent, ident)
507 content, parent, ident)
501 self.log.debug("Sending history reply with %i entries", len(hist))
508 self.log.debug("Sending history reply with %i entries", len(hist))
502
509
503 def connect_request(self, stream, ident, parent):
510 def connect_request(self, stream, ident, parent):
504 if self._recorded_ports is not None:
511 if self._recorded_ports is not None:
505 content = self._recorded_ports.copy()
512 content = self._recorded_ports.copy()
506 else:
513 else:
507 content = {}
514 content = {}
508 msg = self.session.send(stream, 'connect_reply',
515 msg = self.session.send(stream, 'connect_reply',
509 content, parent, ident)
516 content, parent, ident)
510 self.log.debug("%s", msg)
517 self.log.debug("%s", msg)
511
518
519 def version_request(self, stream, ident, parent):
520 vinfo = {
521 'version': version,
522 'version_major': version_major,
523 'version_minor': version_minor,
524 }
525 msg = self.session.send(stream, 'version_reply',
526 vinfo, parent, ident)
527 self.log.debug("%s", msg)
528
512 def shutdown_request(self, stream, ident, parent):
529 def shutdown_request(self, stream, ident, parent):
513 self.shell.exit_now = True
530 self.shell.exit_now = True
514 content = dict(status='ok')
531 content = dict(status='ok')
515 content.update(parent['content'])
532 content.update(parent['content'])
516 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
533 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
517 # same content, but different msg_id for broadcasting on IOPub
534 # same content, but different msg_id for broadcasting on IOPub
518 self._shutdown_message = self.session.msg(u'shutdown_reply',
535 self._shutdown_message = self.session.msg(u'shutdown_reply',
519 content, parent
536 content, parent
520 )
537 )
521
538
522 self._at_shutdown()
539 self._at_shutdown()
523 # call sys.exit after a short delay
540 # call sys.exit after a short delay
524 loop = ioloop.IOLoop.instance()
541 loop = ioloop.IOLoop.instance()
525 loop.add_timeout(time.time()+0.1, loop.stop)
542 loop.add_timeout(time.time()+0.1, loop.stop)
526
543
527 #---------------------------------------------------------------------------
544 #---------------------------------------------------------------------------
528 # Engine methods
545 # Engine methods
529 #---------------------------------------------------------------------------
546 #---------------------------------------------------------------------------
530
547
531 def apply_request(self, stream, ident, parent):
548 def apply_request(self, stream, ident, parent):
532 try:
549 try:
533 content = parent[u'content']
550 content = parent[u'content']
534 bufs = parent[u'buffers']
551 bufs = parent[u'buffers']
535 msg_id = parent['header']['msg_id']
552 msg_id = parent['header']['msg_id']
536 except:
553 except:
537 self.log.error("Got bad msg: %s", parent, exc_info=True)
554 self.log.error("Got bad msg: %s", parent, exc_info=True)
538 return
555 return
539
556
540 self._publish_status(u'busy', parent)
557 self._publish_status(u'busy', parent)
541
558
542 # Set the parent message of the display hook and out streams.
559 # Set the parent message of the display hook and out streams.
543 shell = self.shell
560 shell = self.shell
544 shell.displayhook.set_parent(parent)
561 shell.displayhook.set_parent(parent)
545 shell.display_pub.set_parent(parent)
562 shell.display_pub.set_parent(parent)
546 shell.data_pub.set_parent(parent)
563 shell.data_pub.set_parent(parent)
547 sys.stdout.set_parent(parent)
564 sys.stdout.set_parent(parent)
548 sys.stderr.set_parent(parent)
565 sys.stderr.set_parent(parent)
549
566
550 # 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)
551 # self.iopub_socket.send(pyin_msg)
568 # self.iopub_socket.send(pyin_msg)
552 # 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)
553 md = self._make_metadata(parent['metadata'])
570 md = self._make_metadata(parent['metadata'])
554 try:
571 try:
555 working = shell.user_ns
572 working = shell.user_ns
556
573
557 prefix = "_"+str(msg_id).replace("-","")+"_"
574 prefix = "_"+str(msg_id).replace("-","")+"_"
558
575
559 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
576 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
560
577
561 fname = getattr(f, '__name__', 'f')
578 fname = getattr(f, '__name__', 'f')
562
579
563 fname = prefix+"f"
580 fname = prefix+"f"
564 argname = prefix+"args"
581 argname = prefix+"args"
565 kwargname = prefix+"kwargs"
582 kwargname = prefix+"kwargs"
566 resultname = prefix+"result"
583 resultname = prefix+"result"
567
584
568 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
585 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
569 # print ns
586 # print ns
570 working.update(ns)
587 working.update(ns)
571 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
588 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
572 try:
589 try:
573 exec code in shell.user_global_ns, shell.user_ns
590 exec code in shell.user_global_ns, shell.user_ns
574 result = working.get(resultname)
591 result = working.get(resultname)
575 finally:
592 finally:
576 for key in ns.iterkeys():
593 for key in ns.iterkeys():
577 working.pop(key)
594 working.pop(key)
578
595
579 result_buf = serialize_object(result,
596 result_buf = serialize_object(result,
580 buffer_threshold=self.session.buffer_threshold,
597 buffer_threshold=self.session.buffer_threshold,
581 item_threshold=self.session.item_threshold,
598 item_threshold=self.session.item_threshold,
582 )
599 )
583
600
584 except:
601 except:
585 # invoke IPython traceback formatting
602 # invoke IPython traceback formatting
586 shell.showtraceback()
603 shell.showtraceback()
587 # FIXME - fish exception info out of shell, possibly left there by
604 # FIXME - fish exception info out of shell, possibly left there by
588 # run_code. We'll need to clean up this logic later.
605 # run_code. We'll need to clean up this logic later.
589 reply_content = {}
606 reply_content = {}
590 if shell._reply_content is not None:
607 if shell._reply_content is not None:
591 reply_content.update(shell._reply_content)
608 reply_content.update(shell._reply_content)
592 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')
593 reply_content['engine_info'] = e_info
610 reply_content['engine_info'] = e_info
594 # reset after use
611 # reset after use
595 shell._reply_content = None
612 shell._reply_content = None
596
613
597 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,
598 ident=self._topic('pyerr'))
615 ident=self._topic('pyerr'))
599 result_buf = []
616 result_buf = []
600
617
601 if reply_content['ename'] == 'UnmetDependency':
618 if reply_content['ename'] == 'UnmetDependency':
602 md['dependencies_met'] = False
619 md['dependencies_met'] = False
603 else:
620 else:
604 reply_content = {'status' : 'ok'}
621 reply_content = {'status' : 'ok'}
605
622
606 # put 'ok'/'error' status in header, for scheduler introspection:
623 # put 'ok'/'error' status in header, for scheduler introspection:
607 md['status'] = reply_content['status']
624 md['status'] = reply_content['status']
608
625
609 # flush i/o
626 # flush i/o
610 sys.stdout.flush()
627 sys.stdout.flush()
611 sys.stderr.flush()
628 sys.stderr.flush()
612
629
613 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
630 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
614 parent=parent, ident=ident,buffers=result_buf, metadata=md)
631 parent=parent, ident=ident,buffers=result_buf, metadata=md)
615
632
616 self._publish_status(u'idle', parent)
633 self._publish_status(u'idle', parent)
617
634
618 #---------------------------------------------------------------------------
635 #---------------------------------------------------------------------------
619 # Control messages
636 # Control messages
620 #---------------------------------------------------------------------------
637 #---------------------------------------------------------------------------
621
638
622 def abort_request(self, stream, ident, parent):
639 def abort_request(self, stream, ident, parent):
623 """abort a specifig msg by id"""
640 """abort a specifig msg by id"""
624 msg_ids = parent['content'].get('msg_ids', None)
641 msg_ids = parent['content'].get('msg_ids', None)
625 if isinstance(msg_ids, basestring):
642 if isinstance(msg_ids, basestring):
626 msg_ids = [msg_ids]
643 msg_ids = [msg_ids]
627 if not msg_ids:
644 if not msg_ids:
628 self.abort_queues()
645 self.abort_queues()
629 for mid in msg_ids:
646 for mid in msg_ids:
630 self.aborted.add(str(mid))
647 self.aborted.add(str(mid))
631
648
632 content = dict(status='ok')
649 content = dict(status='ok')
633 reply_msg = self.session.send(stream, 'abort_reply', content=content,
650 reply_msg = self.session.send(stream, 'abort_reply', content=content,
634 parent=parent, ident=ident)
651 parent=parent, ident=ident)
635 self.log.debug("%s", reply_msg)
652 self.log.debug("%s", reply_msg)
636
653
637 def clear_request(self, stream, idents, parent):
654 def clear_request(self, stream, idents, parent):
638 """Clear our namespace."""
655 """Clear our namespace."""
639 self.shell.reset(False)
656 self.shell.reset(False)
640 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
657 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
641 content = dict(status='ok'))
658 content = dict(status='ok'))
642
659
643
660
644 #---------------------------------------------------------------------------
661 #---------------------------------------------------------------------------
645 # Protected interface
662 # Protected interface
646 #---------------------------------------------------------------------------
663 #---------------------------------------------------------------------------
647
664
648
665
649 def _wrap_exception(self, method=None):
666 def _wrap_exception(self, method=None):
650 # import here, because _wrap_exception is only used in parallel,
667 # import here, because _wrap_exception is only used in parallel,
651 # and parallel has higher min pyzmq version
668 # and parallel has higher min pyzmq version
652 from IPython.parallel.error import wrap_exception
669 from IPython.parallel.error import wrap_exception
653 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)
654 content = wrap_exception(e_info)
671 content = wrap_exception(e_info)
655 return content
672 return content
656
673
657 def _topic(self, topic):
674 def _topic(self, topic):
658 """prefixed topic for IOPub messages"""
675 """prefixed topic for IOPub messages"""
659 if self.int_id >= 0:
676 if self.int_id >= 0:
660 base = "engine.%i" % self.int_id
677 base = "engine.%i" % self.int_id
661 else:
678 else:
662 base = "kernel.%s" % self.ident
679 base = "kernel.%s" % self.ident
663
680
664 return py3compat.cast_bytes("%s.%s" % (base, topic))
681 return py3compat.cast_bytes("%s.%s" % (base, topic))
665
682
666 def _abort_queues(self):
683 def _abort_queues(self):
667 for stream in self.shell_streams:
684 for stream in self.shell_streams:
668 if stream:
685 if stream:
669 self._abort_queue(stream)
686 self._abort_queue(stream)
670
687
671 def _abort_queue(self, stream):
688 def _abort_queue(self, stream):
672 poller = zmq.Poller()
689 poller = zmq.Poller()
673 poller.register(stream.socket, zmq.POLLIN)
690 poller.register(stream.socket, zmq.POLLIN)
674 while True:
691 while True:
675 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
692 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
676 if msg is None:
693 if msg is None:
677 return
694 return
678
695
679 self.log.info("Aborting:")
696 self.log.info("Aborting:")
680 self.log.info("%s", msg)
697 self.log.info("%s", msg)
681 msg_type = msg['header']['msg_type']
698 msg_type = msg['header']['msg_type']
682 reply_type = msg_type.split('_')[0] + '_reply'
699 reply_type = msg_type.split('_')[0] + '_reply'
683
700
684 status = {'status' : 'aborted'}
701 status = {'status' : 'aborted'}
685 md = {'engine' : self.ident}
702 md = {'engine' : self.ident}
686 md.update(status)
703 md.update(status)
687 reply_msg = self.session.send(stream, reply_type, metadata=md,
704 reply_msg = self.session.send(stream, reply_type, metadata=md,
688 content=status, parent=msg, ident=idents)
705 content=status, parent=msg, ident=idents)
689 self.log.debug("%s", reply_msg)
706 self.log.debug("%s", reply_msg)
690 # 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
691 # be set shorter for true asynchronous clients.
708 # be set shorter for true asynchronous clients.
692 poller.poll(50)
709 poller.poll(50)
693
710
694
711
695 def _no_raw_input(self):
712 def _no_raw_input(self):
696 """Raise StdinNotImplentedError if active frontend doesn't support
713 """Raise StdinNotImplentedError if active frontend doesn't support
697 stdin."""
714 stdin."""
698 raise StdinNotImplementedError("raw_input was called, but this "
715 raise StdinNotImplementedError("raw_input was called, but this "
699 "frontend does not support stdin.")
716 "frontend does not support stdin.")
700
717
701 def _raw_input(self, prompt, ident, parent):
718 def _raw_input(self, prompt, ident, parent):
702 # Flush output before making the request.
719 # Flush output before making the request.
703 sys.stderr.flush()
720 sys.stderr.flush()
704 sys.stdout.flush()
721 sys.stdout.flush()
705
722
706 # Send the input request.
723 # Send the input request.
707 content = json_clean(dict(prompt=prompt))
724 content = json_clean(dict(prompt=prompt))
708 self.session.send(self.stdin_socket, u'input_request', content, parent,
725 self.session.send(self.stdin_socket, u'input_request', content, parent,
709 ident=ident)
726 ident=ident)
710
727
711 # Await a response.
728 # Await a response.
712 while True:
729 while True:
713 try:
730 try:
714 ident, reply = self.session.recv(self.stdin_socket, 0)
731 ident, reply = self.session.recv(self.stdin_socket, 0)
715 except Exception:
732 except Exception:
716 self.log.warn("Invalid Message:", exc_info=True)
733 self.log.warn("Invalid Message:", exc_info=True)
717 else:
734 else:
718 break
735 break
719 try:
736 try:
720 value = reply['content']['value']
737 value = reply['content']['value']
721 except:
738 except:
722 self.log.error("Got bad raw_input reply: ")
739 self.log.error("Got bad raw_input reply: ")
723 self.log.error("%s", parent)
740 self.log.error("%s", parent)
724 value = ''
741 value = ''
725 if value == '\x04':
742 if value == '\x04':
726 # EOF
743 # EOF
727 raise EOFError
744 raise EOFError
728 return value
745 return value
729
746
730 def _complete(self, msg):
747 def _complete(self, msg):
731 c = msg['content']
748 c = msg['content']
732 try:
749 try:
733 cpos = int(c['cursor_pos'])
750 cpos = int(c['cursor_pos'])
734 except:
751 except:
735 # 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
736 # 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
737 # the text, if there's any, and otherwise of the line
754 # the text, if there's any, and otherwise of the line
738 cpos = len(c['text'])
755 cpos = len(c['text'])
739 if cpos==0:
756 if cpos==0:
740 cpos = len(c['line'])
757 cpos = len(c['line'])
741 return self.shell.complete(c['text'], c['line'], cpos)
758 return self.shell.complete(c['text'], c['line'], cpos)
742
759
743 def _object_info(self, context):
760 def _object_info(self, context):
744 symbol, leftover = self._symbol_from_context(context)
761 symbol, leftover = self._symbol_from_context(context)
745 if symbol is not None and not leftover:
762 if symbol is not None and not leftover:
746 doc = getattr(symbol, '__doc__', '')
763 doc = getattr(symbol, '__doc__', '')
747 else:
764 else:
748 doc = ''
765 doc = ''
749 object_info = dict(docstring = doc)
766 object_info = dict(docstring = doc)
750 return object_info
767 return object_info
751
768
752 def _symbol_from_context(self, context):
769 def _symbol_from_context(self, context):
753 if not context:
770 if not context:
754 return None, context
771 return None, context
755
772
756 base_symbol_string = context[0]
773 base_symbol_string = context[0]
757 symbol = self.shell.user_ns.get(base_symbol_string, None)
774 symbol = self.shell.user_ns.get(base_symbol_string, None)
758 if symbol is None:
775 if symbol is None:
759 symbol = __builtin__.__dict__.get(base_symbol_string, None)
776 symbol = __builtin__.__dict__.get(base_symbol_string, None)
760 if symbol is None:
777 if symbol is None:
761 return None, context
778 return None, context
762
779
763 context = context[1:]
780 context = context[1:]
764 for i, name in enumerate(context):
781 for i, name in enumerate(context):
765 new_symbol = getattr(symbol, name, None)
782 new_symbol = getattr(symbol, name, None)
766 if new_symbol is None:
783 if new_symbol is None:
767 return symbol, context[i:]
784 return symbol, context[i:]
768 else:
785 else:
769 symbol = new_symbol
786 symbol = new_symbol
770
787
771 return symbol, []
788 return symbol, []
772
789
773 def _at_shutdown(self):
790 def _at_shutdown(self):
774 """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.
775 """
792 """
776 # io.rprint("Kernel at_shutdown") # dbg
793 # io.rprint("Kernel at_shutdown") # dbg
777 if self._shutdown_message is not None:
794 if self._shutdown_message is not None:
778 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'))
779 self.log.debug("%s", self._shutdown_message)
796 self.log.debug("%s", self._shutdown_message)
780 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
797 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
781
798
782 #-----------------------------------------------------------------------------
799 #-----------------------------------------------------------------------------
783 # Aliases and Flags for the IPKernelApp
800 # Aliases and Flags for the IPKernelApp
784 #-----------------------------------------------------------------------------
801 #-----------------------------------------------------------------------------
785
802
786 flags = dict(kernel_flags)
803 flags = dict(kernel_flags)
787 flags.update(shell_flags)
804 flags.update(shell_flags)
788
805
789 addflag = lambda *args: flags.update(boolean_flag(*args))
806 addflag = lambda *args: flags.update(boolean_flag(*args))
790
807
791 flags['pylab'] = (
808 flags['pylab'] = (
792 {'IPKernelApp' : {'pylab' : 'auto'}},
809 {'IPKernelApp' : {'pylab' : 'auto'}},
793 """Pre-load matplotlib and numpy for interactive use with
810 """Pre-load matplotlib and numpy for interactive use with
794 the default matplotlib backend."""
811 the default matplotlib backend."""
795 )
812 )
796
813
797 aliases = dict(kernel_aliases)
814 aliases = dict(kernel_aliases)
798 aliases.update(shell_aliases)
815 aliases.update(shell_aliases)
799
816
800 #-----------------------------------------------------------------------------
817 #-----------------------------------------------------------------------------
801 # The IPKernelApp class
818 # The IPKernelApp class
802 #-----------------------------------------------------------------------------
819 #-----------------------------------------------------------------------------
803
820
804 class IPKernelApp(KernelApp, InteractiveShellApp):
821 class IPKernelApp(KernelApp, InteractiveShellApp):
805 name = 'ipkernel'
822 name = 'ipkernel'
806
823
807 aliases = Dict(aliases)
824 aliases = Dict(aliases)
808 flags = Dict(flags)
825 flags = Dict(flags)
809 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
826 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
810
827
811 @catch_config_error
828 @catch_config_error
812 def initialize(self, argv=None):
829 def initialize(self, argv=None):
813 super(IPKernelApp, self).initialize(argv)
830 super(IPKernelApp, self).initialize(argv)
814 self.init_path()
831 self.init_path()
815 self.init_shell()
832 self.init_shell()
816 self.init_gui_pylab()
833 self.init_gui_pylab()
817 self.init_extensions()
834 self.init_extensions()
818 self.init_code()
835 self.init_code()
819
836
820 def init_kernel(self):
837 def init_kernel(self):
821
838
822 shell_stream = ZMQStream(self.shell_socket)
839 shell_stream = ZMQStream(self.shell_socket)
823
840
824 kernel = Kernel(config=self.config, session=self.session,
841 kernel = Kernel(config=self.config, session=self.session,
825 shell_streams=[shell_stream],
842 shell_streams=[shell_stream],
826 iopub_socket=self.iopub_socket,
843 iopub_socket=self.iopub_socket,
827 stdin_socket=self.stdin_socket,
844 stdin_socket=self.stdin_socket,
828 log=self.log,
845 log=self.log,
829 profile_dir=self.profile_dir,
846 profile_dir=self.profile_dir,
830 )
847 )
831 self.kernel = kernel
848 self.kernel = kernel
832 kernel.record_ports(self.ports)
849 kernel.record_ports(self.ports)
833 shell = kernel.shell
850 shell = kernel.shell
834
851
835 def init_gui_pylab(self):
852 def init_gui_pylab(self):
836 """Enable GUI event loop integration, taking pylab into account."""
853 """Enable GUI event loop integration, taking pylab into account."""
837
854
838 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
855 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
839 # to ensure that any exception is printed straight to stderr.
856 # to ensure that any exception is printed straight to stderr.
840 # Normally _showtraceback associates the reply with an execution,
857 # Normally _showtraceback associates the reply with an execution,
841 # which means frontends will never draw it, as this exception
858 # which means frontends will never draw it, as this exception
842 # is not associated with any execute request.
859 # is not associated with any execute request.
843
860
844 shell = self.shell
861 shell = self.shell
845 _showtraceback = shell._showtraceback
862 _showtraceback = shell._showtraceback
846 try:
863 try:
847 # replace pyerr-sending traceback with stderr
864 # replace pyerr-sending traceback with stderr
848 def print_tb(etype, evalue, stb):
865 def print_tb(etype, evalue, stb):
849 print ("GUI event loop or pylab initialization failed",
866 print ("GUI event loop or pylab initialization failed",
850 file=io.stderr)
867 file=io.stderr)
851 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
868 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
852 shell._showtraceback = print_tb
869 shell._showtraceback = print_tb
853 InteractiveShellApp.init_gui_pylab(self)
870 InteractiveShellApp.init_gui_pylab(self)
854 finally:
871 finally:
855 shell._showtraceback = _showtraceback
872 shell._showtraceback = _showtraceback
856
873
857 def init_shell(self):
874 def init_shell(self):
858 self.shell = self.kernel.shell
875 self.shell = self.kernel.shell
859 self.shell.configurables.append(self)
876 self.shell.configurables.append(self)
860
877
861
878
862 #-----------------------------------------------------------------------------
879 #-----------------------------------------------------------------------------
863 # Kernel main and launch functions
880 # Kernel main and launch functions
864 #-----------------------------------------------------------------------------
881 #-----------------------------------------------------------------------------
865
882
866 def launch_kernel(*args, **kwargs):
883 def launch_kernel(*args, **kwargs):
867 """Launches a localhost IPython kernel, binding to the specified ports.
884 """Launches a localhost IPython kernel, binding to the specified ports.
868
885
869 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
870 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.
871
888
872 Returns
889 Returns
873 -------
890 -------
874 A tuple of form:
891 A tuple of form:
875 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
892 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
876 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.
877 """
894 """
878 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
895 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
879 *args, **kwargs)
896 *args, **kwargs)
880
897
881
898
882 def embed_kernel(module=None, local_ns=None, **kwargs):
899 def embed_kernel(module=None, local_ns=None, **kwargs):
883 """Embed and start an IPython kernel in a given scope.
900 """Embed and start an IPython kernel in a given scope.
884
901
885 Parameters
902 Parameters
886 ----------
903 ----------
887 module : ModuleType, optional
904 module : ModuleType, optional
888 The module to load into IPython globals (default: caller)
905 The module to load into IPython globals (default: caller)
889 local_ns : dict, optional
906 local_ns : dict, optional
890 The namespace to load into IPython user namespace (default: caller)
907 The namespace to load into IPython user namespace (default: caller)
891
908
892 kwargs : various, optional
909 kwargs : various, optional
893 Further keyword args are relayed to the KernelApp constructor,
910 Further keyword args are relayed to the KernelApp constructor,
894 allowing configuration of the Kernel. Will only have an effect
911 allowing configuration of the Kernel. Will only have an effect
895 on the first embed_kernel call for a given process.
912 on the first embed_kernel call for a given process.
896
913
897 """
914 """
898 # 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
899 if IPKernelApp.initialized():
916 if IPKernelApp.initialized():
900 app = IPKernelApp.instance()
917 app = IPKernelApp.instance()
901 else:
918 else:
902 app = IPKernelApp.instance(**kwargs)
919 app = IPKernelApp.instance(**kwargs)
903 app.initialize([])
920 app.initialize([])
904 # Undo unnecessary sys module mangling from init_sys_modules.
921 # Undo unnecessary sys module mangling from init_sys_modules.
905 # This would not be necessary if we could prevent it
922 # This would not be necessary if we could prevent it
906 # in the first place by using a different InteractiveShell
923 # in the first place by using a different InteractiveShell
907 # subclass, as in the regular embed case.
924 # subclass, as in the regular embed case.
908 main = app.kernel.shell._orig_sys_modules_main_mod
925 main = app.kernel.shell._orig_sys_modules_main_mod
909 if main is not None:
926 if main is not None:
910 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
927 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
911
928
912 # load the calling scope if not given
929 # load the calling scope if not given
913 (caller_module, caller_locals) = extract_module_locals(1)
930 (caller_module, caller_locals) = extract_module_locals(1)
914 if module is None:
931 if module is None:
915 module = caller_module
932 module = caller_module
916 if local_ns is None:
933 if local_ns is None:
917 local_ns = caller_locals
934 local_ns = caller_locals
918
935
919 app.kernel.user_module = module
936 app.kernel.user_module = module
920 app.kernel.user_ns = local_ns
937 app.kernel.user_ns = local_ns
921 app.shell.set_completer_frame()
938 app.shell.set_completer_frame()
922 app.start()
939 app.start()
923
940
924 def main():
941 def main():
925 """Run an IPKernel as an application"""
942 """Run an IPKernel as an application"""
926 app = IPKernelApp.instance()
943 app = IPKernelApp.instance()
927 app.initialize()
944 app.initialize()
928 app.start()
945 app.start()
929
946
930
947
931 if __name__ == '__main__':
948 if __name__ == '__main__':
932 main()
949 main()
General Comments 0
You need to be logged in to leave comments. Login now