##// END OF EJS Templates
re-enter kernel.eventloop after catching SIGINT
MinRK -
Show More
@@ -1,652 +1,657 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 from signal import (
25 from signal import (
26 signal, default_int_handler, SIGINT, SIG_IGN
26 signal, default_int_handler, SIGINT, SIG_IGN
27 )
27 )
28 # System library imports.
28 # System library imports.
29 import zmq
29 import zmq
30
30
31 # Local imports.
31 # Local imports.
32 from IPython.core import pylabtools
32 from IPython.core import pylabtools
33 from IPython.config.configurable import Configurable
33 from IPython.config.configurable import Configurable
34 from IPython.config.application import boolean_flag, catch_config_error
34 from IPython.config.application import boolean_flag, catch_config_error
35 from IPython.core.application import ProfileDir
35 from IPython.core.application import ProfileDir
36 from IPython.core.error import StdinNotImplementedError
36 from IPython.core.error import StdinNotImplementedError
37 from IPython.core.shellapp import (
37 from IPython.core.shellapp import (
38 InteractiveShellApp, shell_flags, shell_aliases
38 InteractiveShellApp, shell_flags, shell_aliases
39 )
39 )
40 from IPython.utils import io
40 from IPython.utils import io
41 from IPython.utils import py3compat
41 from IPython.utils import py3compat
42 from IPython.utils.jsonutil import json_clean
42 from IPython.utils.jsonutil import json_clean
43 from IPython.utils.traitlets import (
43 from IPython.utils.traitlets import (
44 Any, Instance, Float, Dict, CaselessStrEnum
44 Any, Instance, Float, Dict, CaselessStrEnum
45 )
45 )
46
46
47 from entry_point import base_launch_kernel
47 from entry_point import base_launch_kernel
48 from kernelapp import KernelApp, kernel_flags, kernel_aliases
48 from kernelapp import KernelApp, kernel_flags, kernel_aliases
49 from session import Session, Message
49 from session import Session, Message
50 from zmqshell import ZMQInteractiveShell
50 from zmqshell import ZMQInteractiveShell
51
51
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Main kernel class
54 # Main kernel class
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 class Kernel(Configurable):
57 class Kernel(Configurable):
58
58
59 #---------------------------------------------------------------------------
59 #---------------------------------------------------------------------------
60 # Kernel interface
60 # Kernel interface
61 #---------------------------------------------------------------------------
61 #---------------------------------------------------------------------------
62
62
63 # attribute to override with a GUI
63 # attribute to override with a GUI
64 eventloop = Any(None)
64 eventloop = Any(None)
65
65
66 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
66 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
67 session = Instance(Session)
67 session = Instance(Session)
68 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
68 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
69 shell_socket = Instance('zmq.Socket')
69 shell_socket = Instance('zmq.Socket')
70 iopub_socket = Instance('zmq.Socket')
70 iopub_socket = Instance('zmq.Socket')
71 stdin_socket = Instance('zmq.Socket')
71 stdin_socket = Instance('zmq.Socket')
72 log = Instance(logging.Logger)
72 log = Instance(logging.Logger)
73
73
74 # Private interface
74 # Private interface
75
75
76 # Time to sleep after flushing the stdout/err buffers in each execute
76 # Time to sleep after flushing the stdout/err buffers in each execute
77 # cycle. While this introduces a hard limit on the minimal latency of the
77 # cycle. While this introduces a hard limit on the minimal latency of the
78 # execute cycle, it helps prevent output synchronization problems for
78 # execute cycle, it helps prevent output synchronization problems for
79 # clients.
79 # clients.
80 # Units are in seconds. The minimum zmq latency on local host is probably
80 # Units are in seconds. The minimum zmq latency on local host is probably
81 # ~150 microseconds, set this to 500us for now. We may need to increase it
81 # ~150 microseconds, set this to 500us for now. We may need to increase it
82 # a little if it's not enough after more interactive testing.
82 # a little if it's not enough after more interactive testing.
83 _execute_sleep = Float(0.0005, config=True)
83 _execute_sleep = Float(0.0005, config=True)
84
84
85 # Frequency of the kernel's event loop.
85 # Frequency of the kernel's event loop.
86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
87 # adapt to milliseconds.
87 # adapt to milliseconds.
88 _poll_interval = Float(0.05, config=True)
88 _poll_interval = Float(0.05, config=True)
89
89
90 # If the shutdown was requested over the network, we leave here the
90 # If the shutdown was requested over the network, we leave here the
91 # necessary reply message so it can be sent by our registered atexit
91 # necessary reply message so it can be sent by our registered atexit
92 # handler. This ensures that the reply is only sent to clients truly at
92 # handler. This ensures that the reply is only sent to clients truly at
93 # the end of our shutdown process (which happens after the underlying
93 # the end of our shutdown process (which happens after the underlying
94 # IPython shell's own shutdown).
94 # IPython shell's own shutdown).
95 _shutdown_message = None
95 _shutdown_message = None
96
96
97 # This is a dict of port number that the kernel is listening on. It is set
97 # This is a dict of port number that the kernel is listening on. It is set
98 # by record_ports and used by connect_request.
98 # by record_ports and used by connect_request.
99 _recorded_ports = Dict()
99 _recorded_ports = Dict()
100
100
101
101
102
102
103 def __init__(self, **kwargs):
103 def __init__(self, **kwargs):
104 super(Kernel, self).__init__(**kwargs)
104 super(Kernel, self).__init__(**kwargs)
105
105
106 # Before we even start up the shell, register *first* our exit handlers
106 # Before we even start up the shell, register *first* our exit handlers
107 # so they come before the shell's
107 # so they come before the shell's
108 atexit.register(self._at_shutdown)
108 atexit.register(self._at_shutdown)
109
109
110 # Initialize the InteractiveShell subclass
110 # Initialize the InteractiveShell subclass
111 self.shell = ZMQInteractiveShell.instance(config=self.config,
111 self.shell = ZMQInteractiveShell.instance(config=self.config,
112 profile_dir = self.profile_dir,
112 profile_dir = self.profile_dir,
113 )
113 )
114 self.shell.displayhook.session = self.session
114 self.shell.displayhook.session = self.session
115 self.shell.displayhook.pub_socket = self.iopub_socket
115 self.shell.displayhook.pub_socket = self.iopub_socket
116 self.shell.display_pub.session = self.session
116 self.shell.display_pub.session = self.session
117 self.shell.display_pub.pub_socket = self.iopub_socket
117 self.shell.display_pub.pub_socket = self.iopub_socket
118
118
119 # TMP - hack while developing
119 # TMP - hack while developing
120 self.shell._reply_content = None
120 self.shell._reply_content = None
121
121
122 # Build dict of handlers for message types
122 # Build dict of handlers for message types
123 msg_types = [ 'execute_request', 'complete_request',
123 msg_types = [ 'execute_request', 'complete_request',
124 'object_info_request', 'history_request',
124 'object_info_request', 'history_request',
125 'connect_request', 'shutdown_request']
125 'connect_request', 'shutdown_request']
126 self.handlers = {}
126 self.handlers = {}
127 for msg_type in msg_types:
127 for msg_type in msg_types:
128 self.handlers[msg_type] = getattr(self, msg_type)
128 self.handlers[msg_type] = getattr(self, msg_type)
129
129
130 def do_one_iteration(self):
130 def do_one_iteration(self):
131 """Do one iteration of the kernel's evaluation loop.
131 """Do one iteration of the kernel's evaluation loop.
132 """
132 """
133 try:
133 try:
134 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
134 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
135 except Exception:
135 except Exception:
136 self.log.warn("Invalid Message:", exc_info=True)
136 self.log.warn("Invalid Message:", exc_info=True)
137 return
137 return
138 if msg is None:
138 if msg is None:
139 return
139 return
140
140
141 msg_type = msg['header']['msg_type']
141 msg_type = msg['header']['msg_type']
142
142
143 # This assert will raise in versions of zeromq 2.0.7 and lesser.
143 # This assert will raise in versions of zeromq 2.0.7 and lesser.
144 # We now require 2.0.8 or above, so we can uncomment for safety.
144 # We now require 2.0.8 or above, so we can uncomment for safety.
145 # print(ident,msg, file=sys.__stdout__)
145 # print(ident,msg, file=sys.__stdout__)
146 assert ident is not None, "Missing message part."
146 assert ident is not None, "Missing message part."
147
147
148 # Print some info about this message and leave a '--->' marker, so it's
148 # Print some info about this message and leave a '--->' marker, so it's
149 # easier to trace visually the message chain when debugging. Each
149 # easier to trace visually the message chain when debugging. Each
150 # handler prints its message at the end.
150 # handler prints its message at the end.
151 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
151 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
152 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
152 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
153
153
154 # Find and call actual handler for message
154 # Find and call actual handler for message
155 handler = self.handlers.get(msg_type, None)
155 handler = self.handlers.get(msg_type, None)
156 if handler is None:
156 if handler is None:
157 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
157 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
158 else:
158 else:
159 handler(ident, msg)
159 handler(ident, msg)
160
160
161 # Check whether we should exit, in case the incoming message set the
161 # Check whether we should exit, in case the incoming message set the
162 # exit flag on
162 # exit flag on
163 if self.shell.exit_now:
163 if self.shell.exit_now:
164 self.log.debug('\nExiting IPython kernel...')
164 self.log.debug('\nExiting IPython kernel...')
165 # We do a normal, clean exit, which allows any actions registered
165 # We do a normal, clean exit, which allows any actions registered
166 # via atexit (such as history saving) to take place.
166 # via atexit (such as history saving) to take place.
167 sys.exit(0)
167 sys.exit(0)
168
168
169
169
170 def start(self):
170 def start(self):
171 """ Start the kernel main loop.
171 """ Start the kernel main loop.
172 """
172 """
173 # a KeyboardInterrupt (SIGINT) can occur on any python statement, so
173 # a KeyboardInterrupt (SIGINT) can occur on any python statement, so
174 # let's ignore (SIG_IGN) them until we're in a place to handle them properly
174 # let's ignore (SIG_IGN) them until we're in a place to handle them properly
175 signal(SIGINT,SIG_IGN)
175 signal(SIGINT,SIG_IGN)
176 poller = zmq.Poller()
176 poller = zmq.Poller()
177 poller.register(self.shell_socket, zmq.POLLIN)
177 poller.register(self.shell_socket, zmq.POLLIN)
178 # loop while self.eventloop has not been overridden
178 # loop while self.eventloop has not been overridden
179 while self.eventloop is None:
179 while self.eventloop is None:
180 try:
180 try:
181 # scale by extra factor of 10, because there is no
181 # scale by extra factor of 10, because there is no
182 # reason for this to be anything less than ~ 0.1s
182 # reason for this to be anything less than ~ 0.1s
183 # since it is a real poller and will respond
183 # since it is a real poller and will respond
184 # to events immediately
184 # to events immediately
185
185
186 # double nested try/except, to properly catch KeyboardInterrupt
186 # double nested try/except, to properly catch KeyboardInterrupt
187 # due to pyzmq Issue #130
187 # due to pyzmq Issue #130
188 try:
188 try:
189 poller.poll(10*1000*self._poll_interval)
189 poller.poll(10*1000*self._poll_interval)
190 # restore raising of KeyboardInterrupt
190 # restore raising of KeyboardInterrupt
191 signal(SIGINT, default_int_handler)
191 signal(SIGINT, default_int_handler)
192 self.do_one_iteration()
192 self.do_one_iteration()
193 except:
193 except:
194 raise
194 raise
195 finally:
195 finally:
196 # prevent raising of KeyboardInterrupt
196 # prevent raising of KeyboardInterrupt
197 signal(SIGINT,SIG_IGN)
197 signal(SIGINT,SIG_IGN)
198 except KeyboardInterrupt:
198 except KeyboardInterrupt:
199 # Ctrl-C shouldn't crash the kernel
199 # Ctrl-C shouldn't crash the kernel
200 io.raw_print("KeyboardInterrupt caught in kernel")
200 io.raw_print("KeyboardInterrupt caught in kernel")
201 # stop ignoring sigint, now that we are out of our own loop,
201 # stop ignoring sigint, now that we are out of our own loop,
202 # we don't want to prevent future code from handling it
202 # we don't want to prevent future code from handling it
203 signal(SIGINT, default_int_handler)
203 signal(SIGINT, default_int_handler)
204 if self.eventloop is not None:
204 while self.eventloop is not None:
205 try:
205 try:
206 self.eventloop(self)
206 self.eventloop(self)
207 except KeyboardInterrupt:
207 except KeyboardInterrupt:
208 # Ctrl-C shouldn't crash the kernel
208 # Ctrl-C shouldn't crash the kernel
209 io.raw_print("KeyboardInterrupt caught in kernel")
209 io.raw_print("KeyboardInterrupt caught in kernel")
210 continue
211 else:
212 # eventloop exited cleanly, this means we should stop (right?)
213 self.eventloop = None
214 break
210
215
211
216
212 def record_ports(self, ports):
217 def record_ports(self, ports):
213 """Record the ports that this kernel is using.
218 """Record the ports that this kernel is using.
214
219
215 The creator of the Kernel instance must call this methods if they
220 The creator of the Kernel instance must call this methods if they
216 want the :meth:`connect_request` method to return the port numbers.
221 want the :meth:`connect_request` method to return the port numbers.
217 """
222 """
218 self._recorded_ports = ports
223 self._recorded_ports = ports
219
224
220 #---------------------------------------------------------------------------
225 #---------------------------------------------------------------------------
221 # Kernel request handlers
226 # Kernel request handlers
222 #---------------------------------------------------------------------------
227 #---------------------------------------------------------------------------
223
228
224 def _publish_pyin(self, code, parent):
229 def _publish_pyin(self, code, parent):
225 """Publish the code request on the pyin stream."""
230 """Publish the code request on the pyin stream."""
226
231
227 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
232 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
228 parent=parent)
233 parent=parent)
229
234
230 def execute_request(self, ident, parent):
235 def execute_request(self, ident, parent):
231
236
232 self.session.send(self.iopub_socket,
237 self.session.send(self.iopub_socket,
233 u'status',
238 u'status',
234 {u'execution_state':u'busy'},
239 {u'execution_state':u'busy'},
235 parent=parent )
240 parent=parent )
236
241
237 try:
242 try:
238 content = parent[u'content']
243 content = parent[u'content']
239 code = content[u'code']
244 code = content[u'code']
240 silent = content[u'silent']
245 silent = content[u'silent']
241 except:
246 except:
242 self.log.error("Got bad msg: ")
247 self.log.error("Got bad msg: ")
243 self.log.error(str(Message(parent)))
248 self.log.error(str(Message(parent)))
244 return
249 return
245
250
246 shell = self.shell # we'll need this a lot here
251 shell = self.shell # we'll need this a lot here
247
252
248 # Replace raw_input. Note that is not sufficient to replace
253 # Replace raw_input. Note that is not sufficient to replace
249 # raw_input in the user namespace.
254 # raw_input in the user namespace.
250 if content.get('allow_stdin', False):
255 if content.get('allow_stdin', False):
251 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
256 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
252 else:
257 else:
253 raw_input = lambda prompt='' : self._no_raw_input()
258 raw_input = lambda prompt='' : self._no_raw_input()
254
259
255 if py3compat.PY3:
260 if py3compat.PY3:
256 __builtin__.input = raw_input
261 __builtin__.input = raw_input
257 else:
262 else:
258 __builtin__.raw_input = raw_input
263 __builtin__.raw_input = raw_input
259
264
260 # Set the parent message of the display hook and out streams.
265 # Set the parent message of the display hook and out streams.
261 shell.displayhook.set_parent(parent)
266 shell.displayhook.set_parent(parent)
262 shell.display_pub.set_parent(parent)
267 shell.display_pub.set_parent(parent)
263 sys.stdout.set_parent(parent)
268 sys.stdout.set_parent(parent)
264 sys.stderr.set_parent(parent)
269 sys.stderr.set_parent(parent)
265
270
266 # Re-broadcast our input for the benefit of listening clients, and
271 # Re-broadcast our input for the benefit of listening clients, and
267 # start computing output
272 # start computing output
268 if not silent:
273 if not silent:
269 self._publish_pyin(code, parent)
274 self._publish_pyin(code, parent)
270
275
271 reply_content = {}
276 reply_content = {}
272 try:
277 try:
273 if silent:
278 if silent:
274 # run_code uses 'exec' mode, so no displayhook will fire, and it
279 # run_code uses 'exec' mode, so no displayhook will fire, and it
275 # doesn't call logging or history manipulations. Print
280 # doesn't call logging or history manipulations. Print
276 # statements in that code will obviously still execute.
281 # statements in that code will obviously still execute.
277 shell.run_code(code)
282 shell.run_code(code)
278 else:
283 else:
279 # FIXME: the shell calls the exception handler itself.
284 # FIXME: the shell calls the exception handler itself.
280 shell.run_cell(code, store_history=True)
285 shell.run_cell(code, store_history=True)
281 except:
286 except:
282 status = u'error'
287 status = u'error'
283 # FIXME: this code right now isn't being used yet by default,
288 # FIXME: this code right now isn't being used yet by default,
284 # because the run_cell() call above directly fires off exception
289 # because the run_cell() call above directly fires off exception
285 # reporting. This code, therefore, is only active in the scenario
290 # reporting. This code, therefore, is only active in the scenario
286 # where runlines itself has an unhandled exception. We need to
291 # where runlines itself has an unhandled exception. We need to
287 # uniformize this, for all exception construction to come from a
292 # uniformize this, for all exception construction to come from a
288 # single location in the codbase.
293 # single location in the codbase.
289 etype, evalue, tb = sys.exc_info()
294 etype, evalue, tb = sys.exc_info()
290 tb_list = traceback.format_exception(etype, evalue, tb)
295 tb_list = traceback.format_exception(etype, evalue, tb)
291 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
296 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
292 else:
297 else:
293 status = u'ok'
298 status = u'ok'
294
299
295 reply_content[u'status'] = status
300 reply_content[u'status'] = status
296
301
297 # Return the execution counter so clients can display prompts
302 # Return the execution counter so clients can display prompts
298 reply_content['execution_count'] = shell.execution_count -1
303 reply_content['execution_count'] = shell.execution_count -1
299
304
300 # FIXME - fish exception info out of shell, possibly left there by
305 # FIXME - fish exception info out of shell, possibly left there by
301 # runlines. We'll need to clean up this logic later.
306 # runlines. We'll need to clean up this logic later.
302 if shell._reply_content is not None:
307 if shell._reply_content is not None:
303 reply_content.update(shell._reply_content)
308 reply_content.update(shell._reply_content)
304 # reset after use
309 # reset after use
305 shell._reply_content = None
310 shell._reply_content = None
306
311
307 # At this point, we can tell whether the main code execution succeeded
312 # At this point, we can tell whether the main code execution succeeded
308 # or not. If it did, we proceed to evaluate user_variables/expressions
313 # or not. If it did, we proceed to evaluate user_variables/expressions
309 if reply_content['status'] == 'ok':
314 if reply_content['status'] == 'ok':
310 reply_content[u'user_variables'] = \
315 reply_content[u'user_variables'] = \
311 shell.user_variables(content[u'user_variables'])
316 shell.user_variables(content[u'user_variables'])
312 reply_content[u'user_expressions'] = \
317 reply_content[u'user_expressions'] = \
313 shell.user_expressions(content[u'user_expressions'])
318 shell.user_expressions(content[u'user_expressions'])
314 else:
319 else:
315 # If there was an error, don't even try to compute variables or
320 # If there was an error, don't even try to compute variables or
316 # expressions
321 # expressions
317 reply_content[u'user_variables'] = {}
322 reply_content[u'user_variables'] = {}
318 reply_content[u'user_expressions'] = {}
323 reply_content[u'user_expressions'] = {}
319
324
320 # Payloads should be retrieved regardless of outcome, so we can both
325 # Payloads should be retrieved regardless of outcome, so we can both
321 # recover partial output (that could have been generated early in a
326 # recover partial output (that could have been generated early in a
322 # block, before an error) and clear the payload system always.
327 # block, before an error) and clear the payload system always.
323 reply_content[u'payload'] = shell.payload_manager.read_payload()
328 reply_content[u'payload'] = shell.payload_manager.read_payload()
324 # Be agressive about clearing the payload because we don't want
329 # Be agressive about clearing the payload because we don't want
325 # it to sit in memory until the next execute_request comes in.
330 # it to sit in memory until the next execute_request comes in.
326 shell.payload_manager.clear_payload()
331 shell.payload_manager.clear_payload()
327
332
328 # Flush output before sending the reply.
333 # Flush output before sending the reply.
329 sys.stdout.flush()
334 sys.stdout.flush()
330 sys.stderr.flush()
335 sys.stderr.flush()
331 # FIXME: on rare occasions, the flush doesn't seem to make it to the
336 # FIXME: on rare occasions, the flush doesn't seem to make it to the
332 # clients... This seems to mitigate the problem, but we definitely need
337 # clients... This seems to mitigate the problem, but we definitely need
333 # to better understand what's going on.
338 # to better understand what's going on.
334 if self._execute_sleep:
339 if self._execute_sleep:
335 time.sleep(self._execute_sleep)
340 time.sleep(self._execute_sleep)
336
341
337 # Send the reply.
342 # Send the reply.
338 reply_content = json_clean(reply_content)
343 reply_content = json_clean(reply_content)
339 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
344 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
340 reply_content, parent, ident=ident)
345 reply_content, parent, ident=ident)
341 self.log.debug(str(reply_msg))
346 self.log.debug(str(reply_msg))
342
347
343 if reply_msg['content']['status'] == u'error':
348 if reply_msg['content']['status'] == u'error':
344 self._abort_queue()
349 self._abort_queue()
345
350
346 self.session.send(self.iopub_socket,
351 self.session.send(self.iopub_socket,
347 u'status',
352 u'status',
348 {u'execution_state':u'idle'},
353 {u'execution_state':u'idle'},
349 parent=parent )
354 parent=parent )
350
355
351 def complete_request(self, ident, parent):
356 def complete_request(self, ident, parent):
352 txt, matches = self._complete(parent)
357 txt, matches = self._complete(parent)
353 matches = {'matches' : matches,
358 matches = {'matches' : matches,
354 'matched_text' : txt,
359 'matched_text' : txt,
355 'status' : 'ok'}
360 'status' : 'ok'}
356 matches = json_clean(matches)
361 matches = json_clean(matches)
357 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
362 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
358 matches, parent, ident)
363 matches, parent, ident)
359 self.log.debug(str(completion_msg))
364 self.log.debug(str(completion_msg))
360
365
361 def object_info_request(self, ident, parent):
366 def object_info_request(self, ident, parent):
362 object_info = self.shell.object_inspect(parent['content']['oname'])
367 object_info = self.shell.object_inspect(parent['content']['oname'])
363 # Before we send this object over, we scrub it for JSON usage
368 # Before we send this object over, we scrub it for JSON usage
364 oinfo = json_clean(object_info)
369 oinfo = json_clean(object_info)
365 msg = self.session.send(self.shell_socket, 'object_info_reply',
370 msg = self.session.send(self.shell_socket, 'object_info_reply',
366 oinfo, parent, ident)
371 oinfo, parent, ident)
367 self.log.debug(msg)
372 self.log.debug(msg)
368
373
369 def history_request(self, ident, parent):
374 def history_request(self, ident, parent):
370 # We need to pull these out, as passing **kwargs doesn't work with
375 # We need to pull these out, as passing **kwargs doesn't work with
371 # unicode keys before Python 2.6.5.
376 # unicode keys before Python 2.6.5.
372 hist_access_type = parent['content']['hist_access_type']
377 hist_access_type = parent['content']['hist_access_type']
373 raw = parent['content']['raw']
378 raw = parent['content']['raw']
374 output = parent['content']['output']
379 output = parent['content']['output']
375 if hist_access_type == 'tail':
380 if hist_access_type == 'tail':
376 n = parent['content']['n']
381 n = parent['content']['n']
377 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
382 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
378 include_latest=True)
383 include_latest=True)
379
384
380 elif hist_access_type == 'range':
385 elif hist_access_type == 'range':
381 session = parent['content']['session']
386 session = parent['content']['session']
382 start = parent['content']['start']
387 start = parent['content']['start']
383 stop = parent['content']['stop']
388 stop = parent['content']['stop']
384 hist = self.shell.history_manager.get_range(session, start, stop,
389 hist = self.shell.history_manager.get_range(session, start, stop,
385 raw=raw, output=output)
390 raw=raw, output=output)
386
391
387 elif hist_access_type == 'search':
392 elif hist_access_type == 'search':
388 pattern = parent['content']['pattern']
393 pattern = parent['content']['pattern']
389 hist = self.shell.history_manager.search(pattern, raw=raw,
394 hist = self.shell.history_manager.search(pattern, raw=raw,
390 output=output)
395 output=output)
391
396
392 else:
397 else:
393 hist = []
398 hist = []
394 hist = list(hist)
399 hist = list(hist)
395 content = {'history' : hist}
400 content = {'history' : hist}
396 content = json_clean(content)
401 content = json_clean(content)
397 msg = self.session.send(self.shell_socket, 'history_reply',
402 msg = self.session.send(self.shell_socket, 'history_reply',
398 content, parent, ident)
403 content, parent, ident)
399 self.log.debug("Sending history reply with %i entries", len(hist))
404 self.log.debug("Sending history reply with %i entries", len(hist))
400
405
401 def connect_request(self, ident, parent):
406 def connect_request(self, ident, parent):
402 if self._recorded_ports is not None:
407 if self._recorded_ports is not None:
403 content = self._recorded_ports.copy()
408 content = self._recorded_ports.copy()
404 else:
409 else:
405 content = {}
410 content = {}
406 msg = self.session.send(self.shell_socket, 'connect_reply',
411 msg = self.session.send(self.shell_socket, 'connect_reply',
407 content, parent, ident)
412 content, parent, ident)
408 self.log.debug(msg)
413 self.log.debug(msg)
409
414
410 def shutdown_request(self, ident, parent):
415 def shutdown_request(self, ident, parent):
411 self.shell.exit_now = True
416 self.shell.exit_now = True
412 self._shutdown_message = self.session.msg(u'shutdown_reply',
417 self._shutdown_message = self.session.msg(u'shutdown_reply',
413 parent['content'], parent)
418 parent['content'], parent)
414 sys.exit(0)
419 sys.exit(0)
415
420
416 #---------------------------------------------------------------------------
421 #---------------------------------------------------------------------------
417 # Protected interface
422 # Protected interface
418 #---------------------------------------------------------------------------
423 #---------------------------------------------------------------------------
419
424
420 def _abort_queue(self):
425 def _abort_queue(self):
421 while True:
426 while True:
422 try:
427 try:
423 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
428 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
424 except Exception:
429 except Exception:
425 self.log.warn("Invalid Message:", exc_info=True)
430 self.log.warn("Invalid Message:", exc_info=True)
426 continue
431 continue
427 if msg is None:
432 if msg is None:
428 break
433 break
429 else:
434 else:
430 assert ident is not None, \
435 assert ident is not None, \
431 "Unexpected missing message part."
436 "Unexpected missing message part."
432
437
433 self.log.debug("Aborting:\n"+str(Message(msg)))
438 self.log.debug("Aborting:\n"+str(Message(msg)))
434 msg_type = msg['header']['msg_type']
439 msg_type = msg['header']['msg_type']
435 reply_type = msg_type.split('_')[0] + '_reply'
440 reply_type = msg_type.split('_')[0] + '_reply'
436 reply_msg = self.session.send(self.shell_socket, reply_type,
441 reply_msg = self.session.send(self.shell_socket, reply_type,
437 {'status' : 'aborted'}, msg, ident=ident)
442 {'status' : 'aborted'}, msg, ident=ident)
438 self.log.debug(reply_msg)
443 self.log.debug(reply_msg)
439 # We need to wait a bit for requests to come in. This can probably
444 # We need to wait a bit for requests to come in. This can probably
440 # be set shorter for true asynchronous clients.
445 # be set shorter for true asynchronous clients.
441 time.sleep(0.1)
446 time.sleep(0.1)
442
447
443 def _no_raw_input(self):
448 def _no_raw_input(self):
444 """Raise StdinNotImplentedError if active frontend doesn't support
449 """Raise StdinNotImplentedError if active frontend doesn't support
445 stdin."""
450 stdin."""
446 raise StdinNotImplementedError("raw_input was called, but this "
451 raise StdinNotImplementedError("raw_input was called, but this "
447 "frontend does not support stdin.")
452 "frontend does not support stdin.")
448
453
449 def _raw_input(self, prompt, ident, parent):
454 def _raw_input(self, prompt, ident, parent):
450 # Flush output before making the request.
455 # Flush output before making the request.
451 sys.stderr.flush()
456 sys.stderr.flush()
452 sys.stdout.flush()
457 sys.stdout.flush()
453
458
454 # Send the input request.
459 # Send the input request.
455 content = json_clean(dict(prompt=prompt))
460 content = json_clean(dict(prompt=prompt))
456 self.session.send(self.stdin_socket, u'input_request', content, parent,
461 self.session.send(self.stdin_socket, u'input_request', content, parent,
457 ident=ident)
462 ident=ident)
458
463
459 # Await a response.
464 # Await a response.
460 while True:
465 while True:
461 try:
466 try:
462 ident, reply = self.session.recv(self.stdin_socket, 0)
467 ident, reply = self.session.recv(self.stdin_socket, 0)
463 except Exception:
468 except Exception:
464 self.log.warn("Invalid Message:", exc_info=True)
469 self.log.warn("Invalid Message:", exc_info=True)
465 else:
470 else:
466 break
471 break
467 try:
472 try:
468 value = reply['content']['value']
473 value = reply['content']['value']
469 except:
474 except:
470 self.log.error("Got bad raw_input reply: ")
475 self.log.error("Got bad raw_input reply: ")
471 self.log.error(str(Message(parent)))
476 self.log.error(str(Message(parent)))
472 value = ''
477 value = ''
473 if value == '\x04':
478 if value == '\x04':
474 # EOF
479 # EOF
475 raise EOFError
480 raise EOFError
476 return value
481 return value
477
482
478 def _complete(self, msg):
483 def _complete(self, msg):
479 c = msg['content']
484 c = msg['content']
480 try:
485 try:
481 cpos = int(c['cursor_pos'])
486 cpos = int(c['cursor_pos'])
482 except:
487 except:
483 # If we don't get something that we can convert to an integer, at
488 # If we don't get something that we can convert to an integer, at
484 # least attempt the completion guessing the cursor is at the end of
489 # least attempt the completion guessing the cursor is at the end of
485 # the text, if there's any, and otherwise of the line
490 # the text, if there's any, and otherwise of the line
486 cpos = len(c['text'])
491 cpos = len(c['text'])
487 if cpos==0:
492 if cpos==0:
488 cpos = len(c['line'])
493 cpos = len(c['line'])
489 return self.shell.complete(c['text'], c['line'], cpos)
494 return self.shell.complete(c['text'], c['line'], cpos)
490
495
491 def _object_info(self, context):
496 def _object_info(self, context):
492 symbol, leftover = self._symbol_from_context(context)
497 symbol, leftover = self._symbol_from_context(context)
493 if symbol is not None and not leftover:
498 if symbol is not None and not leftover:
494 doc = getattr(symbol, '__doc__', '')
499 doc = getattr(symbol, '__doc__', '')
495 else:
500 else:
496 doc = ''
501 doc = ''
497 object_info = dict(docstring = doc)
502 object_info = dict(docstring = doc)
498 return object_info
503 return object_info
499
504
500 def _symbol_from_context(self, context):
505 def _symbol_from_context(self, context):
501 if not context:
506 if not context:
502 return None, context
507 return None, context
503
508
504 base_symbol_string = context[0]
509 base_symbol_string = context[0]
505 symbol = self.shell.user_ns.get(base_symbol_string, None)
510 symbol = self.shell.user_ns.get(base_symbol_string, None)
506 if symbol is None:
511 if symbol is None:
507 symbol = __builtin__.__dict__.get(base_symbol_string, None)
512 symbol = __builtin__.__dict__.get(base_symbol_string, None)
508 if symbol is None:
513 if symbol is None:
509 return None, context
514 return None, context
510
515
511 context = context[1:]
516 context = context[1:]
512 for i, name in enumerate(context):
517 for i, name in enumerate(context):
513 new_symbol = getattr(symbol, name, None)
518 new_symbol = getattr(symbol, name, None)
514 if new_symbol is None:
519 if new_symbol is None:
515 return symbol, context[i:]
520 return symbol, context[i:]
516 else:
521 else:
517 symbol = new_symbol
522 symbol = new_symbol
518
523
519 return symbol, []
524 return symbol, []
520
525
521 def _at_shutdown(self):
526 def _at_shutdown(self):
522 """Actions taken at shutdown by the kernel, called by python's atexit.
527 """Actions taken at shutdown by the kernel, called by python's atexit.
523 """
528 """
524 # io.rprint("Kernel at_shutdown") # dbg
529 # io.rprint("Kernel at_shutdown") # dbg
525 if self._shutdown_message is not None:
530 if self._shutdown_message is not None:
526 self.session.send(self.shell_socket, self._shutdown_message)
531 self.session.send(self.shell_socket, self._shutdown_message)
527 self.session.send(self.iopub_socket, self._shutdown_message)
532 self.session.send(self.iopub_socket, self._shutdown_message)
528 self.log.debug(str(self._shutdown_message))
533 self.log.debug(str(self._shutdown_message))
529 # A very short sleep to give zmq time to flush its message buffers
534 # A very short sleep to give zmq time to flush its message buffers
530 # before Python truly shuts down.
535 # before Python truly shuts down.
531 time.sleep(0.01)
536 time.sleep(0.01)
532
537
533 #-----------------------------------------------------------------------------
538 #-----------------------------------------------------------------------------
534 # Aliases and Flags for the IPKernelApp
539 # Aliases and Flags for the IPKernelApp
535 #-----------------------------------------------------------------------------
540 #-----------------------------------------------------------------------------
536
541
537 flags = dict(kernel_flags)
542 flags = dict(kernel_flags)
538 flags.update(shell_flags)
543 flags.update(shell_flags)
539
544
540 addflag = lambda *args: flags.update(boolean_flag(*args))
545 addflag = lambda *args: flags.update(boolean_flag(*args))
541
546
542 flags['pylab'] = (
547 flags['pylab'] = (
543 {'IPKernelApp' : {'pylab' : 'auto'}},
548 {'IPKernelApp' : {'pylab' : 'auto'}},
544 """Pre-load matplotlib and numpy for interactive use with
549 """Pre-load matplotlib and numpy for interactive use with
545 the default matplotlib backend."""
550 the default matplotlib backend."""
546 )
551 )
547
552
548 aliases = dict(kernel_aliases)
553 aliases = dict(kernel_aliases)
549 aliases.update(shell_aliases)
554 aliases.update(shell_aliases)
550
555
551 # it's possible we don't want short aliases for *all* of these:
556 # it's possible we don't want short aliases for *all* of these:
552 aliases.update(dict(
557 aliases.update(dict(
553 pylab='IPKernelApp.pylab',
558 pylab='IPKernelApp.pylab',
554 ))
559 ))
555
560
556 #-----------------------------------------------------------------------------
561 #-----------------------------------------------------------------------------
557 # The IPKernelApp class
562 # The IPKernelApp class
558 #-----------------------------------------------------------------------------
563 #-----------------------------------------------------------------------------
559
564
560 class IPKernelApp(KernelApp, InteractiveShellApp):
565 class IPKernelApp(KernelApp, InteractiveShellApp):
561 name = 'ipkernel'
566 name = 'ipkernel'
562
567
563 aliases = Dict(aliases)
568 aliases = Dict(aliases)
564 flags = Dict(flags)
569 flags = Dict(flags)
565 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
570 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
566 # configurables
571 # configurables
567 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
572 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
568 config=True,
573 config=True,
569 help="""Pre-load matplotlib and numpy for interactive use,
574 help="""Pre-load matplotlib and numpy for interactive use,
570 selecting a particular matplotlib backend and loop integration.
575 selecting a particular matplotlib backend and loop integration.
571 """
576 """
572 )
577 )
573
578
574 @catch_config_error
579 @catch_config_error
575 def initialize(self, argv=None):
580 def initialize(self, argv=None):
576 super(IPKernelApp, self).initialize(argv)
581 super(IPKernelApp, self).initialize(argv)
577 self.init_shell()
582 self.init_shell()
578 self.init_extensions()
583 self.init_extensions()
579 self.init_code()
584 self.init_code()
580
585
581 def init_kernel(self):
586 def init_kernel(self):
582
587
583 kernel = Kernel(config=self.config, session=self.session,
588 kernel = Kernel(config=self.config, session=self.session,
584 shell_socket=self.shell_socket,
589 shell_socket=self.shell_socket,
585 iopub_socket=self.iopub_socket,
590 iopub_socket=self.iopub_socket,
586 stdin_socket=self.stdin_socket,
591 stdin_socket=self.stdin_socket,
587 log=self.log,
592 log=self.log,
588 profile_dir=self.profile_dir,
593 profile_dir=self.profile_dir,
589 )
594 )
590 self.kernel = kernel
595 self.kernel = kernel
591 kernel.record_ports(self.ports)
596 kernel.record_ports(self.ports)
592 shell = kernel.shell
597 shell = kernel.shell
593 if self.pylab:
598 if self.pylab:
594 try:
599 try:
595 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
600 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
596 shell.enable_pylab(gui, import_all=self.pylab_import_all)
601 shell.enable_pylab(gui, import_all=self.pylab_import_all)
597 except Exception:
602 except Exception:
598 self.log.error("Pylab initialization failed", exc_info=True)
603 self.log.error("Pylab initialization failed", exc_info=True)
599 # print exception straight to stdout, because normally
604 # print exception straight to stdout, because normally
600 # _showtraceback associates the reply with an execution,
605 # _showtraceback associates the reply with an execution,
601 # which means frontends will never draw it, as this exception
606 # which means frontends will never draw it, as this exception
602 # is not associated with any execute request.
607 # is not associated with any execute request.
603
608
604 # replace pyerr-sending traceback with stdout
609 # replace pyerr-sending traceback with stdout
605 _showtraceback = shell._showtraceback
610 _showtraceback = shell._showtraceback
606 def print_tb(etype, evalue, stb):
611 def print_tb(etype, evalue, stb):
607 print ("Error initializing pylab, pylab mode will not "
612 print ("Error initializing pylab, pylab mode will not "
608 "be active", file=io.stderr)
613 "be active", file=io.stderr)
609 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
614 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
610 shell._showtraceback = print_tb
615 shell._showtraceback = print_tb
611
616
612 # send the traceback over stdout
617 # send the traceback over stdout
613 shell.showtraceback(tb_offset=0)
618 shell.showtraceback(tb_offset=0)
614
619
615 # restore proper _showtraceback method
620 # restore proper _showtraceback method
616 shell._showtraceback = _showtraceback
621 shell._showtraceback = _showtraceback
617
622
618
623
619 def init_shell(self):
624 def init_shell(self):
620 self.shell = self.kernel.shell
625 self.shell = self.kernel.shell
621 self.shell.configurables.append(self)
626 self.shell.configurables.append(self)
622
627
623
628
624 #-----------------------------------------------------------------------------
629 #-----------------------------------------------------------------------------
625 # Kernel main and launch functions
630 # Kernel main and launch functions
626 #-----------------------------------------------------------------------------
631 #-----------------------------------------------------------------------------
627
632
628 def launch_kernel(*args, **kwargs):
633 def launch_kernel(*args, **kwargs):
629 """Launches a localhost IPython kernel, binding to the specified ports.
634 """Launches a localhost IPython kernel, binding to the specified ports.
630
635
631 This function simply calls entry_point.base_launch_kernel with the right
636 This function simply calls entry_point.base_launch_kernel with the right
632 first command to start an ipkernel. See base_launch_kernel for arguments.
637 first command to start an ipkernel. See base_launch_kernel for arguments.
633
638
634 Returns
639 Returns
635 -------
640 -------
636 A tuple of form:
641 A tuple of form:
637 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
642 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
638 where kernel_process is a Popen object and the ports are integers.
643 where kernel_process is a Popen object and the ports are integers.
639 """
644 """
640 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
645 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
641 *args, **kwargs)
646 *args, **kwargs)
642
647
643
648
644 def main():
649 def main():
645 """Run an IPKernel as an application"""
650 """Run an IPKernel as an application"""
646 app = IPKernelApp.instance()
651 app = IPKernelApp.instance()
647 app.initialize()
652 app.initialize()
648 app.start()
653 app.start()
649
654
650
655
651 if __name__ == '__main__':
656 if __name__ == '__main__':
652 main()
657 main()
General Comments 0
You need to be logged in to leave comments. Login now