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