##// END OF EJS Templates
Add message when kernel catches KeyboardInterrupt.
Thomas Kluyver -
Show More
@@ -1,689 +1,689 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 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.utils import io
30 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
31 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
32 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
33 from IPython.utils.traitlets import Instance, Float
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 start_kernel)
35 start_kernel)
36 from iostream import OutStream
36 from iostream import OutStream
37 from session import Session, Message
37 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
38 from zmqshell import ZMQInteractiveShell
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Globals
41 # Globals
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Module-level logger
44 # Module-level logger
45 logger = logging.getLogger(__name__)
45 logger = logging.getLogger(__name__)
46
46
47 # FIXME: this needs to be done more cleanly later, once we have proper
47 # FIXME: this needs to be done more cleanly later, once we have proper
48 # configuration support. This is a library, so it shouldn't set a stream
48 # configuration support. This is a library, so it shouldn't set a stream
49 # handler, see:
49 # handler, see:
50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
51 # But this lets us at least do developer debugging for now by manually turning
51 # But this lets us at least do developer debugging for now by manually turning
52 # it on/off. And once we have full config support, the client entry points
52 # it on/off. And once we have full config support, the client entry points
53 # will select their logging handlers, as well as passing to this library the
53 # will select their logging handlers, as well as passing to this library the
54 # logging level.
54 # logging level.
55
55
56 if 0: # dbg - set to 1 to actually see the messages.
56 if 0: # dbg - set to 1 to actually see the messages.
57 logger.addHandler(logging.StreamHandler())
57 logger.addHandler(logging.StreamHandler())
58 logger.setLevel(logging.DEBUG)
58 logger.setLevel(logging.DEBUG)
59
59
60 # /FIXME
60 # /FIXME
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # Main kernel class
63 # Main kernel class
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 class Kernel(Configurable):
66 class Kernel(Configurable):
67
67
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69 # Kernel interface
69 # Kernel interface
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71
71
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
73 session = Instance(Session)
73 session = Instance(Session)
74 reply_socket = Instance('zmq.Socket')
74 reply_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
77
77
78 # Private interface
78 # Private interface
79
79
80 # Time to sleep after flushing the stdout/err buffers in each execute
80 # Time to sleep after flushing the stdout/err buffers in each execute
81 # cycle. While this introduces a hard limit on the minimal latency of the
81 # cycle. While this introduces a hard limit on the minimal latency of the
82 # execute cycle, it helps prevent output synchronization problems for
82 # execute cycle, it helps prevent output synchronization problems for
83 # clients.
83 # clients.
84 # Units are in seconds. The minimum zmq latency on local host is probably
84 # Units are in seconds. The minimum zmq latency on local host is probably
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
86 # a little if it's not enough after more interactive testing.
86 # a little if it's not enough after more interactive testing.
87 _execute_sleep = Float(0.0005, config=True)
87 _execute_sleep = Float(0.0005, config=True)
88
88
89 # Frequency of the kernel's event loop.
89 # Frequency of the kernel's event loop.
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
91 # adapt to milliseconds.
91 # adapt to milliseconds.
92 _poll_interval = Float(0.05, config=True)
92 _poll_interval = Float(0.05, config=True)
93
93
94 # If the shutdown was requested over the network, we leave here the
94 # If the shutdown was requested over the network, we leave here the
95 # necessary reply message so it can be sent by our registered atexit
95 # necessary reply message so it can be sent by our registered atexit
96 # handler. This ensures that the reply is only sent to clients truly at
96 # handler. This ensures that the reply is only sent to clients truly at
97 # the end of our shutdown process (which happens after the underlying
97 # the end of our shutdown process (which happens after the underlying
98 # IPython shell's own shutdown).
98 # IPython shell's own shutdown).
99 _shutdown_message = None
99 _shutdown_message = None
100
100
101 # This is a dict of port number that the kernel is listening on. It is set
101 # This is a dict of port number that the kernel is listening on. It is set
102 # by record_ports and used by connect_request.
102 # by record_ports and used by connect_request.
103 _recorded_ports = None
103 _recorded_ports = None
104
104
105
105
106 def __init__(self, **kwargs):
106 def __init__(self, **kwargs):
107 super(Kernel, self).__init__(**kwargs)
107 super(Kernel, self).__init__(**kwargs)
108
108
109 # Before we even start up the shell, register *first* our exit handlers
109 # Before we even start up the shell, register *first* our exit handlers
110 # so they come before the shell's
110 # so they come before the shell's
111 atexit.register(self._at_shutdown)
111 atexit.register(self._at_shutdown)
112
112
113 # Initialize the InteractiveShell subclass
113 # Initialize the InteractiveShell subclass
114 self.shell = ZMQInteractiveShell.instance()
114 self.shell = ZMQInteractiveShell.instance()
115 self.shell.displayhook.session = self.session
115 self.shell.displayhook.session = self.session
116 self.shell.displayhook.pub_socket = self.pub_socket
116 self.shell.displayhook.pub_socket = self.pub_socket
117 self.shell.display_pub.session = self.session
117 self.shell.display_pub.session = self.session
118 self.shell.display_pub.pub_socket = self.pub_socket
118 self.shell.display_pub.pub_socket = self.pub_socket
119
119
120 # TMP - hack while developing
120 # TMP - hack while developing
121 self.shell._reply_content = None
121 self.shell._reply_content = None
122
122
123 # Build dict of handlers for message types
123 # Build dict of handlers for message types
124 msg_types = [ 'execute_request', 'complete_request',
124 msg_types = [ 'execute_request', 'complete_request',
125 'object_info_request', 'history_request',
125 'object_info_request', 'history_request',
126 'connect_request', 'shutdown_request']
126 'connect_request', 'shutdown_request']
127 self.handlers = {}
127 self.handlers = {}
128 for msg_type in msg_types:
128 for msg_type in msg_types:
129 self.handlers[msg_type] = getattr(self, msg_type)
129 self.handlers[msg_type] = getattr(self, msg_type)
130
130
131 def do_one_iteration(self):
131 def do_one_iteration(self):
132 """Do one iteration of the kernel's evaluation loop.
132 """Do one iteration of the kernel's evaluation loop.
133 """
133 """
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
135 if msg is None:
135 if msg is None:
136 return
136 return
137
137
138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
139 # We now require 2.0.8 or above, so we can uncomment for safety.
139 # We now require 2.0.8 or above, so we can uncomment for safety.
140 # print(ident,msg, file=sys.__stdout__)
140 # print(ident,msg, file=sys.__stdout__)
141 assert ident is not None, "Missing message part."
141 assert ident is not None, "Missing message part."
142
142
143 # Print some info about this message and leave a '--->' marker, so it's
143 # Print some info about this message and leave a '--->' marker, so it's
144 # easier to trace visually the message chain when debugging. Each
144 # easier to trace visually the message chain when debugging. Each
145 # handler prints its message at the end.
145 # handler prints its message at the end.
146 # Eventually we'll move these from stdout to a logger.
146 # Eventually we'll move these from stdout to a logger.
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
149
149
150 # Find and call actual handler for message
150 # Find and call actual handler for message
151 handler = self.handlers.get(msg['msg_type'], None)
151 handler = self.handlers.get(msg['msg_type'], None)
152 if handler is None:
152 if handler is None:
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
154 else:
154 else:
155 handler(ident, msg)
155 handler(ident, msg)
156
156
157 # Check whether we should exit, in case the incoming message set the
157 # Check whether we should exit, in case the incoming message set the
158 # exit flag on
158 # exit flag on
159 if self.shell.exit_now:
159 if self.shell.exit_now:
160 logger.debug('\nExiting IPython kernel...')
160 logger.debug('\nExiting IPython kernel...')
161 # We do a normal, clean exit, which allows any actions registered
161 # We do a normal, clean exit, which allows any actions registered
162 # via atexit (such as history saving) to take place.
162 # via atexit (such as history saving) to take place.
163 sys.exit(0)
163 sys.exit(0)
164
164
165
165
166 def start(self):
166 def start(self):
167 """ Start the kernel main loop.
167 """ Start the kernel main loop.
168 """
168 """
169 while True:
169 while True:
170 try:
170 try:
171 time.sleep(self._poll_interval)
171 time.sleep(self._poll_interval)
172 self.do_one_iteration()
172 self.do_one_iteration()
173 except KeyboardInterrupt:
173 except KeyboardInterrupt:
174 # Ctrl-C shouldn't crash the kernel
174 # Ctrl-C shouldn't crash the kernel
175 continue
175 io.raw_print("KeyboardInterrupt caught in kernel")
176
176
177 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
177 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
178 """Record the ports that this kernel is using.
178 """Record the ports that this kernel is using.
179
179
180 The creator of the Kernel instance must call this methods if they
180 The creator of the Kernel instance must call this methods if they
181 want the :meth:`connect_request` method to return the port numbers.
181 want the :meth:`connect_request` method to return the port numbers.
182 """
182 """
183 self._recorded_ports = {
183 self._recorded_ports = {
184 'xrep_port' : xrep_port,
184 'xrep_port' : xrep_port,
185 'pub_port' : pub_port,
185 'pub_port' : pub_port,
186 'req_port' : req_port,
186 'req_port' : req_port,
187 'hb_port' : hb_port
187 'hb_port' : hb_port
188 }
188 }
189
189
190 #---------------------------------------------------------------------------
190 #---------------------------------------------------------------------------
191 # Kernel request handlers
191 # Kernel request handlers
192 #---------------------------------------------------------------------------
192 #---------------------------------------------------------------------------
193
193
194 def _publish_pyin(self, code, parent):
194 def _publish_pyin(self, code, parent):
195 """Publish the code request on the pyin stream."""
195 """Publish the code request on the pyin stream."""
196
196
197 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
197 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
198
198
199 def execute_request(self, ident, parent):
199 def execute_request(self, ident, parent):
200
200
201 status_msg = self.session.send(self.pub_socket,
201 status_msg = self.session.send(self.pub_socket,
202 u'status',
202 u'status',
203 {u'execution_state':u'busy'},
203 {u'execution_state':u'busy'},
204 parent=parent
204 parent=parent
205 )
205 )
206
206
207 try:
207 try:
208 content = parent[u'content']
208 content = parent[u'content']
209 code = content[u'code']
209 code = content[u'code']
210 silent = content[u'silent']
210 silent = content[u'silent']
211 except:
211 except:
212 logger.error("Got bad msg: ")
212 logger.error("Got bad msg: ")
213 logger.error(str(Message(parent)))
213 logger.error(str(Message(parent)))
214 return
214 return
215
215
216 shell = self.shell # we'll need this a lot here
216 shell = self.shell # we'll need this a lot here
217
217
218 # Replace raw_input. Note that is not sufficient to replace
218 # Replace raw_input. Note that is not sufficient to replace
219 # raw_input in the user namespace.
219 # raw_input in the user namespace.
220 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
220 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
221 __builtin__.raw_input = raw_input
221 __builtin__.raw_input = raw_input
222
222
223 # Set the parent message of the display hook and out streams.
223 # Set the parent message of the display hook and out streams.
224 shell.displayhook.set_parent(parent)
224 shell.displayhook.set_parent(parent)
225 shell.display_pub.set_parent(parent)
225 shell.display_pub.set_parent(parent)
226 sys.stdout.set_parent(parent)
226 sys.stdout.set_parent(parent)
227 sys.stderr.set_parent(parent)
227 sys.stderr.set_parent(parent)
228
228
229 # Re-broadcast our input for the benefit of listening clients, and
229 # Re-broadcast our input for the benefit of listening clients, and
230 # start computing output
230 # start computing output
231 if not silent:
231 if not silent:
232 self._publish_pyin(code, parent)
232 self._publish_pyin(code, parent)
233
233
234 reply_content = {}
234 reply_content = {}
235 try:
235 try:
236 if silent:
236 if silent:
237 # run_code uses 'exec' mode, so no displayhook will fire, and it
237 # run_code uses 'exec' mode, so no displayhook will fire, and it
238 # doesn't call logging or history manipulations. Print
238 # doesn't call logging or history manipulations. Print
239 # statements in that code will obviously still execute.
239 # statements in that code will obviously still execute.
240 shell.run_code(code)
240 shell.run_code(code)
241 else:
241 else:
242 # FIXME: the shell calls the exception handler itself.
242 # FIXME: the shell calls the exception handler itself.
243 shell.run_cell(code)
243 shell.run_cell(code)
244 except:
244 except:
245 status = u'error'
245 status = u'error'
246 # FIXME: this code right now isn't being used yet by default,
246 # FIXME: this code right now isn't being used yet by default,
247 # because the run_cell() call above directly fires off exception
247 # because the run_cell() call above directly fires off exception
248 # reporting. This code, therefore, is only active in the scenario
248 # reporting. This code, therefore, is only active in the scenario
249 # where runlines itself has an unhandled exception. We need to
249 # where runlines itself has an unhandled exception. We need to
250 # uniformize this, for all exception construction to come from a
250 # uniformize this, for all exception construction to come from a
251 # single location in the codbase.
251 # single location in the codbase.
252 etype, evalue, tb = sys.exc_info()
252 etype, evalue, tb = sys.exc_info()
253 tb_list = traceback.format_exception(etype, evalue, tb)
253 tb_list = traceback.format_exception(etype, evalue, tb)
254 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
254 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
255 else:
255 else:
256 status = u'ok'
256 status = u'ok'
257
257
258 reply_content[u'status'] = status
258 reply_content[u'status'] = status
259
259
260 # Return the execution counter so clients can display prompts
260 # Return the execution counter so clients can display prompts
261 reply_content['execution_count'] = shell.execution_count -1
261 reply_content['execution_count'] = shell.execution_count -1
262
262
263 # FIXME - fish exception info out of shell, possibly left there by
263 # FIXME - fish exception info out of shell, possibly left there by
264 # runlines. We'll need to clean up this logic later.
264 # runlines. We'll need to clean up this logic later.
265 if shell._reply_content is not None:
265 if shell._reply_content is not None:
266 reply_content.update(shell._reply_content)
266 reply_content.update(shell._reply_content)
267 # reset after use
267 # reset after use
268 shell._reply_content = None
268 shell._reply_content = None
269
269
270 # At this point, we can tell whether the main code execution succeeded
270 # At this point, we can tell whether the main code execution succeeded
271 # or not. If it did, we proceed to evaluate user_variables/expressions
271 # or not. If it did, we proceed to evaluate user_variables/expressions
272 if reply_content['status'] == 'ok':
272 if reply_content['status'] == 'ok':
273 reply_content[u'user_variables'] = \
273 reply_content[u'user_variables'] = \
274 shell.user_variables(content[u'user_variables'])
274 shell.user_variables(content[u'user_variables'])
275 reply_content[u'user_expressions'] = \
275 reply_content[u'user_expressions'] = \
276 shell.user_expressions(content[u'user_expressions'])
276 shell.user_expressions(content[u'user_expressions'])
277 else:
277 else:
278 # If there was an error, don't even try to compute variables or
278 # If there was an error, don't even try to compute variables or
279 # expressions
279 # expressions
280 reply_content[u'user_variables'] = {}
280 reply_content[u'user_variables'] = {}
281 reply_content[u'user_expressions'] = {}
281 reply_content[u'user_expressions'] = {}
282
282
283 # Payloads should be retrieved regardless of outcome, so we can both
283 # Payloads should be retrieved regardless of outcome, so we can both
284 # recover partial output (that could have been generated early in a
284 # recover partial output (that could have been generated early in a
285 # block, before an error) and clear the payload system always.
285 # block, before an error) and clear the payload system always.
286 reply_content[u'payload'] = shell.payload_manager.read_payload()
286 reply_content[u'payload'] = shell.payload_manager.read_payload()
287 # Be agressive about clearing the payload because we don't want
287 # Be agressive about clearing the payload because we don't want
288 # it to sit in memory until the next execute_request comes in.
288 # it to sit in memory until the next execute_request comes in.
289 shell.payload_manager.clear_payload()
289 shell.payload_manager.clear_payload()
290
290
291 # Flush output before sending the reply.
291 # Flush output before sending the reply.
292 sys.stdout.flush()
292 sys.stdout.flush()
293 sys.stderr.flush()
293 sys.stderr.flush()
294 # FIXME: on rare occasions, the flush doesn't seem to make it to the
294 # FIXME: on rare occasions, the flush doesn't seem to make it to the
295 # clients... This seems to mitigate the problem, but we definitely need
295 # clients... This seems to mitigate the problem, but we definitely need
296 # to better understand what's going on.
296 # to better understand what's going on.
297 if self._execute_sleep:
297 if self._execute_sleep:
298 time.sleep(self._execute_sleep)
298 time.sleep(self._execute_sleep)
299
299
300 # Send the reply.
300 # Send the reply.
301 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
301 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
302 reply_content, parent, ident=ident)
302 reply_content, parent, ident=ident)
303 logger.debug(str(reply_msg))
303 logger.debug(str(reply_msg))
304
304
305 if reply_msg['content']['status'] == u'error':
305 if reply_msg['content']['status'] == u'error':
306 self._abort_queue()
306 self._abort_queue()
307
307
308 status_msg = self.session.send(self.pub_socket,
308 status_msg = self.session.send(self.pub_socket,
309 u'status',
309 u'status',
310 {u'execution_state':u'idle'},
310 {u'execution_state':u'idle'},
311 parent=parent
311 parent=parent
312 )
312 )
313
313
314 def complete_request(self, ident, parent):
314 def complete_request(self, ident, parent):
315 txt, matches = self._complete(parent)
315 txt, matches = self._complete(parent)
316 matches = {'matches' : matches,
316 matches = {'matches' : matches,
317 'matched_text' : txt,
317 'matched_text' : txt,
318 'status' : 'ok'}
318 'status' : 'ok'}
319 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
319 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
320 matches, parent, ident)
320 matches, parent, ident)
321 logger.debug(str(completion_msg))
321 logger.debug(str(completion_msg))
322
322
323 def object_info_request(self, ident, parent):
323 def object_info_request(self, ident, parent):
324 object_info = self.shell.object_inspect(parent['content']['oname'])
324 object_info = self.shell.object_inspect(parent['content']['oname'])
325 # Before we send this object over, we scrub it for JSON usage
325 # Before we send this object over, we scrub it for JSON usage
326 oinfo = json_clean(object_info)
326 oinfo = json_clean(object_info)
327 msg = self.session.send(self.reply_socket, 'object_info_reply',
327 msg = self.session.send(self.reply_socket, 'object_info_reply',
328 oinfo, parent, ident)
328 oinfo, parent, ident)
329 logger.debug(msg)
329 logger.debug(msg)
330
330
331 def history_request(self, ident, parent):
331 def history_request(self, ident, parent):
332 # We need to pull these out, as passing **kwargs doesn't work with
332 # We need to pull these out, as passing **kwargs doesn't work with
333 # unicode keys before Python 2.6.5.
333 # unicode keys before Python 2.6.5.
334 hist_access_type = parent['content']['hist_access_type']
334 hist_access_type = parent['content']['hist_access_type']
335 raw = parent['content']['raw']
335 raw = parent['content']['raw']
336 output = parent['content']['output']
336 output = parent['content']['output']
337 if hist_access_type == 'tail':
337 if hist_access_type == 'tail':
338 n = parent['content']['n']
338 n = parent['content']['n']
339 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
339 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
340 include_latest=True)
340 include_latest=True)
341
341
342 elif hist_access_type == 'range':
342 elif hist_access_type == 'range':
343 session = parent['content']['session']
343 session = parent['content']['session']
344 start = parent['content']['start']
344 start = parent['content']['start']
345 stop = parent['content']['stop']
345 stop = parent['content']['stop']
346 hist = self.shell.history_manager.get_range(session, start, stop,
346 hist = self.shell.history_manager.get_range(session, start, stop,
347 raw=raw, output=output)
347 raw=raw, output=output)
348
348
349 elif hist_access_type == 'search':
349 elif hist_access_type == 'search':
350 pattern = parent['content']['pattern']
350 pattern = parent['content']['pattern']
351 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
351 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
352
352
353 else:
353 else:
354 hist = []
354 hist = []
355 content = {'history' : list(hist)}
355 content = {'history' : list(hist)}
356 msg = self.session.send(self.reply_socket, 'history_reply',
356 msg = self.session.send(self.reply_socket, 'history_reply',
357 content, parent, ident)
357 content, parent, ident)
358 logger.debug(str(msg))
358 logger.debug(str(msg))
359
359
360 def connect_request(self, ident, parent):
360 def connect_request(self, ident, parent):
361 if self._recorded_ports is not None:
361 if self._recorded_ports is not None:
362 content = self._recorded_ports.copy()
362 content = self._recorded_ports.copy()
363 else:
363 else:
364 content = {}
364 content = {}
365 msg = self.session.send(self.reply_socket, 'connect_reply',
365 msg = self.session.send(self.reply_socket, 'connect_reply',
366 content, parent, ident)
366 content, parent, ident)
367 logger.debug(msg)
367 logger.debug(msg)
368
368
369 def shutdown_request(self, ident, parent):
369 def shutdown_request(self, ident, parent):
370 self.shell.exit_now = True
370 self.shell.exit_now = True
371 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
371 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
372 sys.exit(0)
372 sys.exit(0)
373
373
374 #---------------------------------------------------------------------------
374 #---------------------------------------------------------------------------
375 # Protected interface
375 # Protected interface
376 #---------------------------------------------------------------------------
376 #---------------------------------------------------------------------------
377
377
378 def _abort_queue(self):
378 def _abort_queue(self):
379 while True:
379 while True:
380 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
380 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
381 if msg is None:
381 if msg is None:
382 break
382 break
383 else:
383 else:
384 assert ident is not None, \
384 assert ident is not None, \
385 "Unexpected missing message part."
385 "Unexpected missing message part."
386
386
387 logger.debug("Aborting:\n"+str(Message(msg)))
387 logger.debug("Aborting:\n"+str(Message(msg)))
388 msg_type = msg['msg_type']
388 msg_type = msg['msg_type']
389 reply_type = msg_type.split('_')[0] + '_reply'
389 reply_type = msg_type.split('_')[0] + '_reply'
390 reply_msg = self.session.send(self.reply_socket, reply_type,
390 reply_msg = self.session.send(self.reply_socket, reply_type,
391 {'status' : 'aborted'}, msg, ident=ident)
391 {'status' : 'aborted'}, msg, ident=ident)
392 logger.debug(reply_msg)
392 logger.debug(reply_msg)
393 # We need to wait a bit for requests to come in. This can probably
393 # We need to wait a bit for requests to come in. This can probably
394 # be set shorter for true asynchronous clients.
394 # be set shorter for true asynchronous clients.
395 time.sleep(0.1)
395 time.sleep(0.1)
396
396
397 def _raw_input(self, prompt, ident, parent):
397 def _raw_input(self, prompt, ident, parent):
398 # Flush output before making the request.
398 # Flush output before making the request.
399 sys.stderr.flush()
399 sys.stderr.flush()
400 sys.stdout.flush()
400 sys.stdout.flush()
401
401
402 # Send the input request.
402 # Send the input request.
403 content = dict(prompt=prompt)
403 content = dict(prompt=prompt)
404 msg = self.session.send(self.req_socket, u'input_request', content, parent)
404 msg = self.session.send(self.req_socket, u'input_request', content, parent)
405
405
406 # Await a response.
406 # Await a response.
407 ident, reply = self.session.recv(self.req_socket, 0)
407 ident, reply = self.session.recv(self.req_socket, 0)
408 try:
408 try:
409 value = reply['content']['value']
409 value = reply['content']['value']
410 except:
410 except:
411 logger.error("Got bad raw_input reply: ")
411 logger.error("Got bad raw_input reply: ")
412 logger.error(str(Message(parent)))
412 logger.error(str(Message(parent)))
413 value = ''
413 value = ''
414 return value
414 return value
415
415
416 def _complete(self, msg):
416 def _complete(self, msg):
417 c = msg['content']
417 c = msg['content']
418 try:
418 try:
419 cpos = int(c['cursor_pos'])
419 cpos = int(c['cursor_pos'])
420 except:
420 except:
421 # If we don't get something that we can convert to an integer, at
421 # If we don't get something that we can convert to an integer, at
422 # least attempt the completion guessing the cursor is at the end of
422 # least attempt the completion guessing the cursor is at the end of
423 # the text, if there's any, and otherwise of the line
423 # the text, if there's any, and otherwise of the line
424 cpos = len(c['text'])
424 cpos = len(c['text'])
425 if cpos==0:
425 if cpos==0:
426 cpos = len(c['line'])
426 cpos = len(c['line'])
427 return self.shell.complete(c['text'], c['line'], cpos)
427 return self.shell.complete(c['text'], c['line'], cpos)
428
428
429 def _object_info(self, context):
429 def _object_info(self, context):
430 symbol, leftover = self._symbol_from_context(context)
430 symbol, leftover = self._symbol_from_context(context)
431 if symbol is not None and not leftover:
431 if symbol is not None and not leftover:
432 doc = getattr(symbol, '__doc__', '')
432 doc = getattr(symbol, '__doc__', '')
433 else:
433 else:
434 doc = ''
434 doc = ''
435 object_info = dict(docstring = doc)
435 object_info = dict(docstring = doc)
436 return object_info
436 return object_info
437
437
438 def _symbol_from_context(self, context):
438 def _symbol_from_context(self, context):
439 if not context:
439 if not context:
440 return None, context
440 return None, context
441
441
442 base_symbol_string = context[0]
442 base_symbol_string = context[0]
443 symbol = self.shell.user_ns.get(base_symbol_string, None)
443 symbol = self.shell.user_ns.get(base_symbol_string, None)
444 if symbol is None:
444 if symbol is None:
445 symbol = __builtin__.__dict__.get(base_symbol_string, None)
445 symbol = __builtin__.__dict__.get(base_symbol_string, None)
446 if symbol is None:
446 if symbol is None:
447 return None, context
447 return None, context
448
448
449 context = context[1:]
449 context = context[1:]
450 for i, name in enumerate(context):
450 for i, name in enumerate(context):
451 new_symbol = getattr(symbol, name, None)
451 new_symbol = getattr(symbol, name, None)
452 if new_symbol is None:
452 if new_symbol is None:
453 return symbol, context[i:]
453 return symbol, context[i:]
454 else:
454 else:
455 symbol = new_symbol
455 symbol = new_symbol
456
456
457 return symbol, []
457 return symbol, []
458
458
459 def _at_shutdown(self):
459 def _at_shutdown(self):
460 """Actions taken at shutdown by the kernel, called by python's atexit.
460 """Actions taken at shutdown by the kernel, called by python's atexit.
461 """
461 """
462 # io.rprint("Kernel at_shutdown") # dbg
462 # io.rprint("Kernel at_shutdown") # dbg
463 if self._shutdown_message is not None:
463 if self._shutdown_message is not None:
464 self.session.send(self.reply_socket, self._shutdown_message)
464 self.session.send(self.reply_socket, self._shutdown_message)
465 self.session.send(self.pub_socket, self._shutdown_message)
465 self.session.send(self.pub_socket, self._shutdown_message)
466 logger.debug(str(self._shutdown_message))
466 logger.debug(str(self._shutdown_message))
467 # A very short sleep to give zmq time to flush its message buffers
467 # A very short sleep to give zmq time to flush its message buffers
468 # before Python truly shuts down.
468 # before Python truly shuts down.
469 time.sleep(0.01)
469 time.sleep(0.01)
470
470
471
471
472 class QtKernel(Kernel):
472 class QtKernel(Kernel):
473 """A Kernel subclass with Qt support."""
473 """A Kernel subclass with Qt support."""
474
474
475 def start(self):
475 def start(self):
476 """Start a kernel with QtPy4 event loop integration."""
476 """Start a kernel with QtPy4 event loop integration."""
477
477
478 from PyQt4 import QtCore
478 from PyQt4 import QtCore
479 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
479 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
480
480
481 self.app = get_app_qt4([" "])
481 self.app = get_app_qt4([" "])
482 self.app.setQuitOnLastWindowClosed(False)
482 self.app.setQuitOnLastWindowClosed(False)
483 self.timer = QtCore.QTimer()
483 self.timer = QtCore.QTimer()
484 self.timer.timeout.connect(self.do_one_iteration)
484 self.timer.timeout.connect(self.do_one_iteration)
485 # Units for the timer are in milliseconds
485 # Units for the timer are in milliseconds
486 self.timer.start(1000*self._poll_interval)
486 self.timer.start(1000*self._poll_interval)
487 start_event_loop_qt4(self.app)
487 start_event_loop_qt4(self.app)
488
488
489
489
490 class WxKernel(Kernel):
490 class WxKernel(Kernel):
491 """A Kernel subclass with Wx support."""
491 """A Kernel subclass with Wx support."""
492
492
493 def start(self):
493 def start(self):
494 """Start a kernel with wx event loop support."""
494 """Start a kernel with wx event loop support."""
495
495
496 import wx
496 import wx
497 from IPython.lib.guisupport import start_event_loop_wx
497 from IPython.lib.guisupport import start_event_loop_wx
498
498
499 doi = self.do_one_iteration
499 doi = self.do_one_iteration
500 # Wx uses milliseconds
500 # Wx uses milliseconds
501 poll_interval = int(1000*self._poll_interval)
501 poll_interval = int(1000*self._poll_interval)
502
502
503 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
503 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
504 # We make the Frame hidden when we create it in the main app below.
504 # We make the Frame hidden when we create it in the main app below.
505 class TimerFrame(wx.Frame):
505 class TimerFrame(wx.Frame):
506 def __init__(self, func):
506 def __init__(self, func):
507 wx.Frame.__init__(self, None, -1)
507 wx.Frame.__init__(self, None, -1)
508 self.timer = wx.Timer(self)
508 self.timer = wx.Timer(self)
509 # Units for the timer are in milliseconds
509 # Units for the timer are in milliseconds
510 self.timer.Start(poll_interval)
510 self.timer.Start(poll_interval)
511 self.Bind(wx.EVT_TIMER, self.on_timer)
511 self.Bind(wx.EVT_TIMER, self.on_timer)
512 self.func = func
512 self.func = func
513
513
514 def on_timer(self, event):
514 def on_timer(self, event):
515 self.func()
515 self.func()
516
516
517 # We need a custom wx.App to create our Frame subclass that has the
517 # We need a custom wx.App to create our Frame subclass that has the
518 # wx.Timer to drive the ZMQ event loop.
518 # wx.Timer to drive the ZMQ event loop.
519 class IPWxApp(wx.App):
519 class IPWxApp(wx.App):
520 def OnInit(self):
520 def OnInit(self):
521 self.frame = TimerFrame(doi)
521 self.frame = TimerFrame(doi)
522 self.frame.Show(False)
522 self.frame.Show(False)
523 return True
523 return True
524
524
525 # The redirect=False here makes sure that wx doesn't replace
525 # The redirect=False here makes sure that wx doesn't replace
526 # sys.stdout/stderr with its own classes.
526 # sys.stdout/stderr with its own classes.
527 self.app = IPWxApp(redirect=False)
527 self.app = IPWxApp(redirect=False)
528 start_event_loop_wx(self.app)
528 start_event_loop_wx(self.app)
529
529
530
530
531 class TkKernel(Kernel):
531 class TkKernel(Kernel):
532 """A Kernel subclass with Tk support."""
532 """A Kernel subclass with Tk support."""
533
533
534 def start(self):
534 def start(self):
535 """Start a Tk enabled event loop."""
535 """Start a Tk enabled event loop."""
536
536
537 import Tkinter
537 import Tkinter
538 doi = self.do_one_iteration
538 doi = self.do_one_iteration
539 # Tk uses milliseconds
539 # Tk uses milliseconds
540 poll_interval = int(1000*self._poll_interval)
540 poll_interval = int(1000*self._poll_interval)
541 # For Tkinter, we create a Tk object and call its withdraw method.
541 # For Tkinter, we create a Tk object and call its withdraw method.
542 class Timer(object):
542 class Timer(object):
543 def __init__(self, func):
543 def __init__(self, func):
544 self.app = Tkinter.Tk()
544 self.app = Tkinter.Tk()
545 self.app.withdraw()
545 self.app.withdraw()
546 self.func = func
546 self.func = func
547
547
548 def on_timer(self):
548 def on_timer(self):
549 self.func()
549 self.func()
550 self.app.after(poll_interval, self.on_timer)
550 self.app.after(poll_interval, self.on_timer)
551
551
552 def start(self):
552 def start(self):
553 self.on_timer() # Call it once to get things going.
553 self.on_timer() # Call it once to get things going.
554 self.app.mainloop()
554 self.app.mainloop()
555
555
556 self.timer = Timer(doi)
556 self.timer = Timer(doi)
557 self.timer.start()
557 self.timer.start()
558
558
559
559
560 class GTKKernel(Kernel):
560 class GTKKernel(Kernel):
561 """A Kernel subclass with GTK support."""
561 """A Kernel subclass with GTK support."""
562
562
563 def start(self):
563 def start(self):
564 """Start the kernel, coordinating with the GTK event loop"""
564 """Start the kernel, coordinating with the GTK event loop"""
565 from .gui.gtkembed import GTKEmbed
565 from .gui.gtkembed import GTKEmbed
566
566
567 gtk_kernel = GTKEmbed(self)
567 gtk_kernel = GTKEmbed(self)
568 gtk_kernel.start()
568 gtk_kernel.start()
569
569
570
570
571 #-----------------------------------------------------------------------------
571 #-----------------------------------------------------------------------------
572 # Kernel main and launch functions
572 # Kernel main and launch functions
573 #-----------------------------------------------------------------------------
573 #-----------------------------------------------------------------------------
574
574
575 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
575 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
576 stdin=None, stdout=None, stderr=None,
576 stdin=None, stdout=None, stderr=None,
577 executable=None, independent=False, pylab=False, colors=None):
577 executable=None, independent=False, pylab=False, colors=None):
578 """Launches a localhost kernel, binding to the specified ports.
578 """Launches a localhost kernel, binding to the specified ports.
579
579
580 Parameters
580 Parameters
581 ----------
581 ----------
582 ip : str, optional
582 ip : str, optional
583 The ip address the kernel will bind to.
583 The ip address the kernel will bind to.
584
584
585 xrep_port : int, optional
585 xrep_port : int, optional
586 The port to use for XREP channel.
586 The port to use for XREP channel.
587
587
588 pub_port : int, optional
588 pub_port : int, optional
589 The port to use for the SUB channel.
589 The port to use for the SUB channel.
590
590
591 req_port : int, optional
591 req_port : int, optional
592 The port to use for the REQ (raw input) channel.
592 The port to use for the REQ (raw input) channel.
593
593
594 hb_port : int, optional
594 hb_port : int, optional
595 The port to use for the hearbeat REP channel.
595 The port to use for the hearbeat REP channel.
596
596
597 stdin, stdout, stderr : optional (default None)
597 stdin, stdout, stderr : optional (default None)
598 Standards streams, as defined in subprocess.Popen.
598 Standards streams, as defined in subprocess.Popen.
599
599
600 executable : str, optional (default sys.executable)
600 executable : str, optional (default sys.executable)
601 The Python executable to use for the kernel process.
601 The Python executable to use for the kernel process.
602
602
603 independent : bool, optional (default False)
603 independent : bool, optional (default False)
604 If set, the kernel process is guaranteed to survive if this process
604 If set, the kernel process is guaranteed to survive if this process
605 dies. If not set, an effort is made to ensure that the kernel is killed
605 dies. If not set, an effort is made to ensure that the kernel is killed
606 when this process dies. Note that in this case it is still good practice
606 when this process dies. Note that in this case it is still good practice
607 to kill kernels manually before exiting.
607 to kill kernels manually before exiting.
608
608
609 pylab : bool or string, optional (default False)
609 pylab : bool or string, optional (default False)
610 If not False, the kernel will be launched with pylab enabled. If a
610 If not False, the kernel will be launched with pylab enabled. If a
611 string is passed, matplotlib will use the specified backend. Otherwise,
611 string is passed, matplotlib will use the specified backend. Otherwise,
612 matplotlib's default backend will be used.
612 matplotlib's default backend will be used.
613
613
614 colors : None or string, optional (default None)
614 colors : None or string, optional (default None)
615 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
615 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
616
616
617 Returns
617 Returns
618 -------
618 -------
619 A tuple of form:
619 A tuple of form:
620 (kernel_process, xrep_port, pub_port, req_port)
620 (kernel_process, xrep_port, pub_port, req_port)
621 where kernel_process is a Popen object and the ports are integers.
621 where kernel_process is a Popen object and the ports are integers.
622 """
622 """
623 extra_arguments = []
623 extra_arguments = []
624 if pylab:
624 if pylab:
625 extra_arguments.append('--pylab')
625 extra_arguments.append('--pylab')
626 if isinstance(pylab, basestring):
626 if isinstance(pylab, basestring):
627 extra_arguments.append(pylab)
627 extra_arguments.append(pylab)
628 if ip is not None:
628 if ip is not None:
629 extra_arguments.append('--ip')
629 extra_arguments.append('--ip')
630 if isinstance(ip, basestring):
630 if isinstance(ip, basestring):
631 extra_arguments.append(ip)
631 extra_arguments.append(ip)
632 if colors is not None:
632 if colors is not None:
633 extra_arguments.append('--colors')
633 extra_arguments.append('--colors')
634 extra_arguments.append(colors)
634 extra_arguments.append(colors)
635 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
635 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
636 xrep_port, pub_port, req_port, hb_port,
636 xrep_port, pub_port, req_port, hb_port,
637 stdin, stdout, stderr,
637 stdin, stdout, stderr,
638 executable, independent, extra_arguments)
638 executable, independent, extra_arguments)
639
639
640
640
641 def main():
641 def main():
642 """ The IPython kernel main entry point.
642 """ The IPython kernel main entry point.
643 """
643 """
644 parser = make_argument_parser()
644 parser = make_argument_parser()
645 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
645 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
646 const='auto', help = \
646 const='auto', help = \
647 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
647 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
648 given, the GUI backend is matplotlib's, otherwise use one of: \
648 given, the GUI backend is matplotlib's, otherwise use one of: \
649 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
649 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
650 parser.add_argument('--colors',
650 parser.add_argument('--colors',
651 type=str, dest='colors',
651 type=str, dest='colors',
652 help="Set the color scheme (NoColor, Linux, and LightBG).",
652 help="Set the color scheme (NoColor, Linux, and LightBG).",
653 metavar='ZMQInteractiveShell.colors')
653 metavar='ZMQInteractiveShell.colors')
654 namespace = parser.parse_args()
654 namespace = parser.parse_args()
655
655
656 kernel_class = Kernel
656 kernel_class = Kernel
657
657
658 kernel_classes = {
658 kernel_classes = {
659 'qt' : QtKernel,
659 'qt' : QtKernel,
660 'qt4': QtKernel,
660 'qt4': QtKernel,
661 'inline': Kernel,
661 'inline': Kernel,
662 'osx': TkKernel,
662 'osx': TkKernel,
663 'wx' : WxKernel,
663 'wx' : WxKernel,
664 'tk' : TkKernel,
664 'tk' : TkKernel,
665 'gtk': GTKKernel,
665 'gtk': GTKKernel,
666 }
666 }
667 if namespace.pylab:
667 if namespace.pylab:
668 if namespace.pylab == 'auto':
668 if namespace.pylab == 'auto':
669 gui, backend = pylabtools.find_gui_and_backend()
669 gui, backend = pylabtools.find_gui_and_backend()
670 else:
670 else:
671 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
671 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
672 kernel_class = kernel_classes.get(gui)
672 kernel_class = kernel_classes.get(gui)
673 if kernel_class is None:
673 if kernel_class is None:
674 raise ValueError('GUI is not supported: %r' % gui)
674 raise ValueError('GUI is not supported: %r' % gui)
675 pylabtools.activate_matplotlib(backend)
675 pylabtools.activate_matplotlib(backend)
676 if namespace.colors:
676 if namespace.colors:
677 ZMQInteractiveShell.colors=namespace.colors
677 ZMQInteractiveShell.colors=namespace.colors
678
678
679 kernel = make_kernel(namespace, kernel_class, OutStream)
679 kernel = make_kernel(namespace, kernel_class, OutStream)
680
680
681 if namespace.pylab:
681 if namespace.pylab:
682 pylabtools.import_pylab(kernel.shell.user_ns, backend,
682 pylabtools.import_pylab(kernel.shell.user_ns, backend,
683 shell=kernel.shell)
683 shell=kernel.shell)
684
684
685 start_kernel(namespace, kernel)
685 start_kernel(namespace, kernel)
686
686
687
687
688 if __name__ == '__main__':
688 if __name__ == '__main__':
689 main()
689 main()
General Comments 0
You need to be logged in to leave comments. Login now