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