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