From 1c5293bd6c010f185205e3d566b262b1ac7cb0f7 2014-12-01 22:17:59
From: Thomas Kluyver <takowl@gmail.com>
Date: 2014-12-01 22:17:59
Subject: [PATCH] Merge pull request #6945 from minrk/kernel-info-lang

fix loading of language name from kernel_info
---

diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js
index f3f1c64..09844a9 100644
--- a/IPython/html/static/notebook/js/notebook.js
+++ b/IPython/html/static/notebook/js/notebook.js
@@ -250,11 +250,9 @@ define([
         this.events.on('kernel_ready.Kernel', function(event, data) {
             var kinfo = data.kernel.info_reply;
             var langinfo = kinfo.language_info || {};
-            if (!langinfo.name)  langinfo.name = kinfo.language;
-                
             that.metadata.language_info = langinfo;
             // Mode 'null' should be plain, unhighlighted text.
-            var cm_mode = langinfo.codemirror_mode || langinfo.language || 'null';
+            var cm_mode = langinfo.codemirror_mode || langinfo.name || 'null';
             that.set_codemirror_mode(cm_mode);
         });
 
@@ -1861,7 +1859,7 @@ define([
         if (this.metadata.language_info !== undefined) {
             var langinfo = this.metadata.language_info;
             // Mode 'null' should be plain, unhighlighted text.
-            var cm_mode = langinfo.codemirror_mode || langinfo.language || 'null';
+            var cm_mode = langinfo.codemirror_mode || langinfo.name || 'null';
             this.set_codemirror_mode(cm_mode);
         }
         
diff --git a/IPython/kernel/adapter.py b/IPython/kernel/adapter.py
index 5c87cd0..25deac2 100644
--- a/IPython/kernel/adapter.py
+++ b/IPython/kernel/adapter.py
@@ -102,16 +102,20 @@ class V5toV4(Adapter):
     # shell channel
     
     def kernel_info_reply(self, msg):
+        v4c = {}
         content = msg['content']
-        content.pop('banner', None)
         for key in ('language_version', 'protocol_version'):
             if key in content:
-                content[key] = _version_str_to_list(content[key])
-        if content.pop('implementation', '') == 'ipython' \
+                v4c[key] = _version_str_to_list(content[key])
+        if content.get('implementation', '') == 'ipython' \
             and 'implementation_version' in content:
-            content['ipython_version'] = content.pop('implmentation_version')
-        content.pop('implementation_version', None)
-        content.setdefault("implmentation", content['language'])
+            v4c['ipython_version'] = _version_str_to_list(content['implementation_version'])
+        language_info = content.get('language_info', {})
+        language = language_info.get('name', '')
+        v4c.setdefault('language', language)
+        if 'version' in language_info:
+            v4c.setdefault('language_version', _version_str_to_list(language_info['version']))
+        msg['content'] = v4c
         return msg
     
     def execute_request(self, msg):
@@ -204,14 +208,23 @@ class V4toV5(Adapter):
     
     def kernel_info_reply(self, msg):
         content = msg['content']
-        for key in ('language_version', 'protocol_version', 'ipython_version'):
+        for key in ('protocol_version', 'ipython_version'):
             if key in content:
-                content[key] = ".".join(map(str, content[key]))
+                content[key] = '.'.join(map(str, content[key]))
+        
+        content.setdefault('protocol_version', '4.1')
         
         if content['language'].startswith('python') and 'ipython_version' in content:
             content['implementation'] = 'ipython'
             content['implementation_version'] = content.pop('ipython_version')
         
+        language = content.pop('language')
+        language_info = content.setdefault('language_info', {})
+        language_info.setdefault('name', language)
+        if 'language_version' in content:
+            language_version = '.'.join(map(str, content.pop('language_version')))
+            language_info.setdefault('version', language_version)
+        
         content['banner'] = ''
         return msg
     
diff --git a/IPython/kernel/tests/test_adapter.py b/IPython/kernel/tests/test_adapter.py
index a976991..fe2c886 100644
--- a/IPython/kernel/tests/test_adapter.py
+++ b/IPython/kernel/tests/test_adapter.py
@@ -169,6 +169,26 @@ class V4toV5TestCase(AdapterTest):
         text = v5c['data']['text/plain']
         self.assertEqual(text, '\n'.join([v4c['definition'], v4c['docstring']]))
     
+    def test_kernel_info_reply(self):
+        msg = self.msg("kernel_info_reply", {
+            'language': 'python',
+            'language_version': [2,8,0],
+            'ipython_version': [1,2,3],
+        })
+        v4, v5 = self.adapt(msg)
+        v4c = v4['content']
+        v5c = v5['content']
+        self.assertEqual(v5c, {
+            'protocol_version': '4.1',
+            'implementation': 'ipython',
+            'implementation_version': '1.2.3',
+            'language_info': {
+                'name': 'python',
+                'version': '2.8.0',
+            },
+            'banner' : '',
+        })
+    
     # iopub channel
     
     def test_display_data(self):
@@ -305,6 +325,29 @@ class V5toV4TestCase(AdapterTest):
         self.assertEqual(sorted(v4c), ['found', 'oname'])
         self.assertEqual(v4c['found'], False)
     
+    def test_kernel_info_reply(self):
+        msg = self.msg("kernel_info_reply", {
+            'protocol_version': '5.0',
+            'implementation': 'ipython',
+            'implementation_version': '1.2.3',
+            'language_info': {
+                'name': 'python',
+                'version': '2.8.0',
+                'mimetype': 'text/x-python',
+            },
+            'banner' : 'the banner',
+        })
+        v5, v4 = self.adapt(msg)
+        v4c = v4['content']
+        v5c = v5['content']
+        info = v5c['language_info']
+        self.assertEqual(v4c, {
+            'protocol_version': [5,0],
+            'language': 'python',
+            'language_version': [2,8,0],
+            'ipython_version': [1,2,3],
+        })
+    
     # iopub channel
     
     def test_display_data(self):
diff --git a/IPython/kernel/tests/test_message_spec.py b/IPython/kernel/tests/test_message_spec.py
index 3a72275..7c4bd46 100644
--- a/IPython/kernel/tests/test_message_spec.py
+++ b/IPython/kernel/tests/test_message_spec.py
@@ -4,8 +4,8 @@
 # Distributed under the terms of the Modified BSD License.
 
 import re
+import sys
 from distutils.version import LooseVersion as V
-from subprocess import PIPE
 try:
     from queue import Empty  # Py 3
 except ImportError:
@@ -13,10 +13,8 @@ except ImportError:
 
 import nose.tools as nt
 
-from IPython.kernel import KernelManager
-
 from IPython.utils.traitlets import (
-    HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
+    HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
 )
 from IPython.utils.py3compat import string_types, iteritems
 
@@ -150,15 +148,20 @@ class CompleteReply(Reference):
     cursor_end = Integer()
     status = Unicode()
 
+class LanguageInfo(Reference):
+    name = Unicode('python')
+    version = Unicode(sys.version.split()[0])
 
 class KernelInfoReply(Reference):
     protocol_version = Version(min='5.0')
     implementation = Unicode('ipython')
     implementation_version = Version(min='2.1')
-    language_version = Version(min='2.7')
-    language = Unicode('python')
     language_info = Dict()
     banner = Unicode()
+    
+    def check(self, d):
+        Reference.check(self, d)
+        LanguageInfo().check(d['language_info'])
 
 
 class IsCompleteReply(Reference):
diff --git a/IPython/kernel/zmq/ipkernel.py b/IPython/kernel/zmq/ipkernel.py
index 5cc0a01..89d9677 100644
--- a/IPython/kernel/zmq/ipkernel.py
+++ b/IPython/kernel/zmq/ipkernel.py
@@ -69,9 +69,10 @@ class IPythonKernel(KernelBase):
     # Kernel info fields
     implementation = 'ipython'
     implementation_version = release.version
-    language = 'python'
-    language_version = sys.version.split()[0]
-    language_info = {'mimetype': 'text/x-python',
+    language_info = {
+                     'name': 'python',
+                     'version': sys.version.split()[0],
+                     'mimetype': 'text/x-python',
                      'codemirror_mode': {'name': 'ipython',
                                          'version': sys.version_info[0]},
                      'pygments_lexer': 'ipython%d' % (3 if PY3 else 2),
diff --git a/IPython/kernel/zmq/kernelbase.py b/IPython/kernel/zmq/kernelbase.py
index 42b38c0..7b59656 100755
--- a/IPython/kernel/zmq/kernelbase.py
+++ b/IPython/kernel/zmq/kernelbase.py
@@ -455,8 +455,6 @@ class Kernel(SingletonConfigurable):
             'protocol_version': release.kernel_protocol_version,
             'implementation': self.implementation,
             'implementation_version': self.implementation_version,
-            'language': self.language,
-            'language_version': self.language_version,
             'language_info': self.language_info,
             'banner': self.banner,
         }
diff --git a/IPython/nbformat/v4/nbformat.v4.schema.json b/IPython/nbformat/v4/nbformat.v4.schema.json
index 317a80b..365a08a 100644
--- a/IPython/nbformat/v4/nbformat.v4.schema.json
+++ b/IPython/nbformat/v4/nbformat.v4.schema.json
@@ -10,25 +10,51 @@
             "type": "object",
             "additionalProperties": true,
             "properties": {
-                "kernel_info": {
+                "kernelspec": {
                     "description": "Kernel information.",
                     "type": "object",
-                    "required": ["name", "language"],
+                    "required": ["name", "display_name"],
                     "properties": {
                         "name": {
                             "description": "Name of the kernel specification.",
                             "type": "string"
                         },
-                        "language": {
-                            "description": "The programming language which this kernel runs.",
-                            "type": "string"
-                        },
-                        "codemirror_mode": {
-                            "description": "The codemirror mode to use for code in this language.",
+                        "display_name": {
+                            "description": "Name to display in UI.",
                             "type": "string"
                         }
                     }
                 },
+                "language_info": {
+                  "description": "Kernel information.",
+                  "type": "object",
+                  "required": ["name"],
+                  "properties": {
+                    "name": {
+                        "description": "The programming language which this kernel runs.",
+                        "type": "string"
+                    },
+                    "codemirror_mode": {
+                        "description": "The codemirror mode to use for code in this language.",
+                        "oneOf": [
+                          {"type": "string"},
+                          {"type": "object"}
+                        ]
+                    },
+                    "file_extension": {
+                        "description": "The file extension for files in this language.",
+                        "type": "string"
+                    },
+                    "mimetype": {
+                        "description": "The mimetype corresponding to files in this language.",
+                        "type": "string"
+                    },
+                    "pygments_lexer": {
+                        "description": "The pygments lexer to use for code in this language.",
+                        "type": "string"
+                    }
+                  }
+                },
                 "signature": {
                     "description": "Hash of the notebook.",
                     "type": "string"
diff --git a/docs/source/development/messaging.rst b/docs/source/development/messaging.rst
index 47befeb..08b7618 100644
--- a/docs/source/development/messaging.rst
+++ b/docs/source/development/messaging.rst
@@ -720,19 +720,20 @@ Message type: ``kernel_info_reply``::
         # Implementation version number.
         # The version number of the kernel's implementation
         # (e.g. IPython.__version__ for the IPython kernel)
-        'implementation_version': 'X.Y.Z', 
-
-        # Programming language in which kernel is implemented.
-        # Kernel included in IPython returns 'python'.
-        'language': str,
-        
-        # Language version number.
-        # It is Python version number (e.g., '2.7.3') for the kernel
-        # included in IPython.
-        'language_version': 'X.Y.Z',
+        'implementation_version': 'X.Y.Z',
 
         # Information about the language of code for the kernel
         'language_info': {
+            # Name of the programming language in which kernel is implemented.
+            # Kernel included in IPython returns 'python'.
+            'name': str,
+            
+            # Language version number.
+            # It is Python version number (e.g., '2.7.3') for the kernel
+            # included in IPython.
+            'version': 'X.Y.Z',
+            
+            # mimetype for script files in this language
             'mimetype': str,
 
             # Extension without the dot, e.g. 'py'
@@ -779,6 +780,14 @@ and `codemirror modes <http://codemirror.net/mode/index.html>`_ for those fields
     ``language_info``, ``implementation``, ``implementation_version``, ``banner``
     and ``help_links`` keys are added.
 
+.. versionchanged:: 5.0
+
+    ``language_version`` moved to ``language_info.version``
+
+.. versionchanged:: 5.0
+
+    ``language`` moved to ``language_info.name``
+
 .. _msging_shutdown:
 
 Kernel shutdown
diff --git a/docs/source/notebook/nbformat.rst b/docs/source/notebook/nbformat.rst
index 975d748..b2ee31f 100644
--- a/docs/source/notebook/nbformat.rst
+++ b/docs/source/notebook/nbformat.rst
@@ -36,11 +36,15 @@ At the highest level, a Jupyter notebook is a dictionary with a few keys:
       "metadata" : {
         "signature": "hex-digest", # used for authenticating unsafe outputs on load
         "kernel_info": {
-            # if kernel_info is defined, its name and language fields are required.
-            "name" : "the name of the kernel",
-            "language" : "the programming language of the kernel",
-            "codemirror_mode": "The name of the codemirror mode to use [optional]"
+            # if kernel_info is defined, its name field is required.
+            "name" : "the name of the kernel"
         },
+        "language_info": {
+            # if language_info is defined, its name field is required.
+            "name" : "the programming language of the kernel",
+            "version": "the version of the language",
+            "codemirror_mode": "The name of the codemirror mode to use [optional]"
+        }
       },
       "nbformat": 4,
       "nbformat_minor": 0,