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