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