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