##// END OF EJS Templates
Fix bug with info requests that were not json-safe....
Fernando Perez -
Show More
@@ -1,517 +1,519 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 sys
20 import sys
21 import time
21 import time
22 import traceback
22 import traceback
23
23
24 # System library imports.
24 # System library imports.
25 import zmq
25 import zmq
26
26
27 # Local imports.
27 # Local imports.
28 from IPython.config.configurable import Configurable
28 from IPython.config.configurable import Configurable
29 from IPython.utils import io
29 from IPython.utils import io
30 from IPython.utils.jsonutil import json_clean
30 from IPython.lib import pylabtools
31 from IPython.lib import pylabtools
31 from IPython.utils.traitlets import Instance, Float
32 from IPython.utils.traitlets import Instance, Float
32 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
33 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
33 start_kernel
34 start_kernel
34 from iostream import OutStream
35 from iostream import OutStream
35 from session import Session, Message
36 from session import Session, Message
36 from zmqshell import ZMQInteractiveShell
37 from zmqshell import ZMQInteractiveShell
37
38
38
39
39 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
40 # Main kernel class
41 # Main kernel class
41 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
42
43
43 class Kernel(Configurable):
44 class Kernel(Configurable):
44
45
45 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
46 # Kernel interface
47 # Kernel interface
47 #---------------------------------------------------------------------------
48 #---------------------------------------------------------------------------
48
49
49 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
50 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
50 session = Instance(Session)
51 session = Instance(Session)
51 reply_socket = Instance('zmq.Socket')
52 reply_socket = Instance('zmq.Socket')
52 pub_socket = Instance('zmq.Socket')
53 pub_socket = Instance('zmq.Socket')
53 req_socket = Instance('zmq.Socket')
54 req_socket = Instance('zmq.Socket')
54
55
55 # Private interface
56 # Private interface
56
57
57 # 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
58 # 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
59 # execute cycle, it helps prevent output synchronization problems for
60 # execute cycle, it helps prevent output synchronization problems for
60 # clients.
61 # clients.
61 # 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
62 # ~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
63 # a little if it's not enough after more interactive testing.
64 # a little if it's not enough after more interactive testing.
64 _execute_sleep = Float(0.0005, config=True)
65 _execute_sleep = Float(0.0005, config=True)
65
66
66 # Frequency of the kernel's event loop.
67 # Frequency of the kernel's event loop.
67 # 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
68 # adapt to milliseconds.
69 # adapt to milliseconds.
69 _poll_interval = Float(0.05, config=True)
70 _poll_interval = Float(0.05, config=True)
70
71
71 def __init__(self, **kwargs):
72 def __init__(self, **kwargs):
72 super(Kernel, self).__init__(**kwargs)
73 super(Kernel, self).__init__(**kwargs)
73
74
74 # Initialize the InteractiveShell subclass
75 # Initialize the InteractiveShell subclass
75 self.shell = ZMQInteractiveShell.instance()
76 self.shell = ZMQInteractiveShell.instance()
76 self.shell.displayhook.session = self.session
77 self.shell.displayhook.session = self.session
77 self.shell.displayhook.pub_socket = self.pub_socket
78 self.shell.displayhook.pub_socket = self.pub_socket
78
79
79 # TMP - hack while developing
80 # TMP - hack while developing
80 self.shell._reply_content = None
81 self.shell._reply_content = None
81
82
82 # Build dict of handlers for message types
83 # Build dict of handlers for message types
83 msg_types = [ 'execute_request', 'complete_request',
84 msg_types = [ 'execute_request', 'complete_request',
84 'object_info_request', 'history_request' ]
85 'object_info_request', 'history_request' ]
85 self.handlers = {}
86 self.handlers = {}
86 for msg_type in msg_types:
87 for msg_type in msg_types:
87 self.handlers[msg_type] = getattr(self, msg_type)
88 self.handlers[msg_type] = getattr(self, msg_type)
88
89
89 def do_one_iteration(self):
90 def do_one_iteration(self):
90 try:
91 try:
91 ident = self.reply_socket.recv(zmq.NOBLOCK)
92 ident = self.reply_socket.recv(zmq.NOBLOCK)
92 except zmq.ZMQError, e:
93 except zmq.ZMQError, e:
93 if e.errno == zmq.EAGAIN:
94 if e.errno == zmq.EAGAIN:
94 return
95 return
95 else:
96 else:
96 raise
97 raise
97 # FIXME: Bug in pyzmq/zmq?
98 # FIXME: Bug in pyzmq/zmq?
98 # assert self.reply_socket.rcvmore(), "Missing message part."
99 # assert self.reply_socket.rcvmore(), "Missing message part."
99 msg = self.reply_socket.recv_json()
100 msg = self.reply_socket.recv_json()
100
101
101 # Print some info about this message and leave a '--->' marker, so it's
102 # Print some info about this message and leave a '--->' marker, so it's
102 # easier to trace visually the message chain when debugging. Each
103 # easier to trace visually the message chain when debugging. Each
103 # handler prints its message at the end.
104 # handler prints its message at the end.
104 # Eventually we'll move these from stdout to a logger.
105 # Eventually we'll move these from stdout to a logger.
105 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
106 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
106 io.raw_print(' Content: ', msg['content'],
107 io.raw_print(' Content: ', msg['content'],
107 '\n --->\n ', sep='', end='')
108 '\n --->\n ', sep='', end='')
108
109
109 # Find and call actual handler for message
110 # Find and call actual handler for message
110 handler = self.handlers.get(msg['msg_type'], None)
111 handler = self.handlers.get(msg['msg_type'], None)
111 if handler is None:
112 if handler is None:
112 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
113 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
113 else:
114 else:
114 handler(ident, msg)
115 handler(ident, msg)
115
116
116 def start(self):
117 def start(self):
117 """ Start the kernel main loop.
118 """ Start the kernel main loop.
118 """
119 """
119 while True:
120 while True:
120 time.sleep(self._poll_interval)
121 time.sleep(self._poll_interval)
121 self.do_one_iteration()
122 self.do_one_iteration()
122
123
123 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
124 # Kernel request handlers
125 # Kernel request handlers
125 #---------------------------------------------------------------------------
126 #---------------------------------------------------------------------------
126
127
127 def _publish_pyin(self, code, parent):
128 def _publish_pyin(self, code, parent):
128 """Publish the code request on the pyin stream."""
129 """Publish the code request on the pyin stream."""
129
130
130 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
131 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
131 self.pub_socket.send_json(pyin_msg)
132 self.pub_socket.send_json(pyin_msg)
132
133
133 def execute_request(self, ident, parent):
134 def execute_request(self, ident, parent):
134 try:
135 try:
135 content = parent[u'content']
136 content = parent[u'content']
136 code = content[u'code']
137 code = content[u'code']
137 silent = content[u'silent']
138 silent = content[u'silent']
138 except:
139 except:
139 io.raw_print_err("Got bad msg: ")
140 io.raw_print_err("Got bad msg: ")
140 io.raw_print_err(Message(parent))
141 io.raw_print_err(Message(parent))
141 return
142 return
142
143
143 shell = self.shell # we'll need this a lot here
144 shell = self.shell # we'll need this a lot here
144
145
145 # Replace raw_input. Note that is not sufficient to replace
146 # Replace raw_input. Note that is not sufficient to replace
146 # raw_input in the user namespace.
147 # raw_input in the user namespace.
147 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
148 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
148 __builtin__.raw_input = raw_input
149 __builtin__.raw_input = raw_input
149
150
150 # Set the parent message of the display hook and out streams.
151 # Set the parent message of the display hook and out streams.
151 shell.displayhook.set_parent(parent)
152 shell.displayhook.set_parent(parent)
152 sys.stdout.set_parent(parent)
153 sys.stdout.set_parent(parent)
153 sys.stderr.set_parent(parent)
154 sys.stderr.set_parent(parent)
154
155
155 # Re-broadcast our input for the benefit of listening clients, and
156 # Re-broadcast our input for the benefit of listening clients, and
156 # start computing output
157 # start computing output
157 if not silent:
158 if not silent:
158 self._publish_pyin(code, parent)
159 self._publish_pyin(code, parent)
159
160
160 reply_content = {}
161 reply_content = {}
161 try:
162 try:
162 if silent:
163 if silent:
163 # runcode uses 'exec' mode, so no displayhook will fire, and it
164 # runcode uses 'exec' mode, so no displayhook will fire, and it
164 # doesn't call logging or history manipulations. Print
165 # doesn't call logging or history manipulations. Print
165 # statements in that code will obviously still execute.
166 # statements in that code will obviously still execute.
166 shell.runcode(code)
167 shell.runcode(code)
167 else:
168 else:
168 # FIXME: runlines calls the exception handler itself.
169 # FIXME: runlines calls the exception handler itself.
169 shell._reply_content = None
170 shell._reply_content = None
170 shell.runlines(code)
171 shell.runlines(code)
171 except:
172 except:
172 status = u'error'
173 status = u'error'
173 # FIXME: this code right now isn't being used yet by default,
174 # FIXME: this code right now isn't being used yet by default,
174 # because the runlines() call above directly fires off exception
175 # because the runlines() call above directly fires off exception
175 # reporting. This code, therefore, is only active in the scenario
176 # reporting. This code, therefore, is only active in the scenario
176 # where runlines itself has an unhandled exception. We need to
177 # where runlines itself has an unhandled exception. We need to
177 # uniformize this, for all exception construction to come from a
178 # uniformize this, for all exception construction to come from a
178 # single location in the codbase.
179 # single location in the codbase.
179 etype, evalue, tb = sys.exc_info()
180 etype, evalue, tb = sys.exc_info()
180 tb_list = traceback.format_exception(etype, evalue, tb)
181 tb_list = traceback.format_exception(etype, evalue, tb)
181 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
182 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
182 else:
183 else:
183 status = u'ok'
184 status = u'ok'
184 reply_content[u'payload'] = shell.payload_manager.read_payload()
185 reply_content[u'payload'] = shell.payload_manager.read_payload()
185 # Be agressive about clearing the payload because we don't want
186 # Be agressive about clearing the payload because we don't want
186 # it to sit in memory until the next execute_request comes in.
187 # it to sit in memory until the next execute_request comes in.
187 shell.payload_manager.clear_payload()
188 shell.payload_manager.clear_payload()
188
189
189 reply_content[u'status'] = status
190 reply_content[u'status'] = status
190 # Compute the execution counter so clients can display prompts
191 # Compute the execution counter so clients can display prompts
191 reply_content['execution_count'] = shell.displayhook.prompt_count
192 reply_content['execution_count'] = shell.displayhook.prompt_count
192
193
193 # FIXME - fish exception info out of shell, possibly left there by
194 # FIXME - fish exception info out of shell, possibly left there by
194 # runlines. We'll need to clean up this logic later.
195 # runlines. We'll need to clean up this logic later.
195 if shell._reply_content is not None:
196 if shell._reply_content is not None:
196 reply_content.update(shell._reply_content)
197 reply_content.update(shell._reply_content)
197
198
198 # At this point, we can tell whether the main code execution succeeded
199 # At this point, we can tell whether the main code execution succeeded
199 # or not. If it did, we proceed to evaluate user_variables/expressions
200 # or not. If it did, we proceed to evaluate user_variables/expressions
200 if reply_content['status'] == 'ok':
201 if reply_content['status'] == 'ok':
201 reply_content[u'user_variables'] = \
202 reply_content[u'user_variables'] = \
202 shell.get_user_variables(content[u'user_variables'])
203 shell.get_user_variables(content[u'user_variables'])
203 reply_content[u'user_expressions'] = \
204 reply_content[u'user_expressions'] = \
204 shell.eval_expressions(content[u'user_expressions'])
205 shell.eval_expressions(content[u'user_expressions'])
205 else:
206 else:
206 # If there was an error, don't even try to compute variables or
207 # If there was an error, don't even try to compute variables or
207 # expressions
208 # expressions
208 reply_content[u'user_variables'] = {}
209 reply_content[u'user_variables'] = {}
209 reply_content[u'user_expressions'] = {}
210 reply_content[u'user_expressions'] = {}
210
211
211 # Send the reply.
212 # Send the reply.
212 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
213 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
213 io.raw_print(reply_msg)
214 io.raw_print(reply_msg)
214
215
215 # Flush output before sending the reply.
216 # Flush output before sending the reply.
216 sys.stdout.flush()
217 sys.stdout.flush()
217 sys.stderr.flush()
218 sys.stderr.flush()
218 # FIXME: on rare occasions, the flush doesn't seem to make it to the
219 # FIXME: on rare occasions, the flush doesn't seem to make it to the
219 # clients... This seems to mitigate the problem, but we definitely need
220 # clients... This seems to mitigate the problem, but we definitely need
220 # to better understand what's going on.
221 # to better understand what's going on.
221 if self._execute_sleep:
222 if self._execute_sleep:
222 time.sleep(self._execute_sleep)
223 time.sleep(self._execute_sleep)
223
224
224 self.reply_socket.send(ident, zmq.SNDMORE)
225 self.reply_socket.send(ident, zmq.SNDMORE)
225 self.reply_socket.send_json(reply_msg)
226 self.reply_socket.send_json(reply_msg)
226 if reply_msg['content']['status'] == u'error':
227 if reply_msg['content']['status'] == u'error':
227 self._abort_queue()
228 self._abort_queue()
228
229
229 def complete_request(self, ident, parent):
230 def complete_request(self, ident, parent):
230 txt, matches = self._complete(parent)
231 txt, matches = self._complete(parent)
231 matches = {'matches' : matches,
232 matches = {'matches' : matches,
232 'matched_text' : txt,
233 'matched_text' : txt,
233 'status' : 'ok'}
234 'status' : 'ok'}
234 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
235 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
235 matches, parent, ident)
236 matches, parent, ident)
236 io.raw_print(completion_msg)
237 io.raw_print(completion_msg)
237
238
238 def object_info_request(self, ident, parent):
239 def object_info_request(self, ident, parent):
239 ##context = parent['content']['oname'].split('.')
240 ##object_info = self._object_info(context)
241 object_info = self.shell.object_inspect(parent['content']['oname'])
240 object_info = self.shell.object_inspect(parent['content']['oname'])
241 # Before we send this object over, we turn it into a dict and we scrub
242 # it for JSON usage
243 oinfo = json_clean(object_info._asdict())
242 msg = self.session.send(self.reply_socket, 'object_info_reply',
244 msg = self.session.send(self.reply_socket, 'object_info_reply',
243 object_info._asdict(), parent, ident)
245 oinfo, parent, ident)
244 io.raw_print(msg)
246 io.raw_print(msg)
245
247
246 def history_request(self, ident, parent):
248 def history_request(self, ident, parent):
247 output = parent['content']['output']
249 output = parent['content']['output']
248 index = parent['content']['index']
250 index = parent['content']['index']
249 raw = parent['content']['raw']
251 raw = parent['content']['raw']
250 hist = self.shell.get_history(index=index, raw=raw, output=output)
252 hist = self.shell.get_history(index=index, raw=raw, output=output)
251 content = {'history' : hist}
253 content = {'history' : hist}
252 msg = self.session.send(self.reply_socket, 'history_reply',
254 msg = self.session.send(self.reply_socket, 'history_reply',
253 content, parent, ident)
255 content, parent, ident)
254 io.raw_print(msg)
256 io.raw_print(msg)
255
257
256 #---------------------------------------------------------------------------
258 #---------------------------------------------------------------------------
257 # Protected interface
259 # Protected interface
258 #---------------------------------------------------------------------------
260 #---------------------------------------------------------------------------
259
261
260 def _abort_queue(self):
262 def _abort_queue(self):
261 while True:
263 while True:
262 try:
264 try:
263 ident = self.reply_socket.recv(zmq.NOBLOCK)
265 ident = self.reply_socket.recv(zmq.NOBLOCK)
264 except zmq.ZMQError, e:
266 except zmq.ZMQError, e:
265 if e.errno == zmq.EAGAIN:
267 if e.errno == zmq.EAGAIN:
266 break
268 break
267 else:
269 else:
268 assert self.reply_socket.rcvmore(), \
270 assert self.reply_socket.rcvmore(), \
269 "Unexpected missing message part."
271 "Unexpected missing message part."
270 msg = self.reply_socket.recv_json()
272 msg = self.reply_socket.recv_json()
271 io.raw_print("Aborting:\n", Message(msg))
273 io.raw_print("Aborting:\n", Message(msg))
272 msg_type = msg['msg_type']
274 msg_type = msg['msg_type']
273 reply_type = msg_type.split('_')[0] + '_reply'
275 reply_type = msg_type.split('_')[0] + '_reply'
274 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
276 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
275 io.raw_print(reply_msg)
277 io.raw_print(reply_msg)
276 self.reply_socket.send(ident,zmq.SNDMORE)
278 self.reply_socket.send(ident,zmq.SNDMORE)
277 self.reply_socket.send_json(reply_msg)
279 self.reply_socket.send_json(reply_msg)
278 # We need to wait a bit for requests to come in. This can probably
280 # We need to wait a bit for requests to come in. This can probably
279 # be set shorter for true asynchronous clients.
281 # be set shorter for true asynchronous clients.
280 time.sleep(0.1)
282 time.sleep(0.1)
281
283
282 def _raw_input(self, prompt, ident, parent):
284 def _raw_input(self, prompt, ident, parent):
283 # Flush output before making the request.
285 # Flush output before making the request.
284 sys.stderr.flush()
286 sys.stderr.flush()
285 sys.stdout.flush()
287 sys.stdout.flush()
286
288
287 # Send the input request.
289 # Send the input request.
288 content = dict(prompt=prompt)
290 content = dict(prompt=prompt)
289 msg = self.session.msg(u'input_request', content, parent)
291 msg = self.session.msg(u'input_request', content, parent)
290 self.req_socket.send_json(msg)
292 self.req_socket.send_json(msg)
291
293
292 # Await a response.
294 # Await a response.
293 reply = self.req_socket.recv_json()
295 reply = self.req_socket.recv_json()
294 try:
296 try:
295 value = reply['content']['value']
297 value = reply['content']['value']
296 except:
298 except:
297 io.raw_print_err("Got bad raw_input reply: ")
299 io.raw_print_err("Got bad raw_input reply: ")
298 io.raw_print_err(Message(parent))
300 io.raw_print_err(Message(parent))
299 value = ''
301 value = ''
300 return value
302 return value
301
303
302 def _complete(self, msg):
304 def _complete(self, msg):
303 c = msg['content']
305 c = msg['content']
304 try:
306 try:
305 cpos = int(c['cursor_pos'])
307 cpos = int(c['cursor_pos'])
306 except:
308 except:
307 # If we don't get something that we can convert to an integer, at
309 # If we don't get something that we can convert to an integer, at
308 # least attempt the completion guessing the cursor is at the end of
310 # least attempt the completion guessing the cursor is at the end of
309 # the text, if there's any, and otherwise of the line
311 # the text, if there's any, and otherwise of the line
310 cpos = len(c['text'])
312 cpos = len(c['text'])
311 if cpos==0:
313 if cpos==0:
312 cpos = len(c['line'])
314 cpos = len(c['line'])
313 return self.shell.complete(c['text'], c['line'], cpos)
315 return self.shell.complete(c['text'], c['line'], cpos)
314
316
315 def _object_info(self, context):
317 def _object_info(self, context):
316 symbol, leftover = self._symbol_from_context(context)
318 symbol, leftover = self._symbol_from_context(context)
317 if symbol is not None and not leftover:
319 if symbol is not None and not leftover:
318 doc = getattr(symbol, '__doc__', '')
320 doc = getattr(symbol, '__doc__', '')
319 else:
321 else:
320 doc = ''
322 doc = ''
321 object_info = dict(docstring = doc)
323 object_info = dict(docstring = doc)
322 return object_info
324 return object_info
323
325
324 def _symbol_from_context(self, context):
326 def _symbol_from_context(self, context):
325 if not context:
327 if not context:
326 return None, context
328 return None, context
327
329
328 base_symbol_string = context[0]
330 base_symbol_string = context[0]
329 symbol = self.shell.user_ns.get(base_symbol_string, None)
331 symbol = self.shell.user_ns.get(base_symbol_string, None)
330 if symbol is None:
332 if symbol is None:
331 symbol = __builtin__.__dict__.get(base_symbol_string, None)
333 symbol = __builtin__.__dict__.get(base_symbol_string, None)
332 if symbol is None:
334 if symbol is None:
333 return None, context
335 return None, context
334
336
335 context = context[1:]
337 context = context[1:]
336 for i, name in enumerate(context):
338 for i, name in enumerate(context):
337 new_symbol = getattr(symbol, name, None)
339 new_symbol = getattr(symbol, name, None)
338 if new_symbol is None:
340 if new_symbol is None:
339 return symbol, context[i:]
341 return symbol, context[i:]
340 else:
342 else:
341 symbol = new_symbol
343 symbol = new_symbol
342
344
343 return symbol, []
345 return symbol, []
344
346
345
347
346 class QtKernel(Kernel):
348 class QtKernel(Kernel):
347 """A Kernel subclass with Qt support."""
349 """A Kernel subclass with Qt support."""
348
350
349 def start(self):
351 def start(self):
350 """Start a kernel with QtPy4 event loop integration."""
352 """Start a kernel with QtPy4 event loop integration."""
351
353
352 from PyQt4 import QtGui, QtCore
354 from PyQt4 import QtGui, QtCore
353 from IPython.lib.guisupport import (
355 from IPython.lib.guisupport import (
354 get_app_qt4, start_event_loop_qt4
356 get_app_qt4, start_event_loop_qt4
355 )
357 )
356 self.app = get_app_qt4([" "])
358 self.app = get_app_qt4([" "])
357 self.app.setQuitOnLastWindowClosed(False)
359 self.app.setQuitOnLastWindowClosed(False)
358 self.timer = QtCore.QTimer()
360 self.timer = QtCore.QTimer()
359 self.timer.timeout.connect(self.do_one_iteration)
361 self.timer.timeout.connect(self.do_one_iteration)
360 # Units for the timer are in milliseconds
362 # Units for the timer are in milliseconds
361 self.timer.start(1000*self._poll_interval)
363 self.timer.start(1000*self._poll_interval)
362 start_event_loop_qt4(self.app)
364 start_event_loop_qt4(self.app)
363
365
364
366
365 class WxKernel(Kernel):
367 class WxKernel(Kernel):
366 """A Kernel subclass with Wx support."""
368 """A Kernel subclass with Wx support."""
367
369
368 def start(self):
370 def start(self):
369 """Start a kernel with wx event loop support."""
371 """Start a kernel with wx event loop support."""
370
372
371 import wx
373 import wx
372 from IPython.lib.guisupport import start_event_loop_wx
374 from IPython.lib.guisupport import start_event_loop_wx
373 doi = self.do_one_iteration
375 doi = self.do_one_iteration
374 _poll_interval = self._poll_interval
376 _poll_interval = self._poll_interval
375
377
376 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
378 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
377 # We make the Frame hidden when we create it in the main app below.
379 # We make the Frame hidden when we create it in the main app below.
378 class TimerFrame(wx.Frame):
380 class TimerFrame(wx.Frame):
379 def __init__(self, func):
381 def __init__(self, func):
380 wx.Frame.__init__(self, None, -1)
382 wx.Frame.__init__(self, None, -1)
381 self.timer = wx.Timer(self)
383 self.timer = wx.Timer(self)
382 # Units for the timer are in milliseconds
384 # Units for the timer are in milliseconds
383 self.timer.Start(1000*_poll_interval)
385 self.timer.Start(1000*_poll_interval)
384 self.Bind(wx.EVT_TIMER, self.on_timer)
386 self.Bind(wx.EVT_TIMER, self.on_timer)
385 self.func = func
387 self.func = func
386 def on_timer(self, event):
388 def on_timer(self, event):
387 self.func()
389 self.func()
388
390
389 # We need a custom wx.App to create our Frame subclass that has the
391 # We need a custom wx.App to create our Frame subclass that has the
390 # wx.Timer to drive the ZMQ event loop.
392 # wx.Timer to drive the ZMQ event loop.
391 class IPWxApp(wx.App):
393 class IPWxApp(wx.App):
392 def OnInit(self):
394 def OnInit(self):
393 self.frame = TimerFrame(doi)
395 self.frame = TimerFrame(doi)
394 self.frame.Show(False)
396 self.frame.Show(False)
395 return True
397 return True
396
398
397 # The redirect=False here makes sure that wx doesn't replace
399 # The redirect=False here makes sure that wx doesn't replace
398 # sys.stdout/stderr with its own classes.
400 # sys.stdout/stderr with its own classes.
399 self.app = IPWxApp(redirect=False)
401 self.app = IPWxApp(redirect=False)
400 start_event_loop_wx(self.app)
402 start_event_loop_wx(self.app)
401
403
402
404
403 class TkKernel(Kernel):
405 class TkKernel(Kernel):
404 """A Kernel subclass with Tk support."""
406 """A Kernel subclass with Tk support."""
405
407
406 def start(self):
408 def start(self):
407 """Start a Tk enabled event loop."""
409 """Start a Tk enabled event loop."""
408
410
409 import Tkinter
411 import Tkinter
410 doi = self.do_one_iteration
412 doi = self.do_one_iteration
411
413
412 # For Tkinter, we create a Tk object and call its withdraw method.
414 # For Tkinter, we create a Tk object and call its withdraw method.
413 class Timer(object):
415 class Timer(object):
414 def __init__(self, func):
416 def __init__(self, func):
415 self.app = Tkinter.Tk()
417 self.app = Tkinter.Tk()
416 self.app.withdraw()
418 self.app.withdraw()
417 self.func = func
419 self.func = func
418 def on_timer(self):
420 def on_timer(self):
419 self.func()
421 self.func()
420 # Units for the timer are in milliseconds
422 # Units for the timer are in milliseconds
421 self.app.after(1000*self._poll_interval, self.on_timer)
423 self.app.after(1000*self._poll_interval, self.on_timer)
422 def start(self):
424 def start(self):
423 self.on_timer() # Call it once to get things going.
425 self.on_timer() # Call it once to get things going.
424 self.app.mainloop()
426 self.app.mainloop()
425
427
426 self.timer = Timer(doi)
428 self.timer = Timer(doi)
427 self.timer.start()
429 self.timer.start()
428
430
429 #-----------------------------------------------------------------------------
431 #-----------------------------------------------------------------------------
430 # Kernel main and launch functions
432 # Kernel main and launch functions
431 #-----------------------------------------------------------------------------
433 #-----------------------------------------------------------------------------
432
434
433 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
435 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
434 independent=False, pylab=False):
436 independent=False, pylab=False):
435 """ Launches a localhost kernel, binding to the specified ports.
437 """ Launches a localhost kernel, binding to the specified ports.
436
438
437 Parameters
439 Parameters
438 ----------
440 ----------
439 xrep_port : int, optional
441 xrep_port : int, optional
440 The port to use for XREP channel.
442 The port to use for XREP channel.
441
443
442 pub_port : int, optional
444 pub_port : int, optional
443 The port to use for the SUB channel.
445 The port to use for the SUB channel.
444
446
445 req_port : int, optional
447 req_port : int, optional
446 The port to use for the REQ (raw input) channel.
448 The port to use for the REQ (raw input) channel.
447
449
448 hb_port : int, optional
450 hb_port : int, optional
449 The port to use for the hearbeat REP channel.
451 The port to use for the hearbeat REP channel.
450
452
451 independent : bool, optional (default False)
453 independent : bool, optional (default False)
452 If set, the kernel process is guaranteed to survive if this process
454 If set, the kernel process is guaranteed to survive if this process
453 dies. If not set, an effort is made to ensure that the kernel is killed
455 dies. If not set, an effort is made to ensure that the kernel is killed
454 when this process dies. Note that in this case it is still good practice
456 when this process dies. Note that in this case it is still good practice
455 to kill kernels manually before exiting.
457 to kill kernels manually before exiting.
456
458
457 pylab : bool or string, optional (default False)
459 pylab : bool or string, optional (default False)
458 If not False, the kernel will be launched with pylab enabled. If a
460 If not False, the kernel will be launched with pylab enabled. If a
459 string is passed, matplotlib will use the specified backend. Otherwise,
461 string is passed, matplotlib will use the specified backend. Otherwise,
460 matplotlib's default backend will be used.
462 matplotlib's default backend will be used.
461
463
462 Returns
464 Returns
463 -------
465 -------
464 A tuple of form:
466 A tuple of form:
465 (kernel_process, xrep_port, pub_port, req_port)
467 (kernel_process, xrep_port, pub_port, req_port)
466 where kernel_process is a Popen object and the ports are integers.
468 where kernel_process is a Popen object and the ports are integers.
467 """
469 """
468 extra_arguments = []
470 extra_arguments = []
469 if pylab:
471 if pylab:
470 extra_arguments.append('--pylab')
472 extra_arguments.append('--pylab')
471 if isinstance(pylab, basestring):
473 if isinstance(pylab, basestring):
472 extra_arguments.append(pylab)
474 extra_arguments.append(pylab)
473 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
475 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
474 xrep_port, pub_port, req_port, hb_port,
476 xrep_port, pub_port, req_port, hb_port,
475 independent, extra_arguments)
477 independent, extra_arguments)
476
478
477
479
478 def main():
480 def main():
479 """ The IPython kernel main entry point.
481 """ The IPython kernel main entry point.
480 """
482 """
481 parser = make_argument_parser()
483 parser = make_argument_parser()
482 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
484 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
483 const='auto', help = \
485 const='auto', help = \
484 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
486 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
485 given, the GUI backend is matplotlib's, otherwise use one of: \
487 given, the GUI backend is matplotlib's, otherwise use one of: \
486 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
488 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
487 namespace = parser.parse_args()
489 namespace = parser.parse_args()
488
490
489 kernel_class = Kernel
491 kernel_class = Kernel
490
492
491 _kernel_classes = {
493 _kernel_classes = {
492 'qt' : QtKernel,
494 'qt' : QtKernel,
493 'qt4' : QtKernel,
495 'qt4' : QtKernel,
494 'payload-svg': Kernel,
496 'payload-svg': Kernel,
495 'wx' : WxKernel,
497 'wx' : WxKernel,
496 'tk' : TkKernel
498 'tk' : TkKernel
497 }
499 }
498 if namespace.pylab:
500 if namespace.pylab:
499 if namespace.pylab == 'auto':
501 if namespace.pylab == 'auto':
500 gui, backend = pylabtools.find_gui_and_backend()
502 gui, backend = pylabtools.find_gui_and_backend()
501 else:
503 else:
502 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
504 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
503 kernel_class = _kernel_classes.get(gui)
505 kernel_class = _kernel_classes.get(gui)
504 if kernel_class is None:
506 if kernel_class is None:
505 raise ValueError('GUI is not supported: %r' % gui)
507 raise ValueError('GUI is not supported: %r' % gui)
506 pylabtools.activate_matplotlib(backend)
508 pylabtools.activate_matplotlib(backend)
507
509
508 kernel = make_kernel(namespace, kernel_class, OutStream)
510 kernel = make_kernel(namespace, kernel_class, OutStream)
509
511
510 if namespace.pylab:
512 if namespace.pylab:
511 pylabtools.import_pylab(kernel.shell.user_ns)
513 pylabtools.import_pylab(kernel.shell.user_ns)
512
514
513 start_kernel(namespace, kernel)
515 start_kernel(namespace, kernel)
514
516
515
517
516 if __name__ == '__main__':
518 if __name__ == '__main__':
517 main()
519 main()
General Comments 0
You need to be logged in to leave comments. Login now