diff --git a/IPython/html/base/zmqhandlers.py b/IPython/html/base/zmqhandlers.py index 99f432e..036e1dd 100644 --- a/IPython/html/base/zmqhandlers.py +++ b/IPython/html/base/zmqhandlers.py @@ -1,20 +1,7 @@ -"""Tornado handlers for WebSocket <-> ZMQ sockets. +"""Tornado handlers for WebSocket <-> ZMQ sockets.""" -Authors: - -* Brian Granger -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. try: from urllib.parse import urlparse # Py 3 @@ -37,9 +24,6 @@ from IPython.utils.py3compat import PY3, cast_unicode from .handlers import IPythonHandler -#----------------------------------------------------------------------------- -# ZMQ handlers -#----------------------------------------------------------------------------- class ZMQStreamHandler(websocket.WebSocketHandler): diff --git a/IPython/html/services/kernels/handlers.py b/IPython/html/services/kernels/handlers.py index 041ffce..b68fc52 100644 --- a/IPython/html/services/kernels/handlers.py +++ b/IPython/html/services/kernels/handlers.py @@ -1,20 +1,7 @@ -"""Tornado handlers for the notebook. +"""Tornado handlers for kernels.""" -Authors: - -* Brian Granger -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. import logging from tornado import web @@ -22,15 +9,13 @@ from tornado import web from zmq.utils import jsonapi from IPython.utils.jsonutil import date_default +from IPython.utils.py3compat import string_types from IPython.html.utils import url_path_join, url_escape from ...base.handlers import IPythonHandler, json_errors from ...base.zmqhandlers import AuthenticatedZMQStreamHandler -#----------------------------------------------------------------------------- -# Kernel handlers -#----------------------------------------------------------------------------- - +from IPython.core.release import kernel_protocol_version class MainKernelHandler(IPythonHandler): @@ -96,6 +81,34 @@ class ZMQChannelHandler(AuthenticatedZMQStreamHandler): km = self.kernel_manager meth = getattr(km, 'connect_%s' % self.channel) self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession) + self.kernel_info_channel = None + self.kernel_info_channel = km.connect_shell(self.kernel_id) + self.kernel_info_channel.on_recv(self._handle_kernel_info) + self._request_kernel_info() + + def _request_kernel_info(self): + self.log.debug("requesting kernel info") + self.session.send(self.kernel_info_channel, "kernel_info_request") + + def _handle_kernel_info(self, msg): + idents,msg = self.session.feed_identities(msg) + try: + msg = self.session.unserialize(msg) + except: + self.log.error("Bad kernel_info reply", exc_info=True) + self._request_kernel_info() + return + else: + if msg['msg_type'] != 'kernel_info_reply' or 'protocol_version' not in msg['content']: + self.log.error("Kernel info request failed, assuming current %s", msg['content']) + else: + protocol_version = msg['content']['protocol_version'] + if protocol_version != kernel_protocol_version: + self.session.adapt_version = int(protocol_version.split('.')[0]) + self.log.info("adapting kernel to %s" % protocol_version) + self.kernel_info_channel.close() + self.kernel_info_channel = None + def initialize(self, *args, **kwargs): self.zmq_stream = None diff --git a/IPython/kernel/blocking/channels.py b/IPython/kernel/blocking/channels.py index e525019..c10431d 100644 --- a/IPython/kernel/blocking/channels.py +++ b/IPython/kernel/blocking/channels.py @@ -63,7 +63,10 @@ class BlockingIOPubChannel(BlockingChannelMixin, IOPubChannel): class BlockingShellChannel(BlockingChannelMixin, ShellChannel): - pass + def call_handlers(self, msg): + if msg['msg_type'] == 'kernel_info_reply': + self._handle_kernel_info_reply(msg) + return super(BlockingShellChannel, self).call_handlers(msg) class BlockingStdInChannel(BlockingChannelMixin, StdInChannel): diff --git a/IPython/kernel/channels.py b/IPython/kernel/channels.py index 4d875ba..24c8b3f 100644 --- a/IPython/kernel/channels.py +++ b/IPython/kernel/channels.py @@ -16,7 +16,8 @@ import zmq from zmq import ZMQError from zmq.eventloop import ioloop, zmqstream -# Local imports +from IPython.core.release import kernel_protocol_version_info + from .channelsabc import ( ShellChannelABC, IOPubChannelABC, HBChannelABC, StdInChannelABC, @@ -27,6 +28,8 @@ from IPython.utils.py3compat import string_types, iteritems # Constants and exceptions #----------------------------------------------------------------------------- +major_protocol_version = kernel_protocol_version_info[0] + class InvalidPortNumber(Exception): pass @@ -173,7 +176,8 @@ class ZMQSocketChannel(Thread): Unpacks message, and calls handlers with it. """ ident,smsg = self.session.feed_identities(msg) - self.call_handlers(self.session.unserialize(smsg)) + msg = self.session.unserialize(smsg) + self.call_handlers(msg) @@ -195,7 +199,7 @@ class ShellChannel(ZMQSocketChannel): def __init__(self, context, session, address): super(ShellChannel, self).__init__(context, session, address) self.ioloop = ioloop.IOLoop() - + def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.DEALER) @@ -365,6 +369,15 @@ class ShellChannel(ZMQSocketChannel): msg = self.session.msg('kernel_info_request') self._queue_send(msg) return msg['header']['msg_id'] + + def _handle_kernel_info_reply(self, msg): + """handle kernel info reply + + sets protocol adaptation version + """ + adapt_version = int(msg['content']['protocol_version'].split('.')[0]) + if adapt_version != major_protocol_version: + self.session.adapt_version = adapt_version def shutdown(self, restart=False): """Request an immediate kernel shutdown. diff --git a/IPython/qt/kernel_mixins.py b/IPython/qt/kernel_mixins.py index 667aa0e..9fb659a 100644 --- a/IPython/qt/kernel_mixins.py +++ b/IPython/qt/kernel_mixins.py @@ -59,6 +59,7 @@ class QtShellChannelMixin(ChannelQObject): complete_reply = QtCore.Signal(object) inspect_reply = QtCore.Signal(object) history_reply = QtCore.Signal(object) + kernel_info_reply = QtCore.Signal(object) #--------------------------------------------------------------------------- # 'ShellChannel' interface @@ -72,6 +73,9 @@ class QtShellChannelMixin(ChannelQObject): # Emit signals for specialized message types. msg_type = msg['header']['msg_type'] + if msg_type == 'kernel_info_reply': + self._handle_kernel_info_reply(msg) + signal = getattr(self, msg_type, None) if signal: signal.emit(msg)