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)