##// END OF EJS Templates
kernel sends reply on the right side of std<x>.flush
MinRK -
Show More
@@ -1,641 +1,641 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_request',
110 'object_info_request', 'history_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 # Send the reply.
272 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
273 reply_content, parent, ident=ident)
274 logger.debug(str(reply_msg))
275
276 # Flush output before sending the reply.
271 # Flush output before sending the reply.
277 sys.stdout.flush()
272 sys.stdout.flush()
278 sys.stderr.flush()
273 sys.stderr.flush()
279 # 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
280 # clients... This seems to mitigate the problem, but we definitely need
275 # clients... This seems to mitigate the problem, but we definitely need
281 # to better understand what's going on.
276 # to better understand what's going on.
282 if self._execute_sleep:
277 if self._execute_sleep:
283 time.sleep(self._execute_sleep)
278 time.sleep(self._execute_sleep)
284
279
280 # Send the reply.
281 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
282 reply_content, parent, ident=ident)
283 logger.debug(str(reply_msg))
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_request(self, ident, parent):
311 def history_request(self, ident, parent):
312 output = parent['content']['output']
312 output = parent['content']['output']
313 index = parent['content']['index']
313 index = parent['content']['index']
314 raw = parent['content']['raw']
314 raw = parent['content']['raw']
315 hist = self.shell.get_history(index=index, raw=raw, output=output)
315 hist = self.shell.get_history(index=index, raw=raw, output=output)
316 content = {'history' : hist}
316 content = {'history' : hist}
317 msg = self.session.send(self.reply_socket, 'history_reply',
317 msg = self.session.send(self.reply_socket, 'history_reply',
318 content, parent, ident)
318 content, parent, ident)
319 logger.debug(str(msg))
319 logger.debug(str(msg))
320
320
321 def connect_request(self, ident, parent):
321 def connect_request(self, ident, parent):
322 if self._recorded_ports is not None:
322 if self._recorded_ports is not None:
323 content = self._recorded_ports.copy()
323 content = self._recorded_ports.copy()
324 else:
324 else:
325 content = {}
325 content = {}
326 msg = self.session.send(self.reply_socket, 'connect_reply',
326 msg = self.session.send(self.reply_socket, 'connect_reply',
327 content, parent, ident)
327 content, parent, ident)
328 logger.debug(msg)
328 logger.debug(msg)
329
329
330 def shutdown_request(self, ident, parent):
330 def shutdown_request(self, ident, parent):
331 self.shell.exit_now = True
331 self.shell.exit_now = True
332 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
332 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
333 sys.exit(0)
333 sys.exit(0)
334
334
335 #---------------------------------------------------------------------------
335 #---------------------------------------------------------------------------
336 # Protected interface
336 # Protected interface
337 #---------------------------------------------------------------------------
337 #---------------------------------------------------------------------------
338
338
339 def _abort_queue(self):
339 def _abort_queue(self):
340 while True:
340 while True:
341 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
341 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
342 if msg is None:
342 if msg is None:
343 break
343 break
344 else:
344 else:
345 assert ident is not None, \
345 assert ident is not None, \
346 "Unexpected missing message part."
346 "Unexpected missing message part."
347
347
348 logger.debug("Aborting:\n"+str(Message(msg)))
348 logger.debug("Aborting:\n"+str(Message(msg)))
349 msg_type = msg['msg_type']
349 msg_type = msg['msg_type']
350 reply_type = msg_type.split('_')[0] + '_reply'
350 reply_type = msg_type.split('_')[0] + '_reply'
351 reply_msg = self.session.send(self.reply_socket, reply_type,
351 reply_msg = self.session.send(self.reply_socket, reply_type,
352 {'status' : 'aborted'}, msg, ident=ident)
352 {'status' : 'aborted'}, msg, ident=ident)
353 logger.debug(reply_msg)
353 logger.debug(reply_msg)
354 # We need to wait a bit for requests to come in. This can probably
354 # We need to wait a bit for requests to come in. This can probably
355 # be set shorter for true asynchronous clients.
355 # be set shorter for true asynchronous clients.
356 time.sleep(0.1)
356 time.sleep(0.1)
357
357
358 def _raw_input(self, prompt, ident, parent):
358 def _raw_input(self, prompt, ident, parent):
359 # Flush output before making the request.
359 # Flush output before making the request.
360 sys.stderr.flush()
360 sys.stderr.flush()
361 sys.stdout.flush()
361 sys.stdout.flush()
362
362
363 # Send the input request.
363 # Send the input request.
364 content = dict(prompt=prompt)
364 content = dict(prompt=prompt)
365 msg = self.session.send(self.req_socket, u'input_request', content, parent)
365 msg = self.session.send(self.req_socket, u'input_request', content, parent)
366
366
367 # Await a response.
367 # Await a response.
368 ident, reply = self.session.recv(self.req_socket, 0)
368 ident, reply = self.session.recv(self.req_socket, 0)
369 try:
369 try:
370 value = reply['content']['value']
370 value = reply['content']['value']
371 except:
371 except:
372 logger.error("Got bad raw_input reply: ")
372 logger.error("Got bad raw_input reply: ")
373 logger.error(str(Message(parent)))
373 logger.error(str(Message(parent)))
374 value = ''
374 value = ''
375 return value
375 return value
376
376
377 def _complete(self, msg):
377 def _complete(self, msg):
378 c = msg['content']
378 c = msg['content']
379 try:
379 try:
380 cpos = int(c['cursor_pos'])
380 cpos = int(c['cursor_pos'])
381 except:
381 except:
382 # If we don't get something that we can convert to an integer, at
382 # If we don't get something that we can convert to an integer, at
383 # least attempt the completion guessing the cursor is at the end of
383 # least attempt the completion guessing the cursor is at the end of
384 # the text, if there's any, and otherwise of the line
384 # the text, if there's any, and otherwise of the line
385 cpos = len(c['text'])
385 cpos = len(c['text'])
386 if cpos==0:
386 if cpos==0:
387 cpos = len(c['line'])
387 cpos = len(c['line'])
388 return self.shell.complete(c['text'], c['line'], cpos)
388 return self.shell.complete(c['text'], c['line'], cpos)
389
389
390 def _object_info(self, context):
390 def _object_info(self, context):
391 symbol, leftover = self._symbol_from_context(context)
391 symbol, leftover = self._symbol_from_context(context)
392 if symbol is not None and not leftover:
392 if symbol is not None and not leftover:
393 doc = getattr(symbol, '__doc__', '')
393 doc = getattr(symbol, '__doc__', '')
394 else:
394 else:
395 doc = ''
395 doc = ''
396 object_info = dict(docstring = doc)
396 object_info = dict(docstring = doc)
397 return object_info
397 return object_info
398
398
399 def _symbol_from_context(self, context):
399 def _symbol_from_context(self, context):
400 if not context:
400 if not context:
401 return None, context
401 return None, context
402
402
403 base_symbol_string = context[0]
403 base_symbol_string = context[0]
404 symbol = self.shell.user_ns.get(base_symbol_string, None)
404 symbol = self.shell.user_ns.get(base_symbol_string, None)
405 if symbol is None:
405 if symbol is None:
406 symbol = __builtin__.__dict__.get(base_symbol_string, None)
406 symbol = __builtin__.__dict__.get(base_symbol_string, None)
407 if symbol is None:
407 if symbol is None:
408 return None, context
408 return None, context
409
409
410 context = context[1:]
410 context = context[1:]
411 for i, name in enumerate(context):
411 for i, name in enumerate(context):
412 new_symbol = getattr(symbol, name, None)
412 new_symbol = getattr(symbol, name, None)
413 if new_symbol is None:
413 if new_symbol is None:
414 return symbol, context[i:]
414 return symbol, context[i:]
415 else:
415 else:
416 symbol = new_symbol
416 symbol = new_symbol
417
417
418 return symbol, []
418 return symbol, []
419
419
420 def _at_shutdown(self):
420 def _at_shutdown(self):
421 """Actions taken at shutdown by the kernel, called by python's atexit.
421 """Actions taken at shutdown by the kernel, called by python's atexit.
422 """
422 """
423 # io.rprint("Kernel at_shutdown") # dbg
423 # io.rprint("Kernel at_shutdown") # dbg
424 if self._shutdown_message is not None:
424 if self._shutdown_message is not None:
425 self.session.send(self.reply_socket, self._shutdown_message)
425 self.session.send(self.reply_socket, self._shutdown_message)
426 self.session.send(self.pub_socket, self._shutdown_message)
426 self.session.send(self.pub_socket, self._shutdown_message)
427 logger.debug(str(self._shutdown_message))
427 logger.debug(str(self._shutdown_message))
428 # A very short sleep to give zmq time to flush its message buffers
428 # A very short sleep to give zmq time to flush its message buffers
429 # before Python truly shuts down.
429 # before Python truly shuts down.
430 time.sleep(0.01)
430 time.sleep(0.01)
431
431
432
432
433 class QtKernel(Kernel):
433 class QtKernel(Kernel):
434 """A Kernel subclass with Qt support."""
434 """A Kernel subclass with Qt support."""
435
435
436 def start(self):
436 def start(self):
437 """Start a kernel with QtPy4 event loop integration."""
437 """Start a kernel with QtPy4 event loop integration."""
438
438
439 from PyQt4 import QtCore
439 from PyQt4 import QtCore
440 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
440 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
441
441
442 self.app = get_app_qt4([" "])
442 self.app = get_app_qt4([" "])
443 self.app.setQuitOnLastWindowClosed(False)
443 self.app.setQuitOnLastWindowClosed(False)
444 self.timer = QtCore.QTimer()
444 self.timer = QtCore.QTimer()
445 self.timer.timeout.connect(self.do_one_iteration)
445 self.timer.timeout.connect(self.do_one_iteration)
446 # Units for the timer are in milliseconds
446 # Units for the timer are in milliseconds
447 self.timer.start(1000*self._poll_interval)
447 self.timer.start(1000*self._poll_interval)
448 start_event_loop_qt4(self.app)
448 start_event_loop_qt4(self.app)
449
449
450
450
451 class WxKernel(Kernel):
451 class WxKernel(Kernel):
452 """A Kernel subclass with Wx support."""
452 """A Kernel subclass with Wx support."""
453
453
454 def start(self):
454 def start(self):
455 """Start a kernel with wx event loop support."""
455 """Start a kernel with wx event loop support."""
456
456
457 import wx
457 import wx
458 from IPython.lib.guisupport import start_event_loop_wx
458 from IPython.lib.guisupport import start_event_loop_wx
459
459
460 doi = self.do_one_iteration
460 doi = self.do_one_iteration
461 # Wx uses milliseconds
461 # Wx uses milliseconds
462 poll_interval = int(1000*self._poll_interval)
462 poll_interval = int(1000*self._poll_interval)
463
463
464 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
464 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
465 # We make the Frame hidden when we create it in the main app below.
465 # We make the Frame hidden when we create it in the main app below.
466 class TimerFrame(wx.Frame):
466 class TimerFrame(wx.Frame):
467 def __init__(self, func):
467 def __init__(self, func):
468 wx.Frame.__init__(self, None, -1)
468 wx.Frame.__init__(self, None, -1)
469 self.timer = wx.Timer(self)
469 self.timer = wx.Timer(self)
470 # Units for the timer are in milliseconds
470 # Units for the timer are in milliseconds
471 self.timer.Start(poll_interval)
471 self.timer.Start(poll_interval)
472 self.Bind(wx.EVT_TIMER, self.on_timer)
472 self.Bind(wx.EVT_TIMER, self.on_timer)
473 self.func = func
473 self.func = func
474
474
475 def on_timer(self, event):
475 def on_timer(self, event):
476 self.func()
476 self.func()
477
477
478 # We need a custom wx.App to create our Frame subclass that has the
478 # We need a custom wx.App to create our Frame subclass that has the
479 # wx.Timer to drive the ZMQ event loop.
479 # wx.Timer to drive the ZMQ event loop.
480 class IPWxApp(wx.App):
480 class IPWxApp(wx.App):
481 def OnInit(self):
481 def OnInit(self):
482 self.frame = TimerFrame(doi)
482 self.frame = TimerFrame(doi)
483 self.frame.Show(False)
483 self.frame.Show(False)
484 return True
484 return True
485
485
486 # The redirect=False here makes sure that wx doesn't replace
486 # The redirect=False here makes sure that wx doesn't replace
487 # sys.stdout/stderr with its own classes.
487 # sys.stdout/stderr with its own classes.
488 self.app = IPWxApp(redirect=False)
488 self.app = IPWxApp(redirect=False)
489 start_event_loop_wx(self.app)
489 start_event_loop_wx(self.app)
490
490
491
491
492 class TkKernel(Kernel):
492 class TkKernel(Kernel):
493 """A Kernel subclass with Tk support."""
493 """A Kernel subclass with Tk support."""
494
494
495 def start(self):
495 def start(self):
496 """Start a Tk enabled event loop."""
496 """Start a Tk enabled event loop."""
497
497
498 import Tkinter
498 import Tkinter
499 doi = self.do_one_iteration
499 doi = self.do_one_iteration
500 # Tk uses milliseconds
500 # Tk uses milliseconds
501 poll_interval = int(1000*self._poll_interval)
501 poll_interval = int(1000*self._poll_interval)
502 # For Tkinter, we create a Tk object and call its withdraw method.
502 # For Tkinter, we create a Tk object and call its withdraw method.
503 class Timer(object):
503 class Timer(object):
504 def __init__(self, func):
504 def __init__(self, func):
505 self.app = Tkinter.Tk()
505 self.app = Tkinter.Tk()
506 self.app.withdraw()
506 self.app.withdraw()
507 self.func = func
507 self.func = func
508
508
509 def on_timer(self):
509 def on_timer(self):
510 self.func()
510 self.func()
511 self.app.after(poll_interval, self.on_timer)
511 self.app.after(poll_interval, self.on_timer)
512
512
513 def start(self):
513 def start(self):
514 self.on_timer() # Call it once to get things going.
514 self.on_timer() # Call it once to get things going.
515 self.app.mainloop()
515 self.app.mainloop()
516
516
517 self.timer = Timer(doi)
517 self.timer = Timer(doi)
518 self.timer.start()
518 self.timer.start()
519
519
520
520
521 class GTKKernel(Kernel):
521 class GTKKernel(Kernel):
522 """A Kernel subclass with GTK support."""
522 """A Kernel subclass with GTK support."""
523
523
524 def start(self):
524 def start(self):
525 """Start the kernel, coordinating with the GTK event loop"""
525 """Start the kernel, coordinating with the GTK event loop"""
526 from .gui.gtkembed import GTKEmbed
526 from .gui.gtkembed import GTKEmbed
527
527
528 gtk_kernel = GTKEmbed(self)
528 gtk_kernel = GTKEmbed(self)
529 gtk_kernel.start()
529 gtk_kernel.start()
530
530
531
531
532 #-----------------------------------------------------------------------------
532 #-----------------------------------------------------------------------------
533 # Kernel main and launch functions
533 # Kernel main and launch functions
534 #-----------------------------------------------------------------------------
534 #-----------------------------------------------------------------------------
535
535
536 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
536 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
537 independent=False, pylab=False, colors=None):
537 independent=False, pylab=False, colors=None):
538 """Launches a localhost kernel, binding to the specified ports.
538 """Launches a localhost kernel, binding to the specified ports.
539
539
540 Parameters
540 Parameters
541 ----------
541 ----------
542 ip : str, optional
542 ip : str, optional
543 The ip address the kernel will bind to.
543 The ip address the kernel will bind to.
544
544
545 xrep_port : int, optional
545 xrep_port : int, optional
546 The port to use for XREP channel.
546 The port to use for XREP channel.
547
547
548 pub_port : int, optional
548 pub_port : int, optional
549 The port to use for the SUB channel.
549 The port to use for the SUB channel.
550
550
551 req_port : int, optional
551 req_port : int, optional
552 The port to use for the REQ (raw input) channel.
552 The port to use for the REQ (raw input) channel.
553
553
554 hb_port : int, optional
554 hb_port : int, optional
555 The port to use for the hearbeat REP channel.
555 The port to use for the hearbeat REP channel.
556
556
557 independent : bool, optional (default False)
557 independent : bool, optional (default False)
558 If set, the kernel process is guaranteed to survive if this process
558 If set, the kernel process is guaranteed to survive if this process
559 dies. If not set, an effort is made to ensure that the kernel is killed
559 dies. If not set, an effort is made to ensure that the kernel is killed
560 when this process dies. Note that in this case it is still good practice
560 when this process dies. Note that in this case it is still good practice
561 to kill kernels manually before exiting.
561 to kill kernels manually before exiting.
562
562
563 pylab : bool or string, optional (default False)
563 pylab : bool or string, optional (default False)
564 If not False, the kernel will be launched with pylab enabled. If a
564 If not False, the kernel will be launched with pylab enabled. If a
565 string is passed, matplotlib will use the specified backend. Otherwise,
565 string is passed, matplotlib will use the specified backend. Otherwise,
566 matplotlib's default backend will be used.
566 matplotlib's default backend will be used.
567
567
568 colors : None or string, optional (default None)
568 colors : None or string, optional (default None)
569 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
569 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
570
570
571 Returns
571 Returns
572 -------
572 -------
573 A tuple of form:
573 A tuple of form:
574 (kernel_process, xrep_port, pub_port, req_port)
574 (kernel_process, xrep_port, pub_port, req_port)
575 where kernel_process is a Popen object and the ports are integers.
575 where kernel_process is a Popen object and the ports are integers.
576 """
576 """
577 extra_arguments = []
577 extra_arguments = []
578 if pylab:
578 if pylab:
579 extra_arguments.append('--pylab')
579 extra_arguments.append('--pylab')
580 if isinstance(pylab, basestring):
580 if isinstance(pylab, basestring):
581 extra_arguments.append(pylab)
581 extra_arguments.append(pylab)
582 if ip is not None:
582 if ip is not None:
583 extra_arguments.append('--ip')
583 extra_arguments.append('--ip')
584 if isinstance(ip, basestring):
584 if isinstance(ip, basestring):
585 extra_arguments.append(ip)
585 extra_arguments.append(ip)
586 if colors is not None:
586 if colors is not None:
587 extra_arguments.append('--colors')
587 extra_arguments.append('--colors')
588 extra_arguments.append(colors)
588 extra_arguments.append(colors)
589 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
589 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
590 xrep_port, pub_port, req_port, hb_port,
590 xrep_port, pub_port, req_port, hb_port,
591 independent, extra_arguments)
591 independent, extra_arguments)
592
592
593
593
594 def main():
594 def main():
595 """ The IPython kernel main entry point.
595 """ The IPython kernel main entry point.
596 """
596 """
597 parser = make_argument_parser()
597 parser = make_argument_parser()
598 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
598 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
599 const='auto', help = \
599 const='auto', help = \
600 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
600 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
601 given, the GUI backend is matplotlib's, otherwise use one of: \
601 given, the GUI backend is matplotlib's, otherwise use one of: \
602 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
602 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
603 parser.add_argument('--colors',
603 parser.add_argument('--colors',
604 type=str, dest='colors',
604 type=str, dest='colors',
605 help="Set the color scheme (NoColor, Linux, and LightBG).",
605 help="Set the color scheme (NoColor, Linux, and LightBG).",
606 metavar='ZMQInteractiveShell.colors')
606 metavar='ZMQInteractiveShell.colors')
607 namespace = parser.parse_args()
607 namespace = parser.parse_args()
608
608
609 kernel_class = Kernel
609 kernel_class = Kernel
610
610
611 kernel_classes = {
611 kernel_classes = {
612 'qt' : QtKernel,
612 'qt' : QtKernel,
613 'qt4': QtKernel,
613 'qt4': QtKernel,
614 'inline': Kernel,
614 'inline': Kernel,
615 'wx' : WxKernel,
615 'wx' : WxKernel,
616 'tk' : TkKernel,
616 'tk' : TkKernel,
617 'gtk': GTKKernel,
617 'gtk': GTKKernel,
618 }
618 }
619 if namespace.pylab:
619 if namespace.pylab:
620 if namespace.pylab == 'auto':
620 if namespace.pylab == 'auto':
621 gui, backend = pylabtools.find_gui_and_backend()
621 gui, backend = pylabtools.find_gui_and_backend()
622 else:
622 else:
623 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
623 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
624 kernel_class = kernel_classes.get(gui)
624 kernel_class = kernel_classes.get(gui)
625 if kernel_class is None:
625 if kernel_class is None:
626 raise ValueError('GUI is not supported: %r' % gui)
626 raise ValueError('GUI is not supported: %r' % gui)
627 pylabtools.activate_matplotlib(backend)
627 pylabtools.activate_matplotlib(backend)
628 if namespace.colors:
628 if namespace.colors:
629 ZMQInteractiveShell.colors=namespace.colors
629 ZMQInteractiveShell.colors=namespace.colors
630
630
631 kernel = make_kernel(namespace, kernel_class, OutStream)
631 kernel = make_kernel(namespace, kernel_class, OutStream)
632
632
633 if namespace.pylab:
633 if namespace.pylab:
634 pylabtools.import_pylab(kernel.shell.user_ns, backend,
634 pylabtools.import_pylab(kernel.shell.user_ns, backend,
635 shell=kernel.shell)
635 shell=kernel.shell)
636
636
637 start_kernel(namespace, kernel)
637 start_kernel(namespace, kernel)
638
638
639
639
640 if __name__ == '__main__':
640 if __name__ == '__main__':
641 main()
641 main()
General Comments 0
You need to be logged in to leave comments. Login now