Show More
@@ -3,6 +3,7 b' from __future__ import print_function' | |||||
3 | # Standard library imports |
|
3 | # Standard library imports | |
4 | from collections import namedtuple |
|
4 | from collections import namedtuple | |
5 | import sys |
|
5 | import sys | |
|
6 | import time | |||
6 |
|
7 | |||
7 | # System library imports |
|
8 | # System library imports | |
8 | from pygments.lexers import PythonLexer |
|
9 | from pygments.lexers import PythonLexer | |
@@ -361,6 +362,19 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
361 | self._append_plain_text(text) |
|
362 | self._append_plain_text(text) | |
362 | self._control.moveCursor(QtGui.QTextCursor.End) |
|
363 | self._control.moveCursor(QtGui.QTextCursor.End) | |
363 |
|
364 | |||
|
365 | def _handle_shutdown_reply(self, msg): | |||
|
366 | """ Handle shutdown signal, only if from other console. | |||
|
367 | """ | |||
|
368 | if not self._hidden and not self._is_from_this_session(msg): | |||
|
369 | if not msg['content']['restart']: | |||
|
370 | sys.exit(0) | |||
|
371 | else: | |||
|
372 | # we just got notified of a restart! | |||
|
373 | time.sleep(0.25) # wait 1/4 sec to reset | |||
|
374 | # lest the request for a new prompt | |||
|
375 | # goes to the old kernel | |||
|
376 | self.reset() | |||
|
377 | ||||
364 | def _started_channels(self): |
|
378 | def _started_channels(self): | |
365 | """ Called when the KernelManager channels have started listening or |
|
379 | """ Called when the KernelManager channels have started listening or | |
366 | when the frontend is assigned an already listening KernelManager. |
|
380 | when the frontend is assigned an already listening KernelManager. |
@@ -31,11 +31,18 b' class MainWindow(QtGui.QMainWindow):' | |||||
31 | # 'object' interface |
|
31 | # 'object' interface | |
32 | #--------------------------------------------------------------------------- |
|
32 | #--------------------------------------------------------------------------- | |
33 |
|
33 | |||
34 | def __init__(self, frontend): |
|
34 | def __init__(self, app, frontend, existing=False): | |
35 | """ Create a MainWindow for the specified FrontendWidget. |
|
35 | """ Create a MainWindow for the specified FrontendWidget. | |
|
36 | ||||
|
37 | The app is passed as an argument to allow for different | |||
|
38 | closing behavior depending on whether we are the Kernel's parent. | |||
|
39 | ||||
|
40 | If existing is True, then this Window does not own the Kernel. | |||
36 | """ |
|
41 | """ | |
37 | super(MainWindow, self).__init__() |
|
42 | super(MainWindow, self).__init__() | |
|
43 | self._app = app | |||
38 | self._frontend = frontend |
|
44 | self._frontend = frontend | |
|
45 | self._existing = existing | |||
39 | self._frontend.exit_requested.connect(self.close) |
|
46 | self._frontend.exit_requested.connect(self.close) | |
40 | self.setCentralWidget(frontend) |
|
47 | self.setCentralWidget(frontend) | |
41 |
|
48 | |||
@@ -50,11 +57,18 b' class MainWindow(QtGui.QMainWindow):' | |||||
50 | if kernel_manager and kernel_manager.channels_running: |
|
57 | if kernel_manager and kernel_manager.channels_running: | |
51 | title = self.window().windowTitle() |
|
58 | title = self.window().windowTitle() | |
52 | reply = QtGui.QMessageBox.question(self, title, |
|
59 | reply = QtGui.QMessageBox.question(self, title, | |
53 | 'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) |
|
60 | "Close just this console, or shutdown the kernel and close "+ | |
54 | if reply == QtGui.QMessageBox.Yes: |
|
61 | "all windows attached to it?", | |
|
62 | 'Cancel', 'Close Console', 'Close All') | |||
|
63 | if reply == 2: # close All | |||
55 | kernel_manager.shutdown_kernel() |
|
64 | kernel_manager.shutdown_kernel() | |
56 | #kernel_manager.stop_channels() |
|
65 | #kernel_manager.stop_channels() | |
57 | event.accept() |
|
66 | event.accept() | |
|
67 | elif reply == 1: # close Console | |||
|
68 | if not self._existing: | |||
|
69 | # I have the kernel: don't quit, just close the window | |||
|
70 | self._app.setQuitOnLastWindowClosed(False) | |||
|
71 | event.accept() | |||
58 | else: |
|
72 | else: | |
59 | event.ignore() |
|
73 | event.ignore() | |
60 |
|
74 | |||
@@ -132,7 +146,7 b' def main():' | |||||
132 | widget.kernel_manager = kernel_manager |
|
146 | widget.kernel_manager = kernel_manager | |
133 |
|
147 | |||
134 | # Create the main window. |
|
148 | # Create the main window. | |
135 | window = MainWindow(widget) |
|
149 | window = MainWindow(app, widget, args.existing) | |
136 | window.setWindowTitle('Python' if args.pure else 'IPython') |
|
150 | window.setWindowTitle('Python' if args.pure else 'IPython') | |
137 | window.show() |
|
151 | window.show() | |
138 |
|
152 |
@@ -105,6 +105,9 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):' | |||||
105 | # last-resort sys.excepthook. |
|
105 | # last-resort sys.excepthook. | |
106 | crash_received = QtCore.pyqtSignal(object) |
|
106 | crash_received = QtCore.pyqtSignal(object) | |
107 |
|
107 | |||
|
108 | # Emitted when a shutdown is noticed. | |||
|
109 | shutdown_reply_received = QtCore.pyqtSignal(object) | |||
|
110 | ||||
108 | #--------------------------------------------------------------------------- |
|
111 | #--------------------------------------------------------------------------- | |
109 | # 'SubSocketChannel' interface |
|
112 | # 'SubSocketChannel' interface | |
110 | #--------------------------------------------------------------------------- |
|
113 | #--------------------------------------------------------------------------- |
@@ -330,7 +330,7 b' class Kernel(Configurable):' | |||||
330 |
|
330 | |||
331 | def shutdown_request(self, ident, parent): |
|
331 | def shutdown_request(self, ident, parent): | |
332 | self.shell.exit_now = True |
|
332 | self.shell.exit_now = True | |
333 |
self._shutdown_message = self.session.msg(u'shutdown_reply', |
|
333 | self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent) | |
334 | sys.exit(0) |
|
334 | sys.exit(0) | |
335 |
|
335 | |||
336 | #--------------------------------------------------------------------------- |
|
336 | #--------------------------------------------------------------------------- | |
@@ -428,6 +428,7 b' class Kernel(Configurable):' | |||||
428 | # io.rprint("Kernel at_shutdown") # dbg |
|
428 | # io.rprint("Kernel at_shutdown") # dbg | |
429 | if self._shutdown_message is not None: |
|
429 | if self._shutdown_message is not None: | |
430 | self.reply_socket.send_json(self._shutdown_message) |
|
430 | self.reply_socket.send_json(self._shutdown_message) | |
|
431 | self.pub_socket.send_json(self._shutdown_message) | |||
431 | io.raw_print(self._shutdown_message) |
|
432 | io.raw_print(self._shutdown_message) | |
432 | # A very short sleep to give zmq time to flush its message buffers |
|
433 | # A very short sleep to give zmq time to flush its message buffers | |
433 | # before Python truly shuts down. |
|
434 | # before Python truly shuts down. |
@@ -305,7 +305,7 b' class XReqSocketChannel(ZmqSocketChannel):' | |||||
305 | self._queue_request(msg) |
|
305 | self._queue_request(msg) | |
306 | return msg['header']['msg_id'] |
|
306 | return msg['header']['msg_id'] | |
307 |
|
307 | |||
308 | def shutdown(self): |
|
308 | def shutdown(self, restart=False): | |
309 | """Request an immediate kernel shutdown. |
|
309 | """Request an immediate kernel shutdown. | |
310 |
|
310 | |||
311 | Upon receipt of the (empty) reply, client code can safely assume that |
|
311 | Upon receipt of the (empty) reply, client code can safely assume that | |
@@ -318,7 +318,7 b' class XReqSocketChannel(ZmqSocketChannel):' | |||||
318 | """ |
|
318 | """ | |
319 | # Send quit message to kernel. Once we implement kernel-side setattr, |
|
319 | # Send quit message to kernel. Once we implement kernel-side setattr, | |
320 | # this should probably be done that way, but for now this will do. |
|
320 | # this should probably be done that way, but for now this will do. | |
321 | msg = self.session.msg('shutdown_request', {}) |
|
321 | msg = self.session.msg('shutdown_request', {'restart':restart}) | |
322 | self._queue_request(msg) |
|
322 | self._queue_request(msg) | |
323 | return msg['header']['msg_id'] |
|
323 | return msg['header']['msg_id'] | |
324 |
|
324 | |||
@@ -743,7 +743,7 b' class KernelManager(HasTraits):' | |||||
743 | self.rep_address = (LOCALHOST, req) |
|
743 | self.rep_address = (LOCALHOST, req) | |
744 | self.hb_address = (LOCALHOST, hb) |
|
744 | self.hb_address = (LOCALHOST, hb) | |
745 |
|
745 | |||
746 | def shutdown_kernel(self): |
|
746 | def shutdown_kernel(self, restart=False): | |
747 | """ Attempts to the stop the kernel process cleanly. If the kernel |
|
747 | """ Attempts to the stop the kernel process cleanly. If the kernel | |
748 | cannot be stopped, it is killed, if possible. |
|
748 | cannot be stopped, it is killed, if possible. | |
749 | """ |
|
749 | """ | |
@@ -759,7 +759,7 b' class KernelManager(HasTraits):' | |||||
759 | # Don't send any additional kernel kill messages immediately, to give |
|
759 | # Don't send any additional kernel kill messages immediately, to give | |
760 | # the kernel a chance to properly execute shutdown actions. Wait for at |
|
760 | # the kernel a chance to properly execute shutdown actions. Wait for at | |
761 | # most 1s, checking every 0.1s. |
|
761 | # most 1s, checking every 0.1s. | |
762 | self.xreq_channel.shutdown() |
|
762 | self.xreq_channel.shutdown(restart=restart) | |
763 | for i in range(10): |
|
763 | for i in range(10): | |
764 | if self.is_alive: |
|
764 | if self.is_alive: | |
765 | time.sleep(0.1) |
|
765 | time.sleep(0.1) | |
@@ -793,7 +793,7 b' class KernelManager(HasTraits):' | |||||
793 | if now: |
|
793 | if now: | |
794 | self.kill_kernel() |
|
794 | self.kill_kernel() | |
795 | else: |
|
795 | else: | |
796 | self.shutdown_kernel() |
|
796 | self.shutdown_kernel(restart=True) | |
797 | self.start_kernel(**self._launch_args) |
|
797 | self.start_kernel(**self._launch_args) | |
798 |
|
798 | |||
799 | # FIXME: Messages get dropped in Windows due to probable ZMQ bug |
|
799 | # FIXME: Messages get dropped in Windows due to probable ZMQ bug |
@@ -60,7 +60,7 b' class Kernel(HasTraits):' | |||||
60 |
|
60 | |||
61 | # Build dict of handlers for message types |
|
61 | # Build dict of handlers for message types | |
62 | msg_types = [ 'execute_request', 'complete_request', |
|
62 | msg_types = [ 'execute_request', 'complete_request', | |
63 | 'object_info_request' ] |
|
63 | 'object_info_request', 'shutdown_request' ] | |
64 | self.handlers = {} |
|
64 | self.handlers = {} | |
65 | for msg_type in msg_types: |
|
65 | for msg_type in msg_types: | |
66 | self.handlers[msg_type] = getattr(self, msg_type) |
|
66 | self.handlers[msg_type] = getattr(self, msg_type) | |
@@ -163,6 +163,16 b' class Kernel(HasTraits):' | |||||
163 | object_info, parent, ident) |
|
163 | object_info, parent, ident) | |
164 | print >> sys.__stdout__, msg |
|
164 | print >> sys.__stdout__, msg | |
165 |
|
165 | |||
|
166 | def shutdown_request(self, ident, parent): | |||
|
167 | content = dict(parent['content']) | |||
|
168 | msg = self.session.send(self.reply_socket, 'shutdown_reply', | |||
|
169 | content, parent, ident) | |||
|
170 | msg = self.session.send(self.pub_socket, 'shutdown_reply', | |||
|
171 | content, parent, ident) | |||
|
172 | print >> sys.__stdout__, msg | |||
|
173 | time.sleep(0.1) | |||
|
174 | sys.exit(0) | |||
|
175 | ||||
166 | #--------------------------------------------------------------------------- |
|
176 | #--------------------------------------------------------------------------- | |
167 | # Protected interface |
|
177 | # Protected interface | |
168 | #--------------------------------------------------------------------------- |
|
178 | #--------------------------------------------------------------------------- |
@@ -663,11 +663,13 b' be sent, so the content dict is empty.' | |||||
663 | Message type: ``shutdown_request``:: |
|
663 | Message type: ``shutdown_request``:: | |
664 |
|
664 | |||
665 | content = { |
|
665 | content = { | |
|
666 | 'restart' : bool # whether the shutdown is final, or precedes a restart | |||
666 | } |
|
667 | } | |
667 |
|
668 | |||
668 | Message type: ``shutdown_reply``:: |
|
669 | Message type: ``shutdown_reply``:: | |
669 |
|
670 | |||
670 | content = { |
|
671 | content = { | |
|
672 | 'restart' : bool # whether the shutdown is final, or precedes a restart | |||
671 | } |
|
673 | } | |
672 |
|
674 | |||
673 | .. Note:: |
|
675 | .. Note:: |
General Comments 0
You need to be logged in to leave comments.
Login now