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