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