##// END OF EJS Templates
allow '--pylab osx' in qtconsole...
MinRK -
Show More
@@ -1,658 +1,659 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_tail_request',
125 'object_info_request', 'history_tail_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 time.sleep(self._poll_interval)
170 time.sleep(self._poll_interval)
171 self.do_one_iteration()
171 self.do_one_iteration()
172
172
173 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
173 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
174 """Record the ports that this kernel is using.
174 """Record the ports that this kernel is using.
175
175
176 The creator of the Kernel instance must call this methods if they
176 The creator of the Kernel instance must call this methods if they
177 want the :meth:`connect_request` method to return the port numbers.
177 want the :meth:`connect_request` method to return the port numbers.
178 """
178 """
179 self._recorded_ports = {
179 self._recorded_ports = {
180 'xrep_port' : xrep_port,
180 'xrep_port' : xrep_port,
181 'pub_port' : pub_port,
181 'pub_port' : pub_port,
182 'req_port' : req_port,
182 'req_port' : req_port,
183 'hb_port' : hb_port
183 'hb_port' : hb_port
184 }
184 }
185
185
186 #---------------------------------------------------------------------------
186 #---------------------------------------------------------------------------
187 # Kernel request handlers
187 # Kernel request handlers
188 #---------------------------------------------------------------------------
188 #---------------------------------------------------------------------------
189
189
190 def _publish_pyin(self, code, parent):
190 def _publish_pyin(self, code, parent):
191 """Publish the code request on the pyin stream."""
191 """Publish the code request on the pyin stream."""
192
192
193 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
193 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
194
194
195 def execute_request(self, ident, parent):
195 def execute_request(self, ident, parent):
196
196
197 status_msg = self.session.send(self.pub_socket,
197 status_msg = self.session.send(self.pub_socket,
198 u'status',
198 u'status',
199 {u'execution_state':u'busy'},
199 {u'execution_state':u'busy'},
200 parent=parent
200 parent=parent
201 )
201 )
202
202
203 try:
203 try:
204 content = parent[u'content']
204 content = parent[u'content']
205 code = content[u'code']
205 code = content[u'code']
206 silent = content[u'silent']
206 silent = content[u'silent']
207 except:
207 except:
208 logger.error("Got bad msg: ")
208 logger.error("Got bad msg: ")
209 logger.error(str(Message(parent)))
209 logger.error(str(Message(parent)))
210 return
210 return
211
211
212 shell = self.shell # we'll need this a lot here
212 shell = self.shell # we'll need this a lot here
213
213
214 # Replace raw_input. Note that is not sufficient to replace
214 # Replace raw_input. Note that is not sufficient to replace
215 # raw_input in the user namespace.
215 # raw_input in the user namespace.
216 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
216 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
217 __builtin__.raw_input = raw_input
217 __builtin__.raw_input = raw_input
218
218
219 # Set the parent message of the display hook and out streams.
219 # Set the parent message of the display hook and out streams.
220 shell.displayhook.set_parent(parent)
220 shell.displayhook.set_parent(parent)
221 shell.display_pub.set_parent(parent)
221 shell.display_pub.set_parent(parent)
222 sys.stdout.set_parent(parent)
222 sys.stdout.set_parent(parent)
223 sys.stderr.set_parent(parent)
223 sys.stderr.set_parent(parent)
224
224
225 # Re-broadcast our input for the benefit of listening clients, and
225 # Re-broadcast our input for the benefit of listening clients, and
226 # start computing output
226 # start computing output
227 if not silent:
227 if not silent:
228 self._publish_pyin(code, parent)
228 self._publish_pyin(code, parent)
229
229
230 reply_content = {}
230 reply_content = {}
231 try:
231 try:
232 if silent:
232 if silent:
233 # run_code uses 'exec' mode, so no displayhook will fire, and it
233 # run_code uses 'exec' mode, so no displayhook will fire, and it
234 # doesn't call logging or history manipulations. Print
234 # doesn't call logging or history manipulations. Print
235 # statements in that code will obviously still execute.
235 # statements in that code will obviously still execute.
236 shell.run_code(code)
236 shell.run_code(code)
237 else:
237 else:
238 # FIXME: the shell calls the exception handler itself.
238 # FIXME: the shell calls the exception handler itself.
239 shell._reply_content = None
239 shell._reply_content = None
240 shell.run_cell(code)
240 shell.run_cell(code)
241 except:
241 except:
242 status = u'error'
242 status = u'error'
243 # FIXME: this code right now isn't being used yet by default,
243 # FIXME: this code right now isn't being used yet by default,
244 # because the run_cell() call above directly fires off exception
244 # because the run_cell() call above directly fires off exception
245 # reporting. This code, therefore, is only active in the scenario
245 # reporting. This code, therefore, is only active in the scenario
246 # where runlines itself has an unhandled exception. We need to
246 # where runlines itself has an unhandled exception. We need to
247 # uniformize this, for all exception construction to come from a
247 # uniformize this, for all exception construction to come from a
248 # single location in the codbase.
248 # single location in the codbase.
249 etype, evalue, tb = sys.exc_info()
249 etype, evalue, tb = sys.exc_info()
250 tb_list = traceback.format_exception(etype, evalue, tb)
250 tb_list = traceback.format_exception(etype, evalue, tb)
251 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
251 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
252 else:
252 else:
253 status = u'ok'
253 status = u'ok'
254
254
255 reply_content[u'status'] = status
255 reply_content[u'status'] = status
256
256
257 # Return the execution counter so clients can display prompts
257 # Return the execution counter so clients can display prompts
258 reply_content['execution_count'] = shell.execution_count -1
258 reply_content['execution_count'] = shell.execution_count -1
259
259
260 # FIXME - fish exception info out of shell, possibly left there by
260 # FIXME - fish exception info out of shell, possibly left there by
261 # runlines. We'll need to clean up this logic later.
261 # runlines. We'll need to clean up this logic later.
262 if shell._reply_content is not None:
262 if shell._reply_content is not None:
263 reply_content.update(shell._reply_content)
263 reply_content.update(shell._reply_content)
264
264
265 # At this point, we can tell whether the main code execution succeeded
265 # At this point, we can tell whether the main code execution succeeded
266 # or not. If it did, we proceed to evaluate user_variables/expressions
266 # or not. If it did, we proceed to evaluate user_variables/expressions
267 if reply_content['status'] == 'ok':
267 if reply_content['status'] == 'ok':
268 reply_content[u'user_variables'] = \
268 reply_content[u'user_variables'] = \
269 shell.user_variables(content[u'user_variables'])
269 shell.user_variables(content[u'user_variables'])
270 reply_content[u'user_expressions'] = \
270 reply_content[u'user_expressions'] = \
271 shell.user_expressions(content[u'user_expressions'])
271 shell.user_expressions(content[u'user_expressions'])
272 else:
272 else:
273 # If there was an error, don't even try to compute variables or
273 # If there was an error, don't even try to compute variables or
274 # expressions
274 # expressions
275 reply_content[u'user_variables'] = {}
275 reply_content[u'user_variables'] = {}
276 reply_content[u'user_expressions'] = {}
276 reply_content[u'user_expressions'] = {}
277
277
278 # Payloads should be retrieved regardless of outcome, so we can both
278 # Payloads should be retrieved regardless of outcome, so we can both
279 # recover partial output (that could have been generated early in a
279 # recover partial output (that could have been generated early in a
280 # block, before an error) and clear the payload system always.
280 # block, before an error) and clear the payload system always.
281 reply_content[u'payload'] = shell.payload_manager.read_payload()
281 reply_content[u'payload'] = shell.payload_manager.read_payload()
282 # Be agressive about clearing the payload because we don't want
282 # Be agressive about clearing the payload because we don't want
283 # it to sit in memory until the next execute_request comes in.
283 # it to sit in memory until the next execute_request comes in.
284 shell.payload_manager.clear_payload()
284 shell.payload_manager.clear_payload()
285
285
286 # Flush output before sending the reply.
286 # Flush output before sending the reply.
287 sys.stdout.flush()
287 sys.stdout.flush()
288 sys.stderr.flush()
288 sys.stderr.flush()
289 # FIXME: on rare occasions, the flush doesn't seem to make it to the
289 # FIXME: on rare occasions, the flush doesn't seem to make it to the
290 # clients... This seems to mitigate the problem, but we definitely need
290 # clients... This seems to mitigate the problem, but we definitely need
291 # to better understand what's going on.
291 # to better understand what's going on.
292 if self._execute_sleep:
292 if self._execute_sleep:
293 time.sleep(self._execute_sleep)
293 time.sleep(self._execute_sleep)
294
294
295 # Send the reply.
295 # Send the reply.
296 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
296 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
297 reply_content, parent, ident=ident)
297 reply_content, parent, ident=ident)
298 logger.debug(str(reply_msg))
298 logger.debug(str(reply_msg))
299
299
300 if reply_msg['content']['status'] == u'error':
300 if reply_msg['content']['status'] == u'error':
301 self._abort_queue()
301 self._abort_queue()
302
302
303 status_msg = self.session.send(self.pub_socket,
303 status_msg = self.session.send(self.pub_socket,
304 u'status',
304 u'status',
305 {u'execution_state':u'idle'},
305 {u'execution_state':u'idle'},
306 parent=parent
306 parent=parent
307 )
307 )
308
308
309 def complete_request(self, ident, parent):
309 def complete_request(self, ident, parent):
310 txt, matches = self._complete(parent)
310 txt, matches = self._complete(parent)
311 matches = {'matches' : matches,
311 matches = {'matches' : matches,
312 'matched_text' : txt,
312 'matched_text' : txt,
313 'status' : 'ok'}
313 'status' : 'ok'}
314 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
314 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
315 matches, parent, ident)
315 matches, parent, ident)
316 logger.debug(str(completion_msg))
316 logger.debug(str(completion_msg))
317
317
318 def object_info_request(self, ident, parent):
318 def object_info_request(self, ident, parent):
319 object_info = self.shell.object_inspect(parent['content']['oname'])
319 object_info = self.shell.object_inspect(parent['content']['oname'])
320 # Before we send this object over, we scrub it for JSON usage
320 # Before we send this object over, we scrub it for JSON usage
321 oinfo = json_clean(object_info)
321 oinfo = json_clean(object_info)
322 msg = self.session.send(self.reply_socket, 'object_info_reply',
322 msg = self.session.send(self.reply_socket, 'object_info_reply',
323 oinfo, parent, ident)
323 oinfo, parent, ident)
324 logger.debug(msg)
324 logger.debug(msg)
325
325
326 def history_tail_request(self, ident, parent):
326 def history_tail_request(self, ident, parent):
327 # We need to pull these out, as passing **kwargs doesn't work with
327 # We need to pull these out, as passing **kwargs doesn't work with
328 # unicode keys before Python 2.6.5.
328 # unicode keys before Python 2.6.5.
329 n = parent['content']['n']
329 n = parent['content']['n']
330 raw = parent['content']['raw']
330 raw = parent['content']['raw']
331 output = parent['content']['output']
331 output = parent['content']['output']
332 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output)
332 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output)
333 content = {'history' : list(hist)}
333 content = {'history' : list(hist)}
334 msg = self.session.send(self.reply_socket, 'history_tail_reply',
334 msg = self.session.send(self.reply_socket, 'history_tail_reply',
335 content, parent, ident)
335 content, parent, ident)
336 logger.debug(str(msg))
336 logger.debug(str(msg))
337
337
338 def connect_request(self, ident, parent):
338 def connect_request(self, ident, parent):
339 if self._recorded_ports is not None:
339 if self._recorded_ports is not None:
340 content = self._recorded_ports.copy()
340 content = self._recorded_ports.copy()
341 else:
341 else:
342 content = {}
342 content = {}
343 msg = self.session.send(self.reply_socket, 'connect_reply',
343 msg = self.session.send(self.reply_socket, 'connect_reply',
344 content, parent, ident)
344 content, parent, ident)
345 logger.debug(msg)
345 logger.debug(msg)
346
346
347 def shutdown_request(self, ident, parent):
347 def shutdown_request(self, ident, parent):
348 self.shell.exit_now = True
348 self.shell.exit_now = True
349 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
349 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
350 sys.exit(0)
350 sys.exit(0)
351
351
352 #---------------------------------------------------------------------------
352 #---------------------------------------------------------------------------
353 # Protected interface
353 # Protected interface
354 #---------------------------------------------------------------------------
354 #---------------------------------------------------------------------------
355
355
356 def _abort_queue(self):
356 def _abort_queue(self):
357 while True:
357 while True:
358 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
358 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
359 if msg is None:
359 if msg is None:
360 break
360 break
361 else:
361 else:
362 assert ident is not None, \
362 assert ident is not None, \
363 "Unexpected missing message part."
363 "Unexpected missing message part."
364
364
365 logger.debug("Aborting:\n"+str(Message(msg)))
365 logger.debug("Aborting:\n"+str(Message(msg)))
366 msg_type = msg['msg_type']
366 msg_type = msg['msg_type']
367 reply_type = msg_type.split('_')[0] + '_reply'
367 reply_type = msg_type.split('_')[0] + '_reply'
368 reply_msg = self.session.send(self.reply_socket, reply_type,
368 reply_msg = self.session.send(self.reply_socket, reply_type,
369 {'status' : 'aborted'}, msg, ident=ident)
369 {'status' : 'aborted'}, msg, ident=ident)
370 logger.debug(reply_msg)
370 logger.debug(reply_msg)
371 # We need to wait a bit for requests to come in. This can probably
371 # We need to wait a bit for requests to come in. This can probably
372 # be set shorter for true asynchronous clients.
372 # be set shorter for true asynchronous clients.
373 time.sleep(0.1)
373 time.sleep(0.1)
374
374
375 def _raw_input(self, prompt, ident, parent):
375 def _raw_input(self, prompt, ident, parent):
376 # Flush output before making the request.
376 # Flush output before making the request.
377 sys.stderr.flush()
377 sys.stderr.flush()
378 sys.stdout.flush()
378 sys.stdout.flush()
379
379
380 # Send the input request.
380 # Send the input request.
381 content = dict(prompt=prompt)
381 content = dict(prompt=prompt)
382 msg = self.session.send(self.req_socket, u'input_request', content, parent)
382 msg = self.session.send(self.req_socket, u'input_request', content, parent)
383
383
384 # Await a response.
384 # Await a response.
385 ident, reply = self.session.recv(self.req_socket, 0)
385 ident, reply = self.session.recv(self.req_socket, 0)
386 try:
386 try:
387 value = reply['content']['value']
387 value = reply['content']['value']
388 except:
388 except:
389 logger.error("Got bad raw_input reply: ")
389 logger.error("Got bad raw_input reply: ")
390 logger.error(str(Message(parent)))
390 logger.error(str(Message(parent)))
391 value = ''
391 value = ''
392 return value
392 return value
393
393
394 def _complete(self, msg):
394 def _complete(self, msg):
395 c = msg['content']
395 c = msg['content']
396 try:
396 try:
397 cpos = int(c['cursor_pos'])
397 cpos = int(c['cursor_pos'])
398 except:
398 except:
399 # If we don't get something that we can convert to an integer, at
399 # If we don't get something that we can convert to an integer, at
400 # least attempt the completion guessing the cursor is at the end of
400 # least attempt the completion guessing the cursor is at the end of
401 # the text, if there's any, and otherwise of the line
401 # the text, if there's any, and otherwise of the line
402 cpos = len(c['text'])
402 cpos = len(c['text'])
403 if cpos==0:
403 if cpos==0:
404 cpos = len(c['line'])
404 cpos = len(c['line'])
405 return self.shell.complete(c['text'], c['line'], cpos)
405 return self.shell.complete(c['text'], c['line'], cpos)
406
406
407 def _object_info(self, context):
407 def _object_info(self, context):
408 symbol, leftover = self._symbol_from_context(context)
408 symbol, leftover = self._symbol_from_context(context)
409 if symbol is not None and not leftover:
409 if symbol is not None and not leftover:
410 doc = getattr(symbol, '__doc__', '')
410 doc = getattr(symbol, '__doc__', '')
411 else:
411 else:
412 doc = ''
412 doc = ''
413 object_info = dict(docstring = doc)
413 object_info = dict(docstring = doc)
414 return object_info
414 return object_info
415
415
416 def _symbol_from_context(self, context):
416 def _symbol_from_context(self, context):
417 if not context:
417 if not context:
418 return None, context
418 return None, context
419
419
420 base_symbol_string = context[0]
420 base_symbol_string = context[0]
421 symbol = self.shell.user_ns.get(base_symbol_string, None)
421 symbol = self.shell.user_ns.get(base_symbol_string, None)
422 if symbol is None:
422 if symbol is None:
423 symbol = __builtin__.__dict__.get(base_symbol_string, None)
423 symbol = __builtin__.__dict__.get(base_symbol_string, None)
424 if symbol is None:
424 if symbol is None:
425 return None, context
425 return None, context
426
426
427 context = context[1:]
427 context = context[1:]
428 for i, name in enumerate(context):
428 for i, name in enumerate(context):
429 new_symbol = getattr(symbol, name, None)
429 new_symbol = getattr(symbol, name, None)
430 if new_symbol is None:
430 if new_symbol is None:
431 return symbol, context[i:]
431 return symbol, context[i:]
432 else:
432 else:
433 symbol = new_symbol
433 symbol = new_symbol
434
434
435 return symbol, []
435 return symbol, []
436
436
437 def _at_shutdown(self):
437 def _at_shutdown(self):
438 """Actions taken at shutdown by the kernel, called by python's atexit.
438 """Actions taken at shutdown by the kernel, called by python's atexit.
439 """
439 """
440 # io.rprint("Kernel at_shutdown") # dbg
440 # io.rprint("Kernel at_shutdown") # dbg
441 if self._shutdown_message is not None:
441 if self._shutdown_message is not None:
442 self.session.send(self.reply_socket, self._shutdown_message)
442 self.session.send(self.reply_socket, self._shutdown_message)
443 self.session.send(self.pub_socket, self._shutdown_message)
443 self.session.send(self.pub_socket, self._shutdown_message)
444 logger.debug(str(self._shutdown_message))
444 logger.debug(str(self._shutdown_message))
445 # A very short sleep to give zmq time to flush its message buffers
445 # A very short sleep to give zmq time to flush its message buffers
446 # before Python truly shuts down.
446 # before Python truly shuts down.
447 time.sleep(0.01)
447 time.sleep(0.01)
448
448
449
449
450 class QtKernel(Kernel):
450 class QtKernel(Kernel):
451 """A Kernel subclass with Qt support."""
451 """A Kernel subclass with Qt support."""
452
452
453 def start(self):
453 def start(self):
454 """Start a kernel with QtPy4 event loop integration."""
454 """Start a kernel with QtPy4 event loop integration."""
455
455
456 from PyQt4 import QtCore
456 from PyQt4 import QtCore
457 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
457 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
458
458
459 self.app = get_app_qt4([" "])
459 self.app = get_app_qt4([" "])
460 self.app.setQuitOnLastWindowClosed(False)
460 self.app.setQuitOnLastWindowClosed(False)
461 self.timer = QtCore.QTimer()
461 self.timer = QtCore.QTimer()
462 self.timer.timeout.connect(self.do_one_iteration)
462 self.timer.timeout.connect(self.do_one_iteration)
463 # Units for the timer are in milliseconds
463 # Units for the timer are in milliseconds
464 self.timer.start(1000*self._poll_interval)
464 self.timer.start(1000*self._poll_interval)
465 start_event_loop_qt4(self.app)
465 start_event_loop_qt4(self.app)
466
466
467
467
468 class WxKernel(Kernel):
468 class WxKernel(Kernel):
469 """A Kernel subclass with Wx support."""
469 """A Kernel subclass with Wx support."""
470
470
471 def start(self):
471 def start(self):
472 """Start a kernel with wx event loop support."""
472 """Start a kernel with wx event loop support."""
473
473
474 import wx
474 import wx
475 from IPython.lib.guisupport import start_event_loop_wx
475 from IPython.lib.guisupport import start_event_loop_wx
476
476
477 doi = self.do_one_iteration
477 doi = self.do_one_iteration
478 # Wx uses milliseconds
478 # Wx uses milliseconds
479 poll_interval = int(1000*self._poll_interval)
479 poll_interval = int(1000*self._poll_interval)
480
480
481 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
481 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
482 # We make the Frame hidden when we create it in the main app below.
482 # We make the Frame hidden when we create it in the main app below.
483 class TimerFrame(wx.Frame):
483 class TimerFrame(wx.Frame):
484 def __init__(self, func):
484 def __init__(self, func):
485 wx.Frame.__init__(self, None, -1)
485 wx.Frame.__init__(self, None, -1)
486 self.timer = wx.Timer(self)
486 self.timer = wx.Timer(self)
487 # Units for the timer are in milliseconds
487 # Units for the timer are in milliseconds
488 self.timer.Start(poll_interval)
488 self.timer.Start(poll_interval)
489 self.Bind(wx.EVT_TIMER, self.on_timer)
489 self.Bind(wx.EVT_TIMER, self.on_timer)
490 self.func = func
490 self.func = func
491
491
492 def on_timer(self, event):
492 def on_timer(self, event):
493 self.func()
493 self.func()
494
494
495 # We need a custom wx.App to create our Frame subclass that has the
495 # We need a custom wx.App to create our Frame subclass that has the
496 # wx.Timer to drive the ZMQ event loop.
496 # wx.Timer to drive the ZMQ event loop.
497 class IPWxApp(wx.App):
497 class IPWxApp(wx.App):
498 def OnInit(self):
498 def OnInit(self):
499 self.frame = TimerFrame(doi)
499 self.frame = TimerFrame(doi)
500 self.frame.Show(False)
500 self.frame.Show(False)
501 return True
501 return True
502
502
503 # The redirect=False here makes sure that wx doesn't replace
503 # The redirect=False here makes sure that wx doesn't replace
504 # sys.stdout/stderr with its own classes.
504 # sys.stdout/stderr with its own classes.
505 self.app = IPWxApp(redirect=False)
505 self.app = IPWxApp(redirect=False)
506 start_event_loop_wx(self.app)
506 start_event_loop_wx(self.app)
507
507
508
508
509 class TkKernel(Kernel):
509 class TkKernel(Kernel):
510 """A Kernel subclass with Tk support."""
510 """A Kernel subclass with Tk support."""
511
511
512 def start(self):
512 def start(self):
513 """Start a Tk enabled event loop."""
513 """Start a Tk enabled event loop."""
514
514
515 import Tkinter
515 import Tkinter
516 doi = self.do_one_iteration
516 doi = self.do_one_iteration
517 # Tk uses milliseconds
517 # Tk uses milliseconds
518 poll_interval = int(1000*self._poll_interval)
518 poll_interval = int(1000*self._poll_interval)
519 # For Tkinter, we create a Tk object and call its withdraw method.
519 # For Tkinter, we create a Tk object and call its withdraw method.
520 class Timer(object):
520 class Timer(object):
521 def __init__(self, func):
521 def __init__(self, func):
522 self.app = Tkinter.Tk()
522 self.app = Tkinter.Tk()
523 self.app.withdraw()
523 self.app.withdraw()
524 self.func = func
524 self.func = func
525
525
526 def on_timer(self):
526 def on_timer(self):
527 self.func()
527 self.func()
528 self.app.after(poll_interval, self.on_timer)
528 self.app.after(poll_interval, self.on_timer)
529
529
530 def start(self):
530 def start(self):
531 self.on_timer() # Call it once to get things going.
531 self.on_timer() # Call it once to get things going.
532 self.app.mainloop()
532 self.app.mainloop()
533
533
534 self.timer = Timer(doi)
534 self.timer = Timer(doi)
535 self.timer.start()
535 self.timer.start()
536
536
537
537
538 class GTKKernel(Kernel):
538 class GTKKernel(Kernel):
539 """A Kernel subclass with GTK support."""
539 """A Kernel subclass with GTK support."""
540
540
541 def start(self):
541 def start(self):
542 """Start the kernel, coordinating with the GTK event loop"""
542 """Start the kernel, coordinating with the GTK event loop"""
543 from .gui.gtkembed import GTKEmbed
543 from .gui.gtkembed import GTKEmbed
544
544
545 gtk_kernel = GTKEmbed(self)
545 gtk_kernel = GTKEmbed(self)
546 gtk_kernel.start()
546 gtk_kernel.start()
547
547
548
548
549 #-----------------------------------------------------------------------------
549 #-----------------------------------------------------------------------------
550 # Kernel main and launch functions
550 # Kernel main and launch functions
551 #-----------------------------------------------------------------------------
551 #-----------------------------------------------------------------------------
552
552
553 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
553 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
554 independent=False, pylab=False, colors=None):
554 independent=False, pylab=False, colors=None):
555 """Launches a localhost kernel, binding to the specified ports.
555 """Launches a localhost kernel, binding to the specified ports.
556
556
557 Parameters
557 Parameters
558 ----------
558 ----------
559 ip : str, optional
559 ip : str, optional
560 The ip address the kernel will bind to.
560 The ip address the kernel will bind to.
561
561
562 xrep_port : int, optional
562 xrep_port : int, optional
563 The port to use for XREP channel.
563 The port to use for XREP channel.
564
564
565 pub_port : int, optional
565 pub_port : int, optional
566 The port to use for the SUB channel.
566 The port to use for the SUB channel.
567
567
568 req_port : int, optional
568 req_port : int, optional
569 The port to use for the REQ (raw input) channel.
569 The port to use for the REQ (raw input) channel.
570
570
571 hb_port : int, optional
571 hb_port : int, optional
572 The port to use for the hearbeat REP channel.
572 The port to use for the hearbeat REP channel.
573
573
574 independent : bool, optional (default False)
574 independent : bool, optional (default False)
575 If set, the kernel process is guaranteed to survive if this process
575 If set, the kernel process is guaranteed to survive if this process
576 dies. If not set, an effort is made to ensure that the kernel is killed
576 dies. If not set, an effort is made to ensure that the kernel is killed
577 when this process dies. Note that in this case it is still good practice
577 when this process dies. Note that in this case it is still good practice
578 to kill kernels manually before exiting.
578 to kill kernels manually before exiting.
579
579
580 pylab : bool or string, optional (default False)
580 pylab : bool or string, optional (default False)
581 If not False, the kernel will be launched with pylab enabled. If a
581 If not False, the kernel will be launched with pylab enabled. If a
582 string is passed, matplotlib will use the specified backend. Otherwise,
582 string is passed, matplotlib will use the specified backend. Otherwise,
583 matplotlib's default backend will be used.
583 matplotlib's default backend will be used.
584
584
585 colors : None or string, optional (default None)
585 colors : None or string, optional (default None)
586 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
586 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
587
587
588 Returns
588 Returns
589 -------
589 -------
590 A tuple of form:
590 A tuple of form:
591 (kernel_process, xrep_port, pub_port, req_port)
591 (kernel_process, xrep_port, pub_port, req_port)
592 where kernel_process is a Popen object and the ports are integers.
592 where kernel_process is a Popen object and the ports are integers.
593 """
593 """
594 extra_arguments = []
594 extra_arguments = []
595 if pylab:
595 if pylab:
596 extra_arguments.append('--pylab')
596 extra_arguments.append('--pylab')
597 if isinstance(pylab, basestring):
597 if isinstance(pylab, basestring):
598 extra_arguments.append(pylab)
598 extra_arguments.append(pylab)
599 if ip is not None:
599 if ip is not None:
600 extra_arguments.append('--ip')
600 extra_arguments.append('--ip')
601 if isinstance(ip, basestring):
601 if isinstance(ip, basestring):
602 extra_arguments.append(ip)
602 extra_arguments.append(ip)
603 if colors is not None:
603 if colors is not None:
604 extra_arguments.append('--colors')
604 extra_arguments.append('--colors')
605 extra_arguments.append(colors)
605 extra_arguments.append(colors)
606 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
606 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
607 xrep_port, pub_port, req_port, hb_port,
607 xrep_port, pub_port, req_port, hb_port,
608 independent, extra_arguments)
608 independent, extra_arguments)
609
609
610
610
611 def main():
611 def main():
612 """ The IPython kernel main entry point.
612 """ The IPython kernel main entry point.
613 """
613 """
614 parser = make_argument_parser()
614 parser = make_argument_parser()
615 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
615 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
616 const='auto', help = \
616 const='auto', help = \
617 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
617 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
618 given, the GUI backend is matplotlib's, otherwise use one of: \
618 given, the GUI backend is matplotlib's, otherwise use one of: \
619 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
619 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
620 parser.add_argument('--colors',
620 parser.add_argument('--colors',
621 type=str, dest='colors',
621 type=str, dest='colors',
622 help="Set the color scheme (NoColor, Linux, and LightBG).",
622 help="Set the color scheme (NoColor, Linux, and LightBG).",
623 metavar='ZMQInteractiveShell.colors')
623 metavar='ZMQInteractiveShell.colors')
624 namespace = parser.parse_args()
624 namespace = parser.parse_args()
625
625
626 kernel_class = Kernel
626 kernel_class = Kernel
627
627
628 kernel_classes = {
628 kernel_classes = {
629 'qt' : QtKernel,
629 'qt' : QtKernel,
630 'qt4': QtKernel,
630 'qt4': QtKernel,
631 'inline': Kernel,
631 'inline': Kernel,
632 'osx': TkKernel,
632 'wx' : WxKernel,
633 'wx' : WxKernel,
633 'tk' : TkKernel,
634 'tk' : TkKernel,
634 'gtk': GTKKernel,
635 'gtk': GTKKernel,
635 }
636 }
636 if namespace.pylab:
637 if namespace.pylab:
637 if namespace.pylab == 'auto':
638 if namespace.pylab == 'auto':
638 gui, backend = pylabtools.find_gui_and_backend()
639 gui, backend = pylabtools.find_gui_and_backend()
639 else:
640 else:
640 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
641 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
641 kernel_class = kernel_classes.get(gui)
642 kernel_class = kernel_classes.get(gui)
642 if kernel_class is None:
643 if kernel_class is None:
643 raise ValueError('GUI is not supported: %r' % gui)
644 raise ValueError('GUI is not supported: %r' % gui)
644 pylabtools.activate_matplotlib(backend)
645 pylabtools.activate_matplotlib(backend)
645 if namespace.colors:
646 if namespace.colors:
646 ZMQInteractiveShell.colors=namespace.colors
647 ZMQInteractiveShell.colors=namespace.colors
647
648
648 kernel = make_kernel(namespace, kernel_class, OutStream)
649 kernel = make_kernel(namespace, kernel_class, OutStream)
649
650
650 if namespace.pylab:
651 if namespace.pylab:
651 pylabtools.import_pylab(kernel.shell.user_ns, backend,
652 pylabtools.import_pylab(kernel.shell.user_ns, backend,
652 shell=kernel.shell)
653 shell=kernel.shell)
653
654
654 start_kernel(namespace, kernel)
655 start_kernel(namespace, kernel)
655
656
656
657
657 if __name__ == '__main__':
658 if __name__ == '__main__':
658 main()
659 main()
General Comments 0
You need to be logged in to leave comments. Login now