From 31d852fda0892f31099dd06458a6908ad0f461b6 2014-05-09 19:04:08
From: MinRK <benjaminrk@gmail.com>
Date: 2014-05-09 19:04:08
Subject: [PATCH] interrogate kernel_info to get protocol version for adaptation

---

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)