Show More
@@ -32,7 +32,8 class BaseFrontendMixin(object): | |||||
32 | old_manager.sub_channel.message_received.disconnect(self._dispatch) |
|
32 | old_manager.sub_channel.message_received.disconnect(self._dispatch) | |
33 | old_manager.xreq_channel.message_received.disconnect(self._dispatch) |
|
33 | old_manager.xreq_channel.message_received.disconnect(self._dispatch) | |
34 | old_manager.rep_channel.message_received.disconnect(self._dispatch) |
|
34 | old_manager.rep_channel.message_received.disconnect(self._dispatch) | |
35 |
old_manager.hb_channel.kernel_died.disconnect( |
|
35 | old_manager.hb_channel.kernel_died.disconnect( | |
|
36 | self._handle_kernel_died) | |||
36 |
|
37 | |||
37 | # Handle the case where the old kernel manager is still listening. |
|
38 | # Handle the case where the old kernel manager is still listening. | |
38 | if old_manager.channels_running: |
|
39 | if old_manager.channels_running: | |
@@ -63,6 +64,19 class BaseFrontendMixin(object): | |||||
63 | #--------------------------------------------------------------------------- |
|
64 | #--------------------------------------------------------------------------- | |
64 | # 'BaseFrontendMixin' abstract interface |
|
65 | # 'BaseFrontendMixin' abstract interface | |
65 | #--------------------------------------------------------------------------- |
|
66 | #--------------------------------------------------------------------------- | |
|
67 | ||||
|
68 | def _handle_kernel_died(self, since_last_heartbeat): | |||
|
69 | """ This is called when the ``kernel_died`` signal is emitted. | |||
|
70 | ||||
|
71 | This method is called when the kernel heartbeat has not been | |||
|
72 | active for a certain amount of time. The typical action will be to | |||
|
73 | give the user the option of restarting the kernel. | |||
|
74 | ||||
|
75 | Parameters | |||
|
76 | ---------- | |||
|
77 | since_last_heartbeat : float | |||
|
78 | The time since the heartbeat was last received. | |||
|
79 | """ | |||
66 |
|
80 | |||
67 | def _started_channels(self): |
|
81 | def _started_channels(self): | |
68 | """ Called when the KernelManager channels have started listening or |
|
82 | """ Called when the KernelManager channels have started listening or | |
@@ -93,17 +107,3 class BaseFrontendMixin(object): | |||||
93 | """ |
|
107 | """ | |
94 | session = self._kernel_manager.session.session |
|
108 | session = self._kernel_manager.session.session | |
95 | return msg['parent_header']['session'] == session |
|
109 | return msg['parent_header']['session'] == session | |
96 |
|
||||
97 | def _handle_kernel_died(self, since_last_heartbeat): |
|
|||
98 | """ This is called when the ``kernel_died`` signal is emitted. |
|
|||
99 |
|
||||
100 | This method is called when the kernel heartbeat has not been |
|
|||
101 | active for a certain amount of time. The typical action will be to |
|
|||
102 | give the user the option of restarting the kernel. |
|
|||
103 |
|
||||
104 | Parameters |
|
|||
105 | ---------- |
|
|||
106 | since_last_heartbeat : float |
|
|||
107 | The time since the heartbeat was last received. |
|
|||
108 | """ |
|
|||
109 | pass |
|
@@ -79,9 +79,10 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
79 | custom_interrupt = Bool(False) |
|
79 | custom_interrupt = Bool(False) | |
80 | custom_interrupt_requested = QtCore.pyqtSignal() |
|
80 | custom_interrupt_requested = QtCore.pyqtSignal() | |
81 |
|
81 | |||
82 | # An option and corresponding signal for overriding the default kernel |
|
82 | # An option and corresponding signals for overriding the default kernel | |
83 | # restart behavior. |
|
83 | # restart behavior. | |
84 | custom_restart = Bool(False) |
|
84 | custom_restart = Bool(False) | |
|
85 | custom_restart_kernel_died = QtCore.pyqtSignal(float) | |||
85 | custom_restart_requested = QtCore.pyqtSignal() |
|
86 | custom_restart_requested = QtCore.pyqtSignal() | |
86 |
|
87 | |||
87 | # Emitted when an 'execute_reply' has been received from the kernel and |
|
88 | # Emitted when an 'execute_reply' has been received from the kernel and | |
@@ -90,7 +91,6 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
90 |
|
91 | |||
91 | # Protected class variables. |
|
92 | # Protected class variables. | |
92 | _input_splitter_class = InputSplitter |
|
93 | _input_splitter_class = InputSplitter | |
93 | _possible_kernel_restart = Bool(False) |
|
|||
94 |
|
94 | |||
95 | #--------------------------------------------------------------------------- |
|
95 | #--------------------------------------------------------------------------- | |
96 | # 'object' interface |
|
96 | # 'object' interface | |
@@ -107,6 +107,7 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
107 | self._highlighter = FrontendHighlighter(self) |
|
107 | self._highlighter = FrontendHighlighter(self) | |
108 | self._input_splitter = self._input_splitter_class(input_mode='block') |
|
108 | self._input_splitter = self._input_splitter_class(input_mode='block') | |
109 | self._kernel_manager = None |
|
109 | self._kernel_manager = None | |
|
110 | self._possible_kernel_restart = False | |||
110 |
|
111 | |||
111 | # Configure the ConsoleWidget. |
|
112 | # Configure the ConsoleWidget. | |
112 | self.tab_width = 4 |
|
113 | self.tab_width = 4 | |
@@ -174,11 +175,11 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
174 | key = event.key() |
|
175 | key = event.key() | |
175 | if self._control_key_down(event.modifiers()): |
|
176 | if self._control_key_down(event.modifiers()): | |
176 | if key == QtCore.Qt.Key_C and self._executing: |
|
177 | if key == QtCore.Qt.Key_C and self._executing: | |
177 |
self. |
|
178 | self.interrupt_kernel() | |
178 | return True |
|
179 | return True_ | |
179 | elif key == QtCore.Qt.Key_Period: |
|
180 | elif key == QtCore.Qt.Key_Period: | |
180 | message = 'Are you sure you want to restart the kernel?' |
|
181 | message = 'Are you sure you want to restart the kernel?' | |
181 |
self._kernel |
|
182 | self.restart_kernel(message) | |
182 | return True |
|
183 | return True | |
183 | return super(FrontendWidget, self)._event_filter_console_keypress(event) |
|
184 | return super(FrontendWidget, self)._event_filter_console_keypress(event) | |
184 |
|
185 | |||
@@ -238,6 +239,18 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
238 | self.kernel_manager.rep_channel.input(line) |
|
239 | self.kernel_manager.rep_channel.input(line) | |
239 | self._readline(msg['content']['prompt'], callback=callback) |
|
240 | self._readline(msg['content']['prompt'], callback=callback) | |
240 |
|
241 | |||
|
242 | def _handle_kernel_died(self, since_last_heartbeat): | |||
|
243 | """ Handle the kernel's death by asking if the user wants to restart. | |||
|
244 | """ | |||
|
245 | message = 'The kernel heartbeat has been inactive for %.2f ' \ | |||
|
246 | 'seconds. Do you want to restart the kernel? You may ' \ | |||
|
247 | 'first want to check the network connection.' % \ | |||
|
248 | since_last_heartbeat | |||
|
249 | if self.custom_restart: | |||
|
250 | self.custom_restart_kernel_died.emit(since_last_heartbeat) | |||
|
251 | else: | |||
|
252 | self.restart_kernel(message) | |||
|
253 | ||||
241 | def _handle_object_info_reply(self, rep): |
|
254 | def _handle_object_info_reply(self, rep): | |
242 | """ Handle replies for call tips. |
|
255 | """ Handle replies for call tips. | |
243 | """ |
|
256 | """ | |
@@ -286,6 +299,50 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
286 | """ |
|
299 | """ | |
287 | self.execute('execfile("%s")' % path, hidden=hidden) |
|
300 | self.execute('execfile("%s")' % path, hidden=hidden) | |
288 |
|
301 | |||
|
302 | def interrupt_kernel(self): | |||
|
303 | """ Attempts to interrupt the running kernel. | |||
|
304 | """ | |||
|
305 | if self.custom_interrupt: | |||
|
306 | self.custom_interrupt_requested.emit() | |||
|
307 | elif self.kernel_manager.has_kernel: | |||
|
308 | self.kernel_manager.signal_kernel(signal.SIGINT) | |||
|
309 | else: | |||
|
310 | self._append_plain_text('Kernel process is either remote or ' | |||
|
311 | 'unspecified. Cannot interrupt.\n') | |||
|
312 | ||||
|
313 | def restart_kernel(self, message): | |||
|
314 | """ Attempts to restart the running kernel. | |||
|
315 | """ | |||
|
316 | # We want to make sure that if this dialog is already happening, that | |||
|
317 | # other signals don't trigger it again. This can happen when the | |||
|
318 | # kernel_died heartbeat signal is emitted and the user is slow to | |||
|
319 | # respond to the dialog. | |||
|
320 | if not self._possible_kernel_restart: | |||
|
321 | if self.custom_restart: | |||
|
322 | self.custom_restart_requested.emit() | |||
|
323 | elif self.kernel_manager.has_kernel: | |||
|
324 | # Setting this to True will prevent this logic from happening | |||
|
325 | # again until the current pass is completed. | |||
|
326 | self._possible_kernel_restart = True | |||
|
327 | buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | |||
|
328 | result = QtGui.QMessageBox.question(self, 'Restart kernel?', | |||
|
329 | message, buttons) | |||
|
330 | if result == QtGui.QMessageBox.Yes: | |||
|
331 | try: | |||
|
332 | self.kernel_manager.restart_kernel() | |||
|
333 | except RuntimeError: | |||
|
334 | message = 'Kernel started externally. Cannot restart.\n' | |||
|
335 | self._append_plain_text(message) | |||
|
336 | else: | |||
|
337 | self._stopped_channels() | |||
|
338 | self._append_plain_text('Kernel restarting...\n') | |||
|
339 | self._show_interpreter_prompt() | |||
|
340 | # This might need to be moved to another location? | |||
|
341 | self._possible_kernel_restart = False | |||
|
342 | else: | |||
|
343 | self._append_plain_text('Kernel process is either remote or ' | |||
|
344 | 'unspecified. Cannot restart.\n') | |||
|
345 | ||||
289 | #--------------------------------------------------------------------------- |
|
346 | #--------------------------------------------------------------------------- | |
290 | # 'FrontendWidget' protected interface |
|
347 | # 'FrontendWidget' protected interface | |
291 | #--------------------------------------------------------------------------- |
|
348 | #--------------------------------------------------------------------------- | |
@@ -339,50 +396,6 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
339 | text = str(cursor.selection().toPlainText()) |
|
396 | text = str(cursor.selection().toPlainText()) | |
340 | return self._completion_lexer.get_context(text) |
|
397 | return self._completion_lexer.get_context(text) | |
341 |
|
398 | |||
342 | def _kernel_interrupt(self): |
|
|||
343 | """ Attempts to interrupt the running kernel. |
|
|||
344 | """ |
|
|||
345 | if self.custom_interrupt: |
|
|||
346 | self.custom_interrupt_requested.emit() |
|
|||
347 | elif self.kernel_manager.has_kernel: |
|
|||
348 | self.kernel_manager.signal_kernel(signal.SIGINT) |
|
|||
349 | else: |
|
|||
350 | self._append_plain_text('Kernel process is either remote or ' |
|
|||
351 | 'unspecified. Cannot interrupt.\n') |
|
|||
352 |
|
||||
353 | def _kernel_restart(self, message): |
|
|||
354 | """ Attempts to restart the running kernel. |
|
|||
355 | """ |
|
|||
356 | # We want to make sure that if this dialog is already happening, that |
|
|||
357 | # other signals don't trigger it again. This can happen when the |
|
|||
358 | # kernel_died heartbeat signal is emitted and the user is slow to |
|
|||
359 | # respond to the dialog. |
|
|||
360 | if not self._possible_kernel_restart: |
|
|||
361 | if self.custom_restart: |
|
|||
362 | self.custom_restart_requested.emit() |
|
|||
363 | elif self.kernel_manager.has_kernel: |
|
|||
364 | # Setting this to True will prevent this logic from happening |
|
|||
365 | # again until the current pass is completed. |
|
|||
366 | self._possible_kernel_restart = True |
|
|||
367 | buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No |
|
|||
368 | result = QtGui.QMessageBox.question(self, 'Restart kernel?', |
|
|||
369 | message, buttons) |
|
|||
370 | if result == QtGui.QMessageBox.Yes: |
|
|||
371 | try: |
|
|||
372 | self.kernel_manager.restart_kernel() |
|
|||
373 | except RuntimeError: |
|
|||
374 | message = 'Kernel started externally. Cannot restart.\n' |
|
|||
375 | self._append_plain_text(message) |
|
|||
376 | else: |
|
|||
377 | self._stopped_channels() |
|
|||
378 | self._append_plain_text('Kernel restarting...\n') |
|
|||
379 | self._show_interpreter_prompt() |
|
|||
380 | # This might need to be moved to another location? |
|
|||
381 | self._possible_kernel_restart = False |
|
|||
382 | else: |
|
|||
383 | self._append_plain_text('Kernel process is either remote or ' |
|
|||
384 | 'unspecified. Cannot restart.\n') |
|
|||
385 |
|
||||
386 | def _process_execute_abort(self, msg): |
|
399 | def _process_execute_abort(self, msg): | |
387 | """ Process a reply for an aborted execution request. |
|
400 | """ Process a reply for an aborted execution request. | |
388 | """ |
|
401 | """ |
@@ -154,14 +154,6 class IPythonWidget(FrontendWidget): | |||||
154 | # FIXME: Disabled until history requests are properly implemented. |
|
154 | # FIXME: Disabled until history requests are properly implemented. | |
155 | #self.kernel_manager.xreq_channel.history(raw=True, output=False) |
|
155 | #self.kernel_manager.xreq_channel.history(raw=True, output=False) | |
156 |
|
156 | |||
157 | def _handle_kernel_died(self, since_last_heartbeat): |
|
|||
158 | """ Handle the kernel's death by asking if the user wants to restart. |
|
|||
159 | """ |
|
|||
160 | message = 'The kernel heartbeat has been inactive for %.2f ' \ |
|
|||
161 | 'seconds. Do you want to restart the kernel? You may ' \ |
|
|||
162 | 'first want to check the network connection.' % since_last_heartbeat |
|
|||
163 | self._kernel_restart(message) |
|
|||
164 |
|
||||
165 | #--------------------------------------------------------------------------- |
|
157 | #--------------------------------------------------------------------------- | |
166 | # 'FrontendWidget' interface |
|
158 | # 'FrontendWidget' interface | |
167 | #--------------------------------------------------------------------------- |
|
159 | #--------------------------------------------------------------------------- |
@@ -92,7 +92,6 def make_kernel(namespace, kernel_factory, | |||||
92 |
|
92 | |||
93 | # Redirect input streams and set a display hook. |
|
93 | # Redirect input streams and set a display hook. | |
94 | if out_stream_factory: |
|
94 | if out_stream_factory: | |
95 | pass |
|
|||
96 | sys.stdout = out_stream_factory(session, pub_socket, u'stdout') |
|
95 | sys.stdout = out_stream_factory(session, pub_socket, u'stdout') | |
97 | sys.stderr = out_stream_factory(session, pub_socket, u'stderr') |
|
96 | sys.stderr = out_stream_factory(session, pub_socket, u'stderr') | |
98 | if display_hook_factory: |
|
97 | if display_hook_factory: | |
@@ -203,7 +202,7 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0, | |||||
203 | DUPLICATE_SAME_ACCESS |
|
202 | DUPLICATE_SAME_ACCESS | |
204 | pid = GetCurrentProcess() |
|
203 | pid = GetCurrentProcess() | |
205 | handle = DuplicateHandle(pid, pid, pid, 0, |
|
204 | handle = DuplicateHandle(pid, pid, pid, 0, | |
206 |
True, # Inheritable by new |
|
205 | True, # Inheritable by new processes. | |
207 | DUPLICATE_SAME_ACCESS) |
|
206 | DUPLICATE_SAME_ACCESS) | |
208 | proc = Popen(arguments + ['--parent', str(int(handle))]) |
|
207 | proc = Popen(arguments + ['--parent', str(int(handle))]) | |
209 | else: |
|
208 | else: |
@@ -437,7 +437,7 given, the GUI backend is matplotlib's, otherwise use one of: \ | |||||
437 | _kernel_classes = { |
|
437 | _kernel_classes = { | |
438 | 'qt' : QtKernel, |
|
438 | 'qt' : QtKernel, | |
439 | 'qt4' : QtKernel, |
|
439 | 'qt4' : QtKernel, | |
440 | 'payload-svg':Kernel, |
|
440 | 'payload-svg': Kernel, | |
441 | 'wx' : WxKernel, |
|
441 | 'wx' : WxKernel, | |
442 | 'tk' : TkKernel |
|
442 | 'tk' : TkKernel | |
443 | } |
|
443 | } |
@@ -227,7 +227,8 class Kernel(HasTraits): | |||||
227 | # Kernel main and launch functions |
|
227 | # Kernel main and launch functions | |
228 | #----------------------------------------------------------------------------- |
|
228 | #----------------------------------------------------------------------------- | |
229 |
|
229 | |||
230 |
def launch_kernel(xrep_port=0, pub_port=0, req_port=0, |
|
230 | def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0, | |
|
231 | independent=False): | |||
231 | """ Launches a localhost kernel, binding to the specified ports. |
|
232 | """ Launches a localhost kernel, binding to the specified ports. | |
232 |
|
233 | |||
233 | Parameters |
|
234 | Parameters | |
@@ -241,6 +242,9 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False): | |||||
241 | req_port : int, optional |
|
242 | req_port : int, optional | |
242 | The port to use for the REQ (raw input) channel. |
|
243 | The port to use for the REQ (raw input) channel. | |
243 |
|
244 | |||
|
245 | hb_port : int, optional | |||
|
246 | The port to use for the hearbeat REP channel. | |||
|
247 | ||||
244 | independent : bool, optional (default False) |
|
248 | independent : bool, optional (default False) | |
245 | If set, the kernel process is guaranteed to survive if this process |
|
249 | If set, the kernel process is guaranteed to survive if this process | |
246 | dies. If not set, an effort is made to ensure that the kernel is killed |
|
250 | dies. If not set, an effort is made to ensure that the kernel is killed | |
@@ -254,7 +258,8 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False): | |||||
254 | where kernel_process is a Popen object and the ports are integers. |
|
258 | where kernel_process is a Popen object and the ports are integers. | |
255 | """ |
|
259 | """ | |
256 | return base_launch_kernel('from IPython.zmq.pykernel import main; main()', |
|
260 | return base_launch_kernel('from IPython.zmq.pykernel import main; main()', | |
257 |
xrep_port, pub_port, req_port, |
|
261 | xrep_port, pub_port, req_port, hb_port, | |
|
262 | independent) | |||
258 |
|
263 | |||
259 | main = make_default_main(Kernel) |
|
264 | main = make_default_main(Kernel) | |
260 |
|
265 |
General Comments 0
You need to be logged in to leave comments.
Login now