##// END OF EJS Templates
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
epatters -
Show More
@@ -0,0 +1,80
1 # Local imports.
2 from frontend_widget import FrontendWidget
3
4
5 class IPythonWidget(FrontendWidget):
6 """ A FrontendWidget for an IPython kernel.
7 """
8
9 #---------------------------------------------------------------------------
10 # 'FrontendWidget' interface
11 #---------------------------------------------------------------------------
12
13 def __init__(self, kernel_manager, parent=None):
14 super(IPythonWidget, self).__init__(kernel_manager, parent)
15
16 self._magic_overrides = {}
17
18 def execute_source(self, source, hidden=False, interactive=False):
19 """ Reimplemented to override magic commands.
20 """
21 magic_source = source.strip()
22 if magic_source.startswith('%'):
23 magic_source = magic_source[1:]
24 magic, sep, arguments = magic_source.partition(' ')
25 if not magic:
26 magic = magic_source
27
28 callback = self._magic_overrides.get(magic)
29 if callback:
30 output = callback(arguments)
31 if output:
32 self.appendPlainText(output)
33 self._show_prompt('>>> ')
34 return True
35 else:
36 return super(IPythonWidget, self).execute_source(source, hidden,
37 interactive)
38
39 #---------------------------------------------------------------------------
40 # 'IPythonWidget' interface
41 #---------------------------------------------------------------------------
42
43 def set_magic_override(self, magic, callback):
44 """ Overrides an IPython magic command. This magic will be intercepted
45 by the frontend rather than passed on to the kernel and 'callback'
46 will be called with a single argument: a string of argument(s) for
47 the magic. The callback can (optionally) return text to print to the
48 console.
49 """
50 self._magic_overrides[magic] = callback
51
52 def remove_magic_override(self, magic):
53 """ Removes the override for the specified magic, if there is one.
54 """
55 try:
56 del self._magic_overrides[magic]
57 except KeyError:
58 pass
59
60
61 if __name__ == '__main__':
62 import sys
63 from IPython.frontend.qt.kernelmanager import QtKernelManager
64
65 # Create KernelManager
66 xreq_addr = ('127.0.0.1', 5575)
67 sub_addr = ('127.0.0.1', 5576)
68 rep_addr = ('127.0.0.1', 5577)
69 kernel_manager = QtKernelManager(xreq_addr, sub_addr, rep_addr)
70 kernel_manager.sub_channel.start()
71 kernel_manager.xreq_channel.start()
72
73 # Launch application
74 app = QtGui.QApplication(sys.argv)
75 widget = IPythonWidget(kernel_manager)
76 widget.setWindowTitle('Python')
77 widget.resize(640, 480)
78 widget.show()
79 sys.exit(app.exec_())
80
@@ -1,324 +1,303
1 1 # Standard library imports
2 2 from codeop import CommandCompiler
3 3 from threading import Thread
4 4 import time
5 5 import types
6 6
7 7 # System library imports
8 8 from pygments.lexers import PythonLexer
9 9 from PyQt4 import QtCore, QtGui
10 10 import zmq
11 11
12 12 # Local imports
13 13 from call_tip_widget import CallTipWidget
14 14 from completion_lexer import CompletionLexer
15 15 from console_widget import HistoryConsoleWidget
16 16 from pygments_highlighter import PygmentsHighlighter
17 17
18 18
19 19 class FrontendHighlighter(PygmentsHighlighter):
20 20 """ A Python PygmentsHighlighter that can be turned on and off and which
21 21 knows about continuation prompts.
22 22 """
23 23
24 24 def __init__(self, frontend):
25 25 PygmentsHighlighter.__init__(self, frontend.document(), PythonLexer())
26 26 self._current_offset = 0
27 27 self._frontend = frontend
28 28 self.highlighting_on = False
29 29
30 30 def highlightBlock(self, qstring):
31 31 """ Highlight a block of text. Reimplemented to highlight selectively.
32 32 """
33 33 if self.highlighting_on:
34 34 for prompt in (self._frontend._prompt,
35 35 self._frontend.continuation_prompt):
36 36 if qstring.startsWith(prompt):
37 37 qstring.remove(0, len(prompt))
38 38 self._current_offset = len(prompt)
39 39 break
40 40 PygmentsHighlighter.highlightBlock(self, qstring)
41 41
42 42 def setFormat(self, start, count, format):
43 43 """ Reimplemented to avoid highlighting continuation prompts.
44 44 """
45 45 start += self._current_offset
46 46 PygmentsHighlighter.setFormat(self, start, count, format)
47 47
48 48
49 49 class FrontendWidget(HistoryConsoleWidget):
50 """ A Qt frontend for an IPython kernel.
50 """ A Qt frontend for a generic Python kernel.
51 51 """
52 52
53 53 # Emitted when an 'execute_reply' is received from the kernel.
54 54 executed = QtCore.pyqtSignal(object)
55 55
56 56 #---------------------------------------------------------------------------
57 57 # 'QWidget' interface
58 58 #---------------------------------------------------------------------------
59 59
60 60 def __init__(self, kernel_manager, parent=None):
61 61 super(FrontendWidget, self).__init__(parent)
62 62
63 63 self._call_tip_widget = CallTipWidget(self)
64 64 self._compile = CommandCompiler()
65 65 self._completion_lexer = CompletionLexer(PythonLexer())
66 self._hidden = False
66 self._hidden = True
67 67 self._highlighter = FrontendHighlighter(self)
68 68 self._kernel_manager = None
69 69
70 70 self.continuation_prompt = '... '
71 71 self.kernel_manager = kernel_manager
72 72
73 73 self.document().contentsChange.connect(self._document_contents_change)
74 74
75 75 def focusOutEvent(self, event):
76 76 """ Reimplemented to hide calltips.
77 77 """
78 78 self._call_tip_widget.hide()
79 79 return super(FrontendWidget, self).focusOutEvent(event)
80 80
81 81 def keyPressEvent(self, event):
82 82 """ Reimplemented to hide calltips.
83 83 """
84 84 if event.key() == QtCore.Qt.Key_Escape:
85 85 self._call_tip_widget.hide()
86 86 return super(FrontendWidget, self).keyPressEvent(event)
87 87
88 88 #---------------------------------------------------------------------------
89 89 # 'ConsoleWidget' abstract interface
90 90 #---------------------------------------------------------------------------
91 91
92 92 def _execute(self, interactive):
93 93 """ Called to execute the input buffer. When triggered by an the enter
94 94 key press, 'interactive' is True; otherwise, it is False. Returns
95 95 whether the input buffer was completely processed and a new prompt
96 96 created.
97 97 """
98 98 return self.execute_source(self.input_buffer, interactive=interactive)
99 99
100 100 def _prompt_started_hook(self):
101 101 """ Called immediately after a new prompt is displayed.
102 102 """
103 103 self._highlighter.highlighting_on = True
104 104
105 105 def _prompt_finished_hook(self):
106 106 """ Called immediately after a prompt is finished, i.e. when some input
107 107 will be processed and a new prompt displayed.
108 108 """
109 109 self._highlighter.highlighting_on = False
110 110
111 111 def _tab_pressed(self):
112 112 """ Called when the tab key is pressed. Returns whether to continue
113 113 processing the event.
114 114 """
115 115 self._keep_cursor_in_buffer()
116 116 cursor = self.textCursor()
117 117 if not self._complete():
118 118 cursor.insertText(' ')
119 119 return False
120 120
121 121 #---------------------------------------------------------------------------
122 122 # 'FrontendWidget' interface
123 123 #---------------------------------------------------------------------------
124 124
125 125 def execute_source(self, source, hidden=False, interactive=False):
126 126 """ Execute a string containing Python code. If 'hidden', no output is
127 127 shown. Returns whether the source executed (i.e., returns True only
128 128 if no more input is necessary).
129 129 """
130 # Use CommandCompiler to determine if more input is needed.
130 131 try:
131 132 code = self._compile(source, symbol='single')
132 133 except (OverflowError, SyntaxError, ValueError):
133 134 # Just let IPython deal with the syntax error.
134 135 code = Exception
135 136
136 137 # Only execute interactive multiline input if it ends with a blank line
137 138 lines = source.splitlines()
138 139 if interactive and len(lines) > 1 and lines[-1].strip() != '':
139 140 code = None
140 141
141 142 executed = code is not None
142 143 if executed:
143 144 self.kernel_manager.xreq_channel.execute(source)
144 145 self._hidden = hidden
145 146 else:
146 147 space = 0
147 148 for char in lines[-1]:
148 149 if char == '\t':
149 150 space += 4
150 151 elif char == ' ':
151 152 space += 1
152 153 else:
153 154 break
154 155 if source.endswith(':') or source.endswith(':\n'):
155 156 space += 4
156 157 self._show_continuation_prompt()
157 158 self.appendPlainText(' ' * space)
158 159
159 160 return executed
160 161
161 162 def execute_file(self, path, hidden=False):
162 163 """ Attempts to execute file with 'path'. If 'hidden', no output is
163 164 shown.
164 165 """
165 166 self.execute_source('run %s' % path, hidden=hidden)
166 167
167 168 def _get_kernel_manager(self):
168 169 """ Returns the current kernel manager.
169 170 """
170 171 return self._kernel_manager
171 172
172 173 def _set_kernel_manager(self, kernel_manager):
173 174 """ Sets a new kernel manager, configuring its channels as necessary.
174 175 """
175 176 # Disconnect the old kernel manager.
176 177 if self._kernel_manager is not None:
177 178 sub = self._kernel_manager.sub_channel
178 179 xreq = self._kernel_manager.xreq_channel
179 180 sub.message_received.disconnect(self._handle_sub)
180 181 xreq.execute_reply.disconnect(self._handle_execute_reply)
181 182 xreq.complete_reply.disconnect(self._handle_complete_reply)
182 183 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
183 184
184 185 # Connect the new kernel manager.
185 186 self._kernel_manager = kernel_manager
186 187 sub = kernel_manager.sub_channel
187 188 xreq = kernel_manager.xreq_channel
188 189 sub.message_received.connect(self._handle_sub)
189 190 xreq.execute_reply.connect(self._handle_execute_reply)
190 191 xreq.complete_reply.connect(self._handle_complete_reply)
191 192 xreq.object_info_reply.connect(self._handle_object_info_reply)
192 193
193 194 self._show_prompt('>>> ')
194 195
195 196 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
196 197
197 198 #---------------------------------------------------------------------------
198 199 # 'FrontendWidget' protected interface
199 200 #---------------------------------------------------------------------------
200 201
201 202 def _call_tip(self):
202 203 """ Shows a call tip, if appropriate, at the current cursor location.
203 204 """
204 205 # Decide if it makes sense to show a call tip
205 206 cursor = self.textCursor()
206 207 cursor.movePosition(QtGui.QTextCursor.Left)
207 208 document = self.document()
208 209 if document.characterAt(cursor.position()).toAscii() != '(':
209 210 return False
210 211 context = self._get_context(cursor)
211 212 if not context:
212 213 return False
213 214
214 215 # Send the metadata request to the kernel
215 216 name = '.'.join(context)
216 217 self._calltip_id = self.kernel_manager.xreq_channel.object_info(name)
217 218 self._calltip_pos = self.textCursor().position()
218 219 return True
219 220
220 221 def _complete(self):
221 222 """ Performs completion at the current cursor location.
222 223 """
223 224 # Decide if it makes sense to do completion
224 225 context = self._get_context()
225 226 if not context:
226 227 return False
227 228
228 229 # Send the completion request to the kernel
229 230 text = '.'.join(context)
230 231 self._complete_id = self.kernel_manager.xreq_channel.complete(
231 232 text, self.input_buffer_cursor_line, self.input_buffer)
232 233 self._complete_pos = self.textCursor().position()
233 234 return True
234 235
235 236 def _get_context(self, cursor=None):
236 237 """ Gets the context at the current cursor location.
237 238 """
238 239 if cursor is None:
239 240 cursor = self.textCursor()
240 241 cursor.movePosition(QtGui.QTextCursor.StartOfLine,
241 242 QtGui.QTextCursor.KeepAnchor)
242 243 text = unicode(cursor.selectedText())
243 244 return self._completion_lexer.get_context(text)
244 245
245 246 #------ Signal handlers ----------------------------------------------------
246 247
247 248 def _document_contents_change(self, position, removed, added):
248 249 """ Called whenever the document's content changes. Display a calltip
249 250 if appropriate.
250 251 """
251 252 # Calculate where the cursor should be *after* the change:
252 253 position += added
253 254
254 255 document = self.document()
255 256 if position == self.textCursor().position():
256 257 self._call_tip()
257 258
258 259 def _handle_sub(self, omsg):
259 260 if not self._hidden:
260 261 handler = getattr(self, '_handle_%s' % omsg['msg_type'], None)
261 262 if handler is not None:
262 263 handler(omsg)
263 264
264 265 def _handle_pyout(self, omsg):
265 266 session = omsg['parent_header']['session']
266 267 if session == self.kernel_manager.session.session:
267 268 self.appendPlainText(omsg['content']['data'] + '\n')
268 269
269 270 def _handle_stream(self, omsg):
270 271 self.appendPlainText(omsg['content']['data'])
271 272
272 273 def _handle_execute_reply(self, rep):
273 274 # Make sure that all output from the SUB channel has been processed
274 275 # before writing a new prompt.
275 276 self.kernel_manager.sub_channel.flush()
276 277
277 278 content = rep['content']
278 279 status = content['status']
279 280 if status == 'error':
280 281 self.appendPlainText(content['traceback'][-1])
281 282 elif status == 'aborted':
282 283 text = "ERROR: ABORTED\n"
283 284 self.appendPlainText(text)
284 self._hidden = False
285 self._hidden = True
285 286 self._show_prompt('>>> ')
286 287 self.executed.emit(rep)
287 288
288 289 def _handle_complete_reply(self, rep):
289 290 cursor = self.textCursor()
290 291 if rep['parent_header']['msg_id'] == self._complete_id and \
291 292 cursor.position() == self._complete_pos:
292 293 text = '.'.join(self._get_context())
293 294 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
294 295 self._complete_with_items(cursor, rep['content']['matches'])
295 296
296 297 def _handle_object_info_reply(self, rep):
297 298 cursor = self.textCursor()
298 299 if rep['parent_header']['msg_id'] == self._calltip_id and \
299 300 cursor.position() == self._calltip_pos:
300 301 doc = rep['content']['docstring']
301 302 if doc:
302 303 self._call_tip_widget.show_tip(doc)
303
304
305 if __name__ == '__main__':
306 import sys
307 from IPython.frontend.qt.kernelmanager import QtKernelManager
308
309 # Create KernelManager
310 xreq_addr = ('127.0.0.1', 5575)
311 sub_addr = ('127.0.0.1', 5576)
312 rep_addr = ('127.0.0.1', 5577)
313 kernel_manager = QtKernelManager(xreq_addr, sub_addr, rep_addr)
314 kernel_manager.sub_channel.start()
315 kernel_manager.xreq_channel.start()
316
317 # Launch application
318 app = QtGui.QApplication(sys.argv)
319 widget = FrontendWidget(kernel_manager)
320 widget.setWindowTitle('Python')
321 widget.resize(640, 480)
322 widget.show()
323 sys.exit(app.exec_())
324
@@ -1,335 +1,335
1 1 """Kernel frontend classes.
2 2
3 3 TODO: Create logger to handle debugging and console messages.
4 4
5 5 """
6 6
7 7 # Standard library imports.
8 8 from Queue import Queue, Empty
9 9 from threading import Thread
10 10 import time
11 11 import traceback
12 12
13 13 # System library imports.
14 14 import zmq
15 15 from zmq import POLLIN, POLLOUT, POLLERR
16 16 from zmq.eventloop import ioloop
17 17
18 18 # Local imports.
19 19 from IPython.utils.traitlets import HasTraits, Any, Int, Instance, Str, Type
20 20 from session import Session
21 21
22 22
23 23 class MissingHandlerError(Exception):
24 24 pass
25 25
26 26
27 27 class ZmqSocketChannel(Thread):
28 28
29 29 socket = None
30 30
31 31 def __init__(self, context, session, addr):
32 32 self.context = context
33 33 self.session = session
34 34 self.addr = addr
35 35 super(ZmqSocketChannel, self).__init__()
36 36 self.daemon = True
37 37
38 38
39 39 class SubSocketChannel(ZmqSocketChannel):
40 40
41 41 handlers = None
42 42 _overriden_call_handler = None
43 43
44 44 def __init__(self, context, session, addr):
45 45 self.handlers = {}
46 46 super(SubSocketChannel, self).__init__(context, session, addr)
47 47
48 48 def run(self):
49 49 self.socket = self.context.socket(zmq.SUB)
50 50 self.socket.setsockopt(zmq.SUBSCRIBE,'')
51 51 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
52 52 self.socket.connect('tcp://%s:%i' % self.addr)
53 53 self.ioloop = ioloop.IOLoop()
54 54 self.ioloop.add_handler(self.socket, self._handle_events,
55 55 POLLIN|POLLERR)
56 56 self.ioloop.start()
57 57
58 58 def _handle_events(self, socket, events):
59 59 # Turn on and off POLLOUT depending on if we have made a request
60 60 if events & POLLERR:
61 61 self._handle_err()
62 62 if events & POLLIN:
63 63 self._handle_recv()
64 64
65 65 def _handle_err(self):
66 66 raise zmq.ZmqError()
67 67
68 68 def _handle_recv(self):
69 69 msg = self.socket.recv_json()
70 70 self.call_handlers(msg)
71 71
72 72 def override_call_handler(self, func):
73 73 """Permanently override the call_handler.
74 74
75 75 The function func will be called as::
76 76
77 77 func(handler, msg)
78 78
79 79 And must call::
80 80
81 81 handler(msg)
82 82
83 83 in the main thread.
84 84 """
85 85 assert callable(func), "not a callable: %r" % func
86 86 self._overriden_call_handler = func
87 87
88 88 def call_handlers(self, msg):
89 89 handler = self.handlers.get(msg['msg_type'], None)
90 90 if handler is not None:
91 91 try:
92 92 self.call_handler(handler, msg)
93 93 except:
94 94 # XXX: This should be logged at least
95 95 traceback.print_last()
96 96
97 97 def call_handler(self, handler, msg):
98 98 if self._overriden_call_handler is not None:
99 99 self._overriden_call_handler(handler, msg)
100 100 elif hasattr(self, '_call_handler'):
101 101 call_handler = getattr(self, '_call_handler')
102 102 call_handler(handler, msg)
103 103 else:
104 104 raise RuntimeError('no handler!')
105 105
106 106 def add_handler(self, callback, msg_type):
107 107 """Register a callback for msg type."""
108 108 self.handlers[msg_type] = callback
109 109
110 110 def remove_handler(self, msg_type):
111 111 """Remove the callback for msg type."""
112 112 self.handlers.pop(msg_type, None)
113 113
114 114 def flush(self):
115 115 """Immediately processes all pending messages on the SUB channel. This
116 116 method is thread safe.
117 117 """
118 118 self._flushed = False
119 119 self.ioloop.add_callback(self._flush)
120 120 while not self._flushed:
121 time.sleep(0)
121 time.sleep(0.01)
122 122
123 123 def _flush(self):
124 124 """Called in this thread by the IOLoop to indicate that all events have
125 125 been processed.
126 126 """
127 127 self._flushed = True
128 128
129 129
130 130 class XReqSocketChannel(ZmqSocketChannel):
131 131
132 132 handler_queue = None
133 133 command_queue = None
134 134 handlers = None
135 135 _overriden_call_handler = None
136 136
137 137 def __init__(self, context, session, addr):
138 138 self.handlers = {}
139 139 self.handler_queue = Queue()
140 140 self.command_queue = Queue()
141 141 super(XReqSocketChannel, self).__init__(context, session, addr)
142 142
143 143 def run(self):
144 144 self.socket = self.context.socket(zmq.XREQ)
145 145 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
146 146 self.socket.connect('tcp://%s:%i' % self.addr)
147 147 self.ioloop = ioloop.IOLoop()
148 148 self.ioloop.add_handler(self.socket, self._handle_events,
149 149 POLLIN|POLLOUT|POLLERR)
150 150 self.ioloop.start()
151 151
152 152 def _handle_events(self, socket, events):
153 153 # Turn on and off POLLOUT depending on if we have made a request
154 154 if events & POLLERR:
155 155 self._handle_err()
156 156 if events & POLLOUT:
157 157 self._handle_send()
158 158 if events & POLLIN:
159 159 self._handle_recv()
160 160
161 161 def _handle_recv(self):
162 162 msg = self.socket.recv_json()
163 163 self.call_handlers(msg)
164 164
165 165 def _handle_send(self):
166 166 try:
167 167 msg = self.command_queue.get(False)
168 168 except Empty:
169 169 pass
170 170 else:
171 171 self.socket.send_json(msg)
172 172
173 173 def _handle_err(self):
174 174 raise zmq.ZmqError()
175 175
176 176 def _queue_request(self, msg, callback):
177 177 handler = self._find_handler(msg['msg_type'], callback)
178 178 self.handler_queue.put(handler)
179 179 self.command_queue.put(msg)
180 180
181 181 def execute(self, code, callback=None):
182 182 # Create class for content/msg creation. Related to, but possibly
183 183 # not in Session.
184 184 content = dict(code=code)
185 185 msg = self.session.msg('execute_request', content)
186 186 self._queue_request(msg, callback)
187 187 return msg['header']['msg_id']
188 188
189 189 def complete(self, text, line, block=None, callback=None):
190 190 content = dict(text=text, line=line)
191 191 msg = self.session.msg('complete_request', content)
192 192 self._queue_request(msg, callback)
193 193 return msg['header']['msg_id']
194 194
195 195 def object_info(self, oname, callback=None):
196 196 content = dict(oname=oname)
197 197 msg = self.session.msg('object_info_request', content)
198 198 self._queue_request(msg, callback)
199 199 return msg['header']['msg_id']
200 200
201 201 def _find_handler(self, name, callback):
202 202 if callback is not None:
203 203 return callback
204 204 handler = self.handlers.get(name)
205 205 if handler is None:
206 206 raise MissingHandlerError(
207 207 'No handler defined for method: %s' % name)
208 208 return handler
209 209
210 210 def override_call_handler(self, func):
211 211 """Permanently override the call_handler.
212 212
213 213 The function func will be called as::
214 214
215 215 func(handler, msg)
216 216
217 217 And must call::
218 218
219 219 handler(msg)
220 220
221 221 in the main thread.
222 222 """
223 223 assert callable(func), "not a callable: %r" % func
224 224 self._overriden_call_handler = func
225 225
226 226 def call_handlers(self, msg):
227 227 try:
228 228 handler = self.handler_queue.get(False)
229 229 except Empty:
230 230 print "Message received with no handler!!!"
231 231 print msg
232 232 else:
233 233 self.call_handler(handler, msg)
234 234
235 235 def call_handler(self, handler, msg):
236 236 if self._overriden_call_handler is not None:
237 237 self._overriden_call_handler(handler, msg)
238 238 elif hasattr(self, '_call_handler'):
239 239 call_handler = getattr(self, '_call_handler')
240 240 call_handler(handler, msg)
241 241 else:
242 242 raise RuntimeError('no handler!')
243 243
244 244
245 245 class RepSocketChannel(ZmqSocketChannel):
246 246
247 247 def on_raw_input():
248 248 pass
249 249
250 250
251 251 class KernelManager(HasTraits):
252 252
253 253 # The addresses to use for the various channels. Should be tuples of form
254 254 # (ip_address, port).
255 255 sub_address = Any
256 256 xreq_address = Any
257 257 rep_address = Any
258 258 # FIXME: Add Tuple to Traitlets.
259 259 #sub_address = Tuple(Str, Int)
260 260 #xreq_address = Tuple(Str, Int)
261 261 #rep_address = Tuple(Str, Int)
262 262
263 263 # The PyZMQ Context to use for communication with the kernel.
264 264 context = Instance(zmq.Context, ())
265 265
266 266 # The Session to use for communication with the kernel.
267 267 session = Instance(Session, ())
268 268
269 269 # The classes to use for the various channels.
270 270 sub_channel_class = Type(SubSocketChannel)
271 271 xreq_channel_class = Type(XReqSocketChannel)
272 272 rep_channel_class = Type(RepSocketChannel)
273 273
274 274 # Protected traits.
275 275 _sub_channel = Any
276 276 _xreq_channel = Any
277 277 _rep_channel = Any
278 278
279 279 def __init__(self, xreq_address, sub_address, rep_address, **traits):
280 280 super(KernelManager, self).__init__()
281 281
282 282 self.xreq_address = xreq_address
283 283 self.sub_address = sub_address
284 284 self.rep_address = rep_address
285 285
286 286 # FIXME: This should be the business of HasTraits. The convention is:
287 287 # HasTraits.__init__(self, **traits_to_be_initialized.)
288 288 for trait in traits:
289 289 setattr(self, trait, traits[trait])
290 290
291 291 def start_kernel(self):
292 292 """Start a localhost kernel on ip and port.
293 293
294 294 The SUB channel is for the frontend to receive messages published by
295 295 the kernel.
296 296
297 297 The REQ channel is for the frontend to make requests of the kernel.
298 298
299 299 The REP channel is for the kernel to request stdin (raw_input) from
300 300 the frontend.
301 301 """
302 302
303 303 def kill_kernel(self):
304 304 """Kill the running kernel"""
305 305
306 306 def is_alive(self):
307 307 """Is the kernel alive?"""
308 308 return True
309 309
310 310 def signal_kernel(self, signum):
311 311 """Send signum to the kernel."""
312 312
313 313 @property
314 314 def sub_channel(self):
315 315 """Get the SUB socket channel object."""
316 316 if self._sub_channel is None:
317 317 self._sub_channel = self.sub_channel_class(
318 318 self.context, self.session, self.sub_address)
319 319 return self._sub_channel
320 320
321 321 @property
322 322 def xreq_channel(self):
323 323 """Get the REQ socket channel object to make requests of the kernel."""
324 324 if self._xreq_channel is None:
325 325 self._xreq_channel = self.xreq_channel_class(
326 326 self.context, self.session, self.xreq_address)
327 327 return self._xreq_channel
328 328
329 329 @property
330 330 def rep_channel(self):
331 331 """Get the REP socket channel object to handle stdin (raw_input)."""
332 332 if self._rep_channel is None:
333 333 self._rep_channel = self.rep_channel_class(
334 334 self.context, self.session, self.rep_address)
335 335 return self._rep_channel
General Comments 0
You need to be logged in to leave comments. Login now