Skip to content

Commit

Permalink
Merge branch 'keepkernel' of https://github.com/minrk/ipython into mi…
Browse files Browse the repository at this point in the history
…nrk-keepkernel

Closes pull request ipythongh-164.
  • Loading branch information
fperez committed Oct 10, 2010
2 parents 2403858 + 5cfc7ae commit 2aa8346
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 11 deletions.
14 changes: 14 additions & 0 deletions IPython/frontend/qt/console/frontend_widget.py
Expand Up @@ -3,6 +3,7 @@
# Standard library imports
from collections import namedtuple
import sys
import time

# System library imports
from pygments.lexers import PythonLexer
Expand Down Expand Up @@ -361,6 +362,19 @@ def _handle_stream(self, msg):
self._append_plain_text(text)
self._control.moveCursor(QtGui.QTextCursor.End)

def _handle_shutdown_reply(self, msg):
""" Handle shutdown signal, only if from other console.
"""
if not self._hidden and not self._is_from_this_session(msg):
if not msg['content']['restart']:
sys.exit(0)
else:
# we just got notified of a restart!
time.sleep(0.25) # wait 1/4 sec to reset
# lest the request for a new prompt
# goes to the old kernel
self.reset()

def _started_channels(self):
""" Called when the KernelManager channels have started listening or
when the frontend is assigned an already listening KernelManager.
Expand Down
22 changes: 18 additions & 4 deletions IPython/frontend/qt/console/ipythonqt.py
Expand Up @@ -31,11 +31,18 @@ class MainWindow(QtGui.QMainWindow):
# 'object' interface
#---------------------------------------------------------------------------

def __init__(self, frontend):
def __init__(self, app, frontend, existing=False):
""" Create a MainWindow for the specified FrontendWidget.
The app is passed as an argument to allow for different
closing behavior depending on whether we are the Kernel's parent.
If existing is True, then this Window does not own the Kernel.
"""
super(MainWindow, self).__init__()
self._app = app
self._frontend = frontend
self._existing = existing
self._frontend.exit_requested.connect(self.close)
self.setCentralWidget(frontend)

Expand All @@ -50,11 +57,18 @@ def closeEvent(self, event):
if kernel_manager and kernel_manager.channels_running:
title = self.window().windowTitle()
reply = QtGui.QMessageBox.question(self, title,
'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
"Close just this console, or shutdown the kernel and close "+
"all windows attached to it?",
'Cancel', 'Close Console', 'Close All')
if reply == 2: # close All
kernel_manager.shutdown_kernel()
#kernel_manager.stop_channels()
event.accept()
elif reply == 1: # close Console
if not self._existing:
# I have the kernel: don't quit, just close the window
self._app.setQuitOnLastWindowClosed(False)
event.accept()
else:
event.ignore()

Expand Down Expand Up @@ -132,7 +146,7 @@ def main():
widget.kernel_manager = kernel_manager

# Create the main window.
window = MainWindow(widget)
window = MainWindow(app, widget, args.existing)
window.setWindowTitle('Python' if args.pure else 'IPython')
window.show()

Expand Down
3 changes: 3 additions & 0 deletions IPython/frontend/qt/kernelmanager.py
Expand Up @@ -105,6 +105,9 @@ class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):
# last-resort sys.excepthook.
crash_received = QtCore.pyqtSignal(object)

# Emitted when a shutdown is noticed.
shutdown_reply_received = QtCore.pyqtSignal(object)

#---------------------------------------------------------------------------
# 'SubSocketChannel' interface
#---------------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion IPython/zmq/ipkernel.py
Expand Up @@ -330,7 +330,7 @@ def connect_request(self, ident, parent):

def shutdown_request(self, ident, parent):
self.shell.exit_now = True
self._shutdown_message = self.session.msg(u'shutdown_reply', {}, parent)
self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
sys.exit(0)

#---------------------------------------------------------------------------
Expand Down Expand Up @@ -428,6 +428,7 @@ def _at_shutdown(self):
# io.rprint("Kernel at_shutdown") # dbg
if self._shutdown_message is not None:
self.reply_socket.send_json(self._shutdown_message)
self.pub_socket.send_json(self._shutdown_message)
io.raw_print(self._shutdown_message)
# A very short sleep to give zmq time to flush its message buffers
# before Python truly shuts down.
Expand Down
10 changes: 5 additions & 5 deletions IPython/zmq/kernelmanager.py
Expand Up @@ -305,7 +305,7 @@ def history(self, index=None, raw=False, output=True):
self._queue_request(msg)
return msg['header']['msg_id']

def shutdown(self):
def shutdown(self, restart=False):
"""Request an immediate kernel shutdown.
Upon receipt of the (empty) reply, client code can safely assume that
Expand All @@ -318,7 +318,7 @@ def shutdown(self):
"""
# Send quit message to kernel. Once we implement kernel-side setattr,
# this should probably be done that way, but for now this will do.
msg = self.session.msg('shutdown_request', {})
msg = self.session.msg('shutdown_request', {'restart':restart})
self._queue_request(msg)
return msg['header']['msg_id']

Expand Down Expand Up @@ -743,7 +743,7 @@ def start_kernel(self, **kw):
self.rep_address = (LOCALHOST, req)
self.hb_address = (LOCALHOST, hb)

def shutdown_kernel(self):
def shutdown_kernel(self, restart=False):
""" Attempts to the stop the kernel process cleanly. If the kernel
cannot be stopped, it is killed, if possible.
"""
Expand All @@ -759,7 +759,7 @@ def shutdown_kernel(self):
# Don't send any additional kernel kill messages immediately, to give
# the kernel a chance to properly execute shutdown actions. Wait for at
# most 1s, checking every 0.1s.
self.xreq_channel.shutdown()
self.xreq_channel.shutdown(restart=restart)
for i in range(10):
if self.is_alive:
time.sleep(0.1)
Expand Down Expand Up @@ -793,7 +793,7 @@ def restart_kernel(self, now=False):
if now:
self.kill_kernel()
else:
self.shutdown_kernel()
self.shutdown_kernel(restart=True)
self.start_kernel(**self._launch_args)

# FIXME: Messages get dropped in Windows due to probable ZMQ bug
Expand Down
12 changes: 11 additions & 1 deletion IPython/zmq/pykernel.py
Expand Up @@ -60,7 +60,7 @@ def __init__(self, **kwargs):

# Build dict of handlers for message types
msg_types = [ 'execute_request', 'complete_request',
'object_info_request' ]
'object_info_request', 'shutdown_request' ]
self.handlers = {}
for msg_type in msg_types:
self.handlers[msg_type] = getattr(self, msg_type)
Expand Down Expand Up @@ -163,6 +163,16 @@ def object_info_request(self, ident, parent):
object_info, parent, ident)
print >> sys.__stdout__, msg

def shutdown_request(self, ident, parent):
content = dict(parent['content'])
msg = self.session.send(self.reply_socket, 'shutdown_reply',
content, parent, ident)
msg = self.session.send(self.pub_socket, 'shutdown_reply',
content, parent, ident)
print >> sys.__stdout__, msg
time.sleep(0.1)
sys.exit(0)

#---------------------------------------------------------------------------
# Protected interface
#---------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions docs/source/development/messaging.txt
Expand Up @@ -663,11 +663,13 @@ be sent, so the content dict is empty.
Message type: ``shutdown_request``::

content = {
'restart' : bool # whether the shutdown is final, or precedes a restart
}

Message type: ``shutdown_reply``::

content = {
'restart' : bool # whether the shutdown is final, or precedes a restart
}

.. Note::
Expand Down

0 comments on commit 2aa8346

Please sign in to comment.