##// END OF EJS Templates
* Adding object_info_request support to prototype kernel....
epatters -
Show More
@@ -1,330 +1,316 b''
1 # Standard library imports
1 # Standard library imports
2 from codeop import CommandCompiler
2 from codeop import CommandCompiler
3 from threading import Thread
3 from threading import Thread
4 import time
4 import time
5 import types
5 import types
6
6
7 # System library imports
7 # System library imports
8 from pygments.lexers import PythonLexer
8 from pygments.lexers import PythonLexer
9 from PyQt4 import QtCore, QtGui
9 from PyQt4 import QtCore, QtGui
10 import zmq
10 import zmq
11
11
12 # Local imports
12 # Local imports
13 from call_tip_widget import CallTipWidget
13 from call_tip_widget import CallTipWidget
14 from completion_lexer import CompletionLexer
14 from completion_lexer import CompletionLexer
15 from console_widget import HistoryConsoleWidget
15 from console_widget import HistoryConsoleWidget
16 from pygments_highlighter import PygmentsHighlighter
16 from pygments_highlighter import PygmentsHighlighter
17
17
18
18
19 class FrontendHighlighter(PygmentsHighlighter):
19 class FrontendHighlighter(PygmentsHighlighter):
20 """ A Python PygmentsHighlighter that can be turned on and off and which
20 """ A Python PygmentsHighlighter that can be turned on and off and which
21 knows about continuation prompts.
21 knows about continuation prompts.
22 """
22 """
23
23
24 def __init__(self, frontend):
24 def __init__(self, frontend):
25 PygmentsHighlighter.__init__(self, frontend.document(), PythonLexer())
25 PygmentsHighlighter.__init__(self, frontend.document(), PythonLexer())
26 self._current_offset = 0
26 self._current_offset = 0
27 self._frontend = frontend
27 self._frontend = frontend
28 self.highlighting_on = False
28 self.highlighting_on = False
29
29
30 def highlightBlock(self, qstring):
30 def highlightBlock(self, qstring):
31 """ Highlight a block of text. Reimplemented to highlight selectively.
31 """ Highlight a block of text. Reimplemented to highlight selectively.
32 """
32 """
33 if self.highlighting_on:
33 if self.highlighting_on:
34 for prompt in (self._frontend._prompt,
34 for prompt in (self._frontend._prompt,
35 self._frontend.continuation_prompt):
35 self._frontend.continuation_prompt):
36 if qstring.startsWith(prompt):
36 if qstring.startsWith(prompt):
37 qstring.remove(0, len(prompt))
37 qstring.remove(0, len(prompt))
38 self._current_offset = len(prompt)
38 self._current_offset = len(prompt)
39 break
39 break
40 PygmentsHighlighter.highlightBlock(self, qstring)
40 PygmentsHighlighter.highlightBlock(self, qstring)
41
41
42 def setFormat(self, start, count, format):
42 def setFormat(self, start, count, format):
43 """ Reimplemented to avoid highlighting continuation prompts.
43 """ Reimplemented to avoid highlighting continuation prompts.
44 """
44 """
45 start += self._current_offset
45 start += self._current_offset
46 PygmentsHighlighter.setFormat(self, start, count, format)
46 PygmentsHighlighter.setFormat(self, start, count, format)
47
47
48
48
49 class FrontendWidget(HistoryConsoleWidget):
49 class FrontendWidget(HistoryConsoleWidget):
50 """ A Qt frontend for an IPython kernel.
50 """ A Qt frontend for an IPython kernel.
51 """
51 """
52
52
53 # Emitted when an 'execute_reply' is received from the kernel.
53 # Emitted when an 'execute_reply' is received from the kernel.
54 executed = QtCore.pyqtSignal(object)
54 executed = QtCore.pyqtSignal(object)
55
55
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57 # 'QWidget' interface
57 # 'QWidget' interface
58 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
59
59
60 def __init__(self, kernel_manager, parent=None):
60 def __init__(self, kernel_manager, parent=None):
61 super(FrontendWidget, self).__init__(parent)
61 super(FrontendWidget, self).__init__(parent)
62
62
63 self._call_tip_widget = CallTipWidget(self)
63 self._call_tip_widget = CallTipWidget(self)
64 self._compile = CommandCompiler()
64 self._compile = CommandCompiler()
65 self._completion_lexer = CompletionLexer(PythonLexer())
65 self._completion_lexer = CompletionLexer(PythonLexer())
66 self._highlighter = FrontendHighlighter(self)
66 self._highlighter = FrontendHighlighter(self)
67 self._kernel_manager = None
67 self._kernel_manager = None
68
68
69 self.document().contentsChange.connect(self._document_contents_change)
70
71 self.continuation_prompt = '... '
69 self.continuation_prompt = '... '
72 self.kernel_manager = kernel_manager
70 self.kernel_manager = kernel_manager
73
71
72 self.document().contentsChange.connect(self._document_contents_change)
73
74 def focusOutEvent(self, event):
74 def focusOutEvent(self, event):
75 """ Reimplemented to hide calltips.
75 """ Reimplemented to hide calltips.
76 """
76 """
77 self._call_tip_widget.hide()
77 self._call_tip_widget.hide()
78 return super(FrontendWidget, self).focusOutEvent(event)
78 return super(FrontendWidget, self).focusOutEvent(event)
79
79
80 def keyPressEvent(self, event):
80 def keyPressEvent(self, event):
81 """ Reimplemented to hide calltips.
81 """ Reimplemented to hide calltips.
82 """
82 """
83 if event.key() == QtCore.Qt.Key_Escape:
83 if event.key() == QtCore.Qt.Key_Escape:
84 self._call_tip_widget.hide()
84 self._call_tip_widget.hide()
85 return super(FrontendWidget, self).keyPressEvent(event)
85 return super(FrontendWidget, self).keyPressEvent(event)
86
86
87 #---------------------------------------------------------------------------
87 #---------------------------------------------------------------------------
88 # 'ConsoleWidget' abstract interface
88 # 'ConsoleWidget' abstract interface
89 #---------------------------------------------------------------------------
89 #---------------------------------------------------------------------------
90
90
91 def _execute(self, interactive):
91 def _execute(self, interactive):
92 """ Called to execute the input buffer. When triggered by an the enter
92 """ Called to execute the input buffer. When triggered by an the enter
93 key press, 'interactive' is True; otherwise, it is False. Returns
93 key press, 'interactive' is True; otherwise, it is False. Returns
94 whether the input buffer was completely processed and a new prompt
94 whether the input buffer was completely processed and a new prompt
95 created.
95 created.
96 """
96 """
97 return self.execute_source(self.input_buffer, interactive=interactive)
97 return self.execute_source(self.input_buffer, interactive=interactive)
98
98
99 def _prompt_started_hook(self):
99 def _prompt_started_hook(self):
100 """ Called immediately after a new prompt is displayed.
100 """ Called immediately after a new prompt is displayed.
101 """
101 """
102 self._highlighter.highlighting_on = True
102 self._highlighter.highlighting_on = True
103
103
104 def _prompt_finished_hook(self):
104 def _prompt_finished_hook(self):
105 """ Called immediately after a prompt is finished, i.e. when some input
105 """ Called immediately after a prompt is finished, i.e. when some input
106 will be processed and a new prompt displayed.
106 will be processed and a new prompt displayed.
107 """
107 """
108 self._highlighter.highlighting_on = False
108 self._highlighter.highlighting_on = False
109
109
110 def _tab_pressed(self):
110 def _tab_pressed(self):
111 """ Called when the tab key is pressed. Returns whether to continue
111 """ Called when the tab key is pressed. Returns whether to continue
112 processing the event.
112 processing the event.
113 """
113 """
114 self._keep_cursor_in_buffer()
114 self._keep_cursor_in_buffer()
115 cursor = self.textCursor()
115 cursor = self.textCursor()
116 if not self._complete():
116 if not self._complete():
117 cursor.insertText(' ')
117 cursor.insertText(' ')
118 return False
118 return False
119
119
120 #---------------------------------------------------------------------------
120 #---------------------------------------------------------------------------
121 # 'FrontendWidget' interface
121 # 'FrontendWidget' interface
122 #---------------------------------------------------------------------------
122 #---------------------------------------------------------------------------
123
123
124 def execute_source(self, source, hidden=False, interactive=False):
124 def execute_source(self, source, hidden=False, interactive=False):
125 """ Execute a string containing Python code. If 'hidden', no output is
125 """ Execute a string containing Python code. If 'hidden', no output is
126 shown. Returns whether the source executed (i.e., returns True only
126 shown. Returns whether the source executed (i.e., returns True only
127 if no more input is necessary).
127 if no more input is necessary).
128 """
128 """
129 try:
129 try:
130 code = self._compile(source, symbol='single')
130 code = self._compile(source, symbol='single')
131 except (OverflowError, SyntaxError, ValueError):
131 except (OverflowError, SyntaxError, ValueError):
132 # Just let IPython deal with the syntax error.
132 # Just let IPython deal with the syntax error.
133 code = Exception
133 code = Exception
134
134
135 # Only execute interactive multiline input if it ends with a blank line
135 # Only execute interactive multiline input if it ends with a blank line
136 lines = source.splitlines()
136 lines = source.splitlines()
137 if interactive and len(lines) > 1 and lines[-1].strip() != '':
137 if interactive and len(lines) > 1 and lines[-1].strip() != '':
138 code = None
138 code = None
139
139
140 executed = code is not None
140 executed = code is not None
141 if executed:
141 if executed:
142 self.kernel_manager.xreq_channel.execute(source)
142 self.kernel_manager.xreq_channel.execute(source)
143 else:
143 else:
144 space = 0
144 space = 0
145 for char in lines[-1]:
145 for char in lines[-1]:
146 if char == '\t':
146 if char == '\t':
147 space += 4
147 space += 4
148 elif char == ' ':
148 elif char == ' ':
149 space += 1
149 space += 1
150 else:
150 else:
151 break
151 break
152 if source.endswith(':') or source.endswith(':\n'):
152 if source.endswith(':') or source.endswith(':\n'):
153 space += 4
153 space += 4
154 self._show_continuation_prompt()
154 self._show_continuation_prompt()
155 self.appendPlainText(' ' * space)
155 self.appendPlainText(' ' * space)
156
156
157 return executed
157 return executed
158
158
159 def execute_file(self, path, hidden=False):
159 def execute_file(self, path, hidden=False):
160 """ Attempts to execute file with 'path'. If 'hidden', no output is
160 """ Attempts to execute file with 'path'. If 'hidden', no output is
161 shown.
161 shown.
162 """
162 """
163 self.execute_source('run %s' % path, hidden=hidden)
163 self.execute_source('run %s' % path, hidden=hidden)
164
164
165 def _get_kernel_manager(self):
165 def _get_kernel_manager(self):
166 """ Returns the current kernel manager.
166 """ Returns the current kernel manager.
167 """
167 """
168 return self._kernel_manager
168 return self._kernel_manager
169
169
170 def _set_kernel_manager(self, kernel_manager):
170 def _set_kernel_manager(self, kernel_manager):
171 """ Sets a new kernel manager, configuring its channels as necessary.
171 """ Sets a new kernel manager, configuring its channels as necessary.
172 """
172 """
173 # Disconnect the old kernel manager.
173 # Disconnect the old kernel manager.
174 if self._kernel_manager is not None:
174 if self._kernel_manager is not None:
175 sub = self._kernel_manager.sub_channel
175 sub = self._kernel_manager.sub_channel
176 xreq = self._kernel_manager.xreq_channel
176 xreq = self._kernel_manager.xreq_channel
177 sub.message_received.disconnect(self._handle_sub)
177 sub.message_received.disconnect(self._handle_sub)
178 xreq.execute_reply.disconnect(self._handle_execute_reply)
178 xreq.execute_reply.disconnect(self._handle_execute_reply)
179 xreq.complete_reply.disconnect(self._handle_complete_reply)
179 xreq.complete_reply.disconnect(self._handle_complete_reply)
180 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
180 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
181
181
182 # Connect the new kernel manager.
182 # Connect the new kernel manager.
183 self._kernel_manager = kernel_manager
183 self._kernel_manager = kernel_manager
184 sub = kernel_manager.sub_channel
184 sub = kernel_manager.sub_channel
185 xreq = kernel_manager.xreq_channel
185 xreq = kernel_manager.xreq_channel
186 sub.message_received.connect(self._handle_sub)
186 sub.message_received.connect(self._handle_sub)
187 xreq.execute_reply.connect(self._handle_execute_reply)
187 xreq.execute_reply.connect(self._handle_execute_reply)
188 #xreq.complete_reply.connect(self._handle_complete_reply)
188 xreq.complete_reply.connect(self._handle_complete_reply)
189 #xreq.object_info_repy.connect(self._handle_object_info_reply)
189 xreq.object_info_reply.connect(self._handle_object_info_reply)
190
190
191 self._show_prompt('>>> ')
191 self._show_prompt('>>> ')
192
192
193 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
193 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
194
194
195 #---------------------------------------------------------------------------
195 #---------------------------------------------------------------------------
196 # 'FrontendWidget' protected interface
196 # 'FrontendWidget' protected interface
197 #---------------------------------------------------------------------------
197 #---------------------------------------------------------------------------
198
198
199 def _call_tip(self):
199 def _call_tip(self):
200 """ Shows a call tip, if appropriate, at the current cursor location.
200 """ Shows a call tip, if appropriate, at the current cursor location.
201 """
201 """
202 # Decide if it makes sense to show a call tip
202 # Decide if it makes sense to show a call tip
203 cursor = self.textCursor()
203 cursor = self.textCursor()
204 cursor.movePosition(QtGui.QTextCursor.Left)
204 cursor.movePosition(QtGui.QTextCursor.Left)
205 document = self.document()
205 document = self.document()
206 if document.characterAt(cursor.position()).toAscii() != '(':
206 if document.characterAt(cursor.position()).toAscii() != '(':
207 return False
207 return False
208 context = self._get_context(cursor)
208 context = self._get_context(cursor)
209 if not context:
209 if not context:
210 return False
210 return False
211
211
212 # Send the metadata request to the kernel
212 # Send the metadata request to the kernel
213 text = '.'.join(context)
213 name = '.'.join(context)
214 msg = self.session.send(self.request_socket, 'metadata_request',
214 self._calltip_id = self.kernel_manager.xreq_channel.object_info(name)
215 dict(context=text))
215 self._calltip_pos = self.textCursor().position()
216
217 # Give the kernel some time to respond
218 rep = self._recv_reply_now('metadata_reply')
219 doc = rep.content.docstring if rep else ''
220
221 # Show the call tip
222 if doc:
223 self._call_tip_widget.show_tip(doc)
224 return True
216 return True
225
217
226 def _complete(self):
218 def _complete(self):
227 """ Performs completion at the current cursor location.
219 """ Performs completion at the current cursor location.
228 """
220 """
229 # Decide if it makes sense to do completion
221 # Decide if it makes sense to do completion
230 context = self._get_context()
222 context = self._get_context()
231 if not context:
223 if not context:
232 return False
224 return False
233
225
234 # Send the completion request to the kernel
226 # Send the completion request to the kernel
235 text = '.'.join(context)
227 text = '.'.join(context)
236 line = self.input_buffer_cursor_line
228 self._complete_id = self.kernel_manager.xreq_channel.complete(
237 msg = self.session.send(self.request_socket, 'complete_request',
229 text, self.input_buffer_cursor_line, self.input_buffer)
238 dict(text=text, line=line))
230 self._complete_pos = self.textCursor().position()
239
240 # Give the kernel some time to respond
241 rep = self._recv_reply_now('complete_reply')
242 matches = rep.content.matches if rep else []
243
244 # Show the completion at the correct location
245 cursor = self.textCursor()
246 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
247 self._complete_with_items(cursor, matches)
248 return True
231 return True
249
232
250 def _get_context(self, cursor=None):
233 def _get_context(self, cursor=None):
251 """ Gets the context at the current cursor location.
234 """ Gets the context at the current cursor location.
252 """
235 """
253 if cursor is None:
236 if cursor is None:
254 cursor = self.textCursor()
237 cursor = self.textCursor()
255 cursor.movePosition(QtGui.QTextCursor.StartOfLine,
238 cursor.movePosition(QtGui.QTextCursor.StartOfLine,
256 QtGui.QTextCursor.KeepAnchor)
239 QtGui.QTextCursor.KeepAnchor)
257 text = unicode(cursor.selectedText())
240 text = unicode(cursor.selectedText())
258 return self._completion_lexer.get_context(text)
241 return self._completion_lexer.get_context(text)
259
242
260 #------ Signal handlers ----------------------------------------------------
243 #------ Signal handlers ----------------------------------------------------
261
244
262 def _document_contents_change(self, position, removed, added):
245 def _document_contents_change(self, position, removed, added):
263 """ Called whenever the document's content changes. Display a calltip
246 """ Called whenever the document's content changes. Display a calltip
264 if appropriate.
247 if appropriate.
265 """
248 """
266 # Calculate where the cursor should be *after* the change:
249 # Calculate where the cursor should be *after* the change:
267 position += added
250 position += added
268
251
269 document = self.document()
252 document = self.document()
270 if position == self.textCursor().position():
253 if position == self.textCursor().position():
271 self._call_tip()
254 self._call_tip()
272
255
273 def _handle_sub(self, omsg):
256 def _handle_sub(self, omsg):
274 handler = getattr(self, '_handle_%s' % omsg['msg_type'], None)
257 handler = getattr(self, '_handle_%s' % omsg['msg_type'], None)
275 if handler is not None:
258 if handler is not None:
276 handler(omsg)
259 handler(omsg)
277
260
278 def _handle_pyout(self, omsg):
261 def _handle_pyout(self, omsg):
279 session = omsg['parent_header']['session']
262 session = omsg['parent_header']['session']
280 if session == self.kernel_manager.session.session:
263 if session == self.kernel_manager.session.session:
281 self.appendPlainText(omsg['content']['data'] + '\n')
264 self.appendPlainText(omsg['content']['data'] + '\n')
282
265
283 def _handle_stream(self, omsg):
266 def _handle_stream(self, omsg):
284 self.appendPlainText(omsg['content']['data'])
267 self.appendPlainText(omsg['content']['data'])
285
268
286 def _handle_execute_reply(self, rep):
269 def _handle_execute_reply(self, rep):
287 content = rep['content']
270 content = rep['content']
288 status = content['status']
271 status = content['status']
289 if status == 'error':
272 if status == 'error':
290 self.appendPlainText(content['traceback'][-1])
273 self.appendPlainText(content['traceback'][-1])
291 elif status == 'aborted':
274 elif status == 'aborted':
292 text = "ERROR: ABORTED\n"
275 text = "ERROR: ABORTED\n"
293 self.appendPlainText(text)
276 self.appendPlainText(text)
294 self._show_prompt('>>> ')
277 self._show_prompt('>>> ')
295 self.executed.emit(rep)
278 self.executed.emit(rep)
296
279
297 #------ Communication methods ----------------------------------------------
280 def _handle_complete_reply(self, rep):
298
281 cursor = self.textCursor()
299 def _recv_reply(self):
282 if rep['parent_header']['msg_id'] == self._complete_id and \
300 return self.session.recv(self.request_socket)
283 cursor.position() == self._complete_pos:
284 text = '.'.join(self._get_context())
285 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
286 self._complete_with_items(cursor, rep['content']['matches'])
301
287
302 def _recv_reply_now(self, msg_type):
288 def _handle_object_info_reply(self, rep):
303 for i in xrange(5):
289 cursor = self.textCursor()
304 rep = self._recv_reply()
290 if rep['parent_header']['msg_id'] == self._calltip_id and \
305 if rep is not None and rep.msg_type == msg_type:
291 cursor.position() == self._calltip_pos:
306 return rep
292 doc = rep['content']['docstring']
307 time.sleep(0.1)
293 if doc:
308 return None
294 self._call_tip_widget.show_tip(doc)
309
295
310
296
311 if __name__ == '__main__':
297 if __name__ == '__main__':
312 import sys
298 import sys
313 from IPython.frontend.qt.kernelmanager import QtKernelManager
299 from IPython.frontend.qt.kernelmanager import QtKernelManager
314
300
315 # Create KernelManager
301 # Create KernelManager
316 xreq_addr = ('127.0.0.1', 5575)
302 xreq_addr = ('127.0.0.1', 5575)
317 sub_addr = ('127.0.0.1', 5576)
303 sub_addr = ('127.0.0.1', 5576)
318 rep_addr = ('127.0.0.1', 5577)
304 rep_addr = ('127.0.0.1', 5577)
319 kernel_manager = QtKernelManager(xreq_addr, sub_addr, rep_addr)
305 kernel_manager = QtKernelManager(xreq_addr, sub_addr, rep_addr)
320 kernel_manager.sub_channel.start()
306 kernel_manager.sub_channel.start()
321 kernel_manager.xreq_channel.start()
307 kernel_manager.xreq_channel.start()
322
308
323 # Launch application
309 # Launch application
324 app = QtGui.QApplication(sys.argv)
310 app = QtGui.QApplication(sys.argv)
325 widget = FrontendWidget(kernel_manager)
311 widget = FrontendWidget(kernel_manager)
326 widget.setWindowTitle('Python')
312 widget.setWindowTitle('Python')
327 widget.resize(640, 480)
313 widget.resize(640, 480)
328 widget.show()
314 widget.show()
329 sys.exit(app.exec_())
315 sys.exit(app.exec_())
330
316
@@ -1,272 +1,311 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 * Finish implementing `raw_input`.
6 * Finish implementing `raw_input`.
7 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 * Implement `set_parent` logic. Right before doing exec, the Kernel should
8 call set_parent on all the PUB objects with the message about to be executed.
8 call set_parent on all the PUB objects with the message about to be executed.
9 * Implement random port and security key logic.
9 * Implement random port and security key logic.
10 * Implement control messages.
10 * Implement control messages.
11 * Implement event loop and poll version.
11 * Implement event loop and poll version.
12 """
12 """
13
13
14 import __builtin__
14 import __builtin__
15 import sys
15 import sys
16 import time
16 import time
17 import traceback
17 import traceback
18
18
19 from code import CommandCompiler
19 from code import CommandCompiler
20
20
21 import zmq
21 import zmq
22
22
23 from session import Session, Message, extract_header
23 from session import Session, Message, extract_header
24 from completer import KernelCompleter
24 from completer import KernelCompleter
25
25
26 class OutStream(object):
26 class OutStream(object):
27 """A file like object that publishes the stream to a 0MQ PUB socket."""
27 """A file like object that publishes the stream to a 0MQ PUB socket."""
28
28
29 def __init__(self, session, pub_socket, name, max_buffer=200):
29 def __init__(self, session, pub_socket, name, max_buffer=200):
30 self.session = session
30 self.session = session
31 self.pub_socket = pub_socket
31 self.pub_socket = pub_socket
32 self.name = name
32 self.name = name
33 self._buffer = []
33 self._buffer = []
34 self._buffer_len = 0
34 self._buffer_len = 0
35 self.max_buffer = max_buffer
35 self.max_buffer = max_buffer
36 self.parent_header = {}
36 self.parent_header = {}
37
37
38 def set_parent(self, parent):
38 def set_parent(self, parent):
39 self.parent_header = extract_header(parent)
39 self.parent_header = extract_header(parent)
40
40
41 def close(self):
41 def close(self):
42 self.pub_socket = None
42 self.pub_socket = None
43
43
44 def flush(self):
44 def flush(self):
45 if self.pub_socket is None:
45 if self.pub_socket is None:
46 raise ValueError(u'I/O operation on closed file')
46 raise ValueError(u'I/O operation on closed file')
47 else:
47 else:
48 if self._buffer:
48 if self._buffer:
49 data = ''.join(self._buffer)
49 data = ''.join(self._buffer)
50 content = {u'name':self.name, u'data':data}
50 content = {u'name':self.name, u'data':data}
51 msg = self.session.msg(u'stream', content=content,
51 msg = self.session.msg(u'stream', content=content,
52 parent=self.parent_header)
52 parent=self.parent_header)
53 print>>sys.__stdout__, Message(msg)
53 print>>sys.__stdout__, Message(msg)
54 self.pub_socket.send_json(msg)
54 self.pub_socket.send_json(msg)
55 self._buffer_len = 0
55 self._buffer_len = 0
56 self._buffer = []
56 self._buffer = []
57
57
58 def isattr(self):
58 def isattr(self):
59 return False
59 return False
60
60
61 def next(self):
61 def next(self):
62 raise IOError('Read not supported on a write only stream.')
62 raise IOError('Read not supported on a write only stream.')
63
63
64 def read(self, size=None):
64 def read(self, size=None):
65 raise IOError('Read not supported on a write only stream.')
65 raise IOError('Read not supported on a write only stream.')
66
66
67 readline=read
67 readline=read
68
68
69 def write(self, s):
69 def write(self, s):
70 if self.pub_socket is None:
70 if self.pub_socket is None:
71 raise ValueError('I/O operation on closed file')
71 raise ValueError('I/O operation on closed file')
72 else:
72 else:
73 self._buffer.append(s)
73 self._buffer.append(s)
74 self._buffer_len += len(s)
74 self._buffer_len += len(s)
75 self._maybe_send()
75 self._maybe_send()
76
76
77 def _maybe_send(self):
77 def _maybe_send(self):
78 if '\n' in self._buffer[-1]:
78 if '\n' in self._buffer[-1]:
79 self.flush()
79 self.flush()
80 if self._buffer_len > self.max_buffer:
80 if self._buffer_len > self.max_buffer:
81 self.flush()
81 self.flush()
82
82
83 def writelines(self, sequence):
83 def writelines(self, sequence):
84 if self.pub_socket is None:
84 if self.pub_socket is None:
85 raise ValueError('I/O operation on closed file')
85 raise ValueError('I/O operation on closed file')
86 else:
86 else:
87 for s in sequence:
87 for s in sequence:
88 self.write(s)
88 self.write(s)
89
89
90
90
91 class DisplayHook(object):
91 class DisplayHook(object):
92
92
93 def __init__(self, session, pub_socket):
93 def __init__(self, session, pub_socket):
94 self.session = session
94 self.session = session
95 self.pub_socket = pub_socket
95 self.pub_socket = pub_socket
96 self.parent_header = {}
96 self.parent_header = {}
97
97
98 def __call__(self, obj):
98 def __call__(self, obj):
99 if obj is None:
99 if obj is None:
100 return
100 return
101
101
102 __builtin__._ = obj
102 __builtin__._ = obj
103 msg = self.session.msg(u'pyout', {u'data':repr(obj)},
103 msg = self.session.msg(u'pyout', {u'data':repr(obj)},
104 parent=self.parent_header)
104 parent=self.parent_header)
105 self.pub_socket.send_json(msg)
105 self.pub_socket.send_json(msg)
106
106
107 def set_parent(self, parent):
107 def set_parent(self, parent):
108 self.parent_header = extract_header(parent)
108 self.parent_header = extract_header(parent)
109
109
110
110
111 class RawInput(object):
111 class RawInput(object):
112
112
113 def __init__(self, session, socket):
113 def __init__(self, session, socket):
114 self.session = session
114 self.session = session
115 self.socket = socket
115 self.socket = socket
116
116
117 def __call__(self, prompt=None):
117 def __call__(self, prompt=None):
118 msg = self.session.msg(u'raw_input')
118 msg = self.session.msg(u'raw_input')
119 self.socket.send_json(msg)
119 self.socket.send_json(msg)
120 while True:
120 while True:
121 try:
121 try:
122 reply = self.socket.recv_json(zmq.NOBLOCK)
122 reply = self.socket.recv_json(zmq.NOBLOCK)
123 except zmq.ZMQError, e:
123 except zmq.ZMQError, e:
124 if e.errno == zmq.EAGAIN:
124 if e.errno == zmq.EAGAIN:
125 pass
125 pass
126 else:
126 else:
127 raise
127 raise
128 else:
128 else:
129 break
129 break
130 return reply[u'content'][u'data']
130 return reply[u'content'][u'data']
131
131
132
132
133 class Kernel(object):
133 class Kernel(object):
134
134
135 def __init__(self, session, reply_socket, pub_socket):
135 def __init__(self, session, reply_socket, pub_socket):
136 self.session = session
136 self.session = session
137 self.reply_socket = reply_socket
137 self.reply_socket = reply_socket
138 self.pub_socket = pub_socket
138 self.pub_socket = pub_socket
139 self.user_ns = {}
139 self.user_ns = {}
140 self.history = []
140 self.history = []
141 self.compiler = CommandCompiler()
141 self.compiler = CommandCompiler()
142 self.completer = KernelCompleter(self.user_ns)
142 self.completer = KernelCompleter(self.user_ns)
143
143
144 # Build dict of handlers for message types
144 # Build dict of handlers for message types
145 msg_types = [ 'execute_request', 'complete_request',
146 'object_info_request' ]
145 self.handlers = {}
147 self.handlers = {}
146 for msg_type in ['execute_request', 'complete_request']:
148 for msg_type in msg_types:
147 self.handlers[msg_type] = getattr(self, msg_type)
149 self.handlers[msg_type] = getattr(self, msg_type)
148
150
149 def abort_queue(self):
151 def abort_queue(self):
150 while True:
152 while True:
151 try:
153 try:
152 ident = self.reply_socket.recv(zmq.NOBLOCK)
154 ident = self.reply_socket.recv(zmq.NOBLOCK)
153 except zmq.ZMQError, e:
155 except zmq.ZMQError, e:
154 if e.errno == zmq.EAGAIN:
156 if e.errno == zmq.EAGAIN:
155 break
157 break
156 else:
158 else:
157 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
159 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
158 msg = self.reply_socket.recv_json()
160 msg = self.reply_socket.recv_json()
159 print>>sys.__stdout__, "Aborting:"
161 print>>sys.__stdout__, "Aborting:"
160 print>>sys.__stdout__, Message(msg)
162 print>>sys.__stdout__, Message(msg)
161 msg_type = msg['msg_type']
163 msg_type = msg['msg_type']
162 reply_type = msg_type.split('_')[0] + '_reply'
164 reply_type = msg_type.split('_')[0] + '_reply'
163 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
165 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
164 print>>sys.__stdout__, Message(reply_msg)
166 print>>sys.__stdout__, Message(reply_msg)
165 self.reply_socket.send(ident,zmq.SNDMORE)
167 self.reply_socket.send(ident,zmq.SNDMORE)
166 self.reply_socket.send_json(reply_msg)
168 self.reply_socket.send_json(reply_msg)
167 # We need to wait a bit for requests to come in. This can probably
169 # We need to wait a bit for requests to come in. This can probably
168 # be set shorter for true asynchronous clients.
170 # be set shorter for true asynchronous clients.
169 time.sleep(0.1)
171 time.sleep(0.1)
170
172
171 def execute_request(self, ident, parent):
173 def execute_request(self, ident, parent):
172 try:
174 try:
173 code = parent[u'content'][u'code']
175 code = parent[u'content'][u'code']
174 except:
176 except:
175 print>>sys.__stderr__, "Got bad msg: "
177 print>>sys.__stderr__, "Got bad msg: "
176 print>>sys.__stderr__, Message(parent)
178 print>>sys.__stderr__, Message(parent)
177 return
179 return
178 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
180 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
179 self.pub_socket.send_json(pyin_msg)
181 self.pub_socket.send_json(pyin_msg)
180 try:
182 try:
181 comp_code = self.compiler(code, '<zmq-kernel>')
183 comp_code = self.compiler(code, '<zmq-kernel>')
182 sys.displayhook.set_parent(parent)
184 sys.displayhook.set_parent(parent)
183 exec comp_code in self.user_ns, self.user_ns
185 exec comp_code in self.user_ns, self.user_ns
184 except:
186 except:
185 result = u'error'
187 result = u'error'
186 etype, evalue, tb = sys.exc_info()
188 etype, evalue, tb = sys.exc_info()
187 tb = traceback.format_exception(etype, evalue, tb)
189 tb = traceback.format_exception(etype, evalue, tb)
188 exc_content = {
190 exc_content = {
189 u'status' : u'error',
191 u'status' : u'error',
190 u'traceback' : tb,
192 u'traceback' : tb,
191 u'etype' : unicode(etype),
193 u'etype' : unicode(etype),
192 u'evalue' : unicode(evalue)
194 u'evalue' : unicode(evalue)
193 }
195 }
194 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
196 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
195 self.pub_socket.send_json(exc_msg)
197 self.pub_socket.send_json(exc_msg)
196 reply_content = exc_content
198 reply_content = exc_content
197 else:
199 else:
198 reply_content = {'status' : 'ok'}
200 reply_content = {'status' : 'ok'}
199 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
201 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
200 print>>sys.__stdout__, Message(reply_msg)
202 print>>sys.__stdout__, Message(reply_msg)
201 self.reply_socket.send(ident, zmq.SNDMORE)
203 self.reply_socket.send(ident, zmq.SNDMORE)
202 self.reply_socket.send_json(reply_msg)
204 self.reply_socket.send_json(reply_msg)
203 if reply_msg['content']['status'] == u'error':
205 if reply_msg['content']['status'] == u'error':
204 self.abort_queue()
206 self.abort_queue()
205
207
206 def complete_request(self, ident, parent):
208 def complete_request(self, ident, parent):
207 matches = {'matches' : self.complete(parent),
209 matches = {'matches' : self.complete(parent),
208 'status' : 'ok'}
210 'status' : 'ok'}
209 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
211 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
210 matches, parent, ident)
212 matches, parent, ident)
211 print >> sys.__stdout__, completion_msg
213 print >> sys.__stdout__, completion_msg
212
214
213 def complete(self, msg):
215 def complete(self, msg):
214 return self.completer.complete(msg.content.line, msg.content.text)
216 return self.completer.complete(msg.content.line, msg.content.text)
215
217
218 def object_info_request(self, ident, parent):
219 context = parent['content']['oname'].split('.')
220 object_info = self.object_info(context)
221 msg = self.session.send(self.reply_socket, 'object_info_reply',
222 object_info, parent, ident)
223 print >> sys.__stdout__, msg
224
225 def object_info(self, context):
226 symbol, leftover = self.symbol_from_context(context)
227 if symbol is not None and not leftover:
228 doc = getattr(symbol, '__doc__', '')
229 else:
230 doc = ''
231 object_info = dict(docstring = doc)
232 return object_info
233
234 def symbol_from_context(self, context):
235 if not context:
236 return None, context
237
238 base_symbol_string = context[0]
239 symbol = self.user_ns.get(base_symbol_string, None)
240 if symbol is None:
241 symbol = __builtin__.__dict__.get(base_symbol_string, None)
242 if symbol is None:
243 return None, context
244
245 context = context[1:]
246 for i, name in enumerate(context):
247 new_symbol = getattr(symbol, name, None)
248 if new_symbol is None:
249 return symbol, context[i:]
250 else:
251 symbol = new_symbol
252
253 return symbol, []
254
216 def start(self):
255 def start(self):
217 while True:
256 while True:
218 ident = self.reply_socket.recv()
257 ident = self.reply_socket.recv()
219 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
258 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
220 msg = self.reply_socket.recv_json()
259 msg = self.reply_socket.recv_json()
221 omsg = Message(msg)
260 omsg = Message(msg)
222 print>>sys.__stdout__
261 print>>sys.__stdout__
223 print>>sys.__stdout__, omsg
262 print>>sys.__stdout__, omsg
224 handler = self.handlers.get(omsg.msg_type, None)
263 handler = self.handlers.get(omsg.msg_type, None)
225 if handler is None:
264 if handler is None:
226 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
265 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
227 else:
266 else:
228 handler(ident, omsg)
267 handler(ident, omsg)
229
268
230
269
231 def main():
270 def main():
232 c = zmq.Context()
271 c = zmq.Context()
233
272
234 ip = '127.0.0.1'
273 ip = '127.0.0.1'
235 port_base = 5575
274 port_base = 5575
236 connection = ('tcp://%s' % ip) + ':%i'
275 connection = ('tcp://%s' % ip) + ':%i'
237 rep_conn = connection % port_base
276 rep_conn = connection % port_base
238 pub_conn = connection % (port_base+1)
277 pub_conn = connection % (port_base+1)
239
278
240 print >>sys.__stdout__, "Starting the kernel..."
279 print >>sys.__stdout__, "Starting the kernel..."
241 print >>sys.__stdout__, "XREP Channel:", rep_conn
280 print >>sys.__stdout__, "XREP Channel:", rep_conn
242 print >>sys.__stdout__, "PUB Channel:", pub_conn
281 print >>sys.__stdout__, "PUB Channel:", pub_conn
243
282
244 session = Session(username=u'kernel')
283 session = Session(username=u'kernel')
245
284
246 reply_socket = c.socket(zmq.XREP)
285 reply_socket = c.socket(zmq.XREP)
247 reply_socket.bind(rep_conn)
286 reply_socket.bind(rep_conn)
248
287
249 pub_socket = c.socket(zmq.PUB)
288 pub_socket = c.socket(zmq.PUB)
250 pub_socket.bind(pub_conn)
289 pub_socket.bind(pub_conn)
251
290
252 stdout = OutStream(session, pub_socket, u'stdout')
291 stdout = OutStream(session, pub_socket, u'stdout')
253 stderr = OutStream(session, pub_socket, u'stderr')
292 stderr = OutStream(session, pub_socket, u'stderr')
254 sys.stdout = stdout
293 sys.stdout = stdout
255 sys.stderr = stderr
294 sys.stderr = stderr
256
295
257 display_hook = DisplayHook(session, pub_socket)
296 display_hook = DisplayHook(session, pub_socket)
258 sys.displayhook = display_hook
297 sys.displayhook = display_hook
259
298
260 kernel = Kernel(session, reply_socket, pub_socket)
299 kernel = Kernel(session, reply_socket, pub_socket)
261
300
262 # For debugging convenience, put sleep and a string in the namespace, so we
301 # For debugging convenience, put sleep and a string in the namespace, so we
263 # have them every time we start.
302 # have them every time we start.
264 kernel.user_ns['sleep'] = time.sleep
303 kernel.user_ns['sleep'] = time.sleep
265 kernel.user_ns['s'] = 'Test string'
304 kernel.user_ns['s'] = 'Test string'
266
305
267 print >>sys.__stdout__, "Use Ctrl-\\ (NOT Ctrl-C!) to terminate."
306 print >>sys.__stdout__, "Use Ctrl-\\ (NOT Ctrl-C!) to terminate."
268 kernel.start()
307 kernel.start()
269
308
270
309
271 if __name__ == '__main__':
310 if __name__ == '__main__':
272 main()
311 main()
General Comments 0
You need to be logged in to leave comments. Login now