Repository: jupyter/jupyter_client
Pull request #340 (2018-01-23 19:37:42)

Backport PR #339 on branch 5.x
Backport PR #339: avoid calling private method in subclasses of KernelSpecManager


Patch:
diff --git a/jupyter_client/kernelspec.py b/jupyter_client/kernelspec.py
index 3e05d29..78a5b56 100644
--- a/jupyter_client/kernelspec.py
+++ b/jupyter_client/kernelspec.py
@@ -254,7 +254,14 @@ class KernelSpecManager(LoggingConfigurable):
         res = {}
         for kname, resource_dir in d.items():
             try:
-                spec = self._get_kernel_spec_by_name(kname, resource_dir)
+                if self.__class__ is KernelSpecManager:
+                    spec = self._get_kernel_spec_by_name(kname, resource_dir)
+                else:
+                    # avoid calling private methods in subclasses,
+                    # which may have overridden find_kernel_specs
+                    # and get_kernel_spec, but not the newer get_all_specs
+                    spec = self.get_kernel_spec(kname)
+
                 res[kname] = {
                     "resource_dir": resource_dir,
                     "spec": spec.to_dict()


Test patch:
diff --git a/jupyter_client/tests/test_kernelspec.py b/jupyter_client/tests/test_kernelspec.py
index b2ec419..2919923 100644
--- a/jupyter_client/tests/test_kernelspec.py
+++ b/jupyter_client/tests/test_kernelspec.py
@@ -4,6 +4,7 @@
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
 
+import copy
 import io
 import json
 from logging import StreamHandler
@@ -11,6 +12,7 @@ import os
 from os.path import join as pjoin
 from subprocess import Popen, PIPE, STDOUT
 import sys
+import tempfile
 import unittest
 
 import pytest
@@ -156,7 +158,7 @@ class KernelSpecTests(unittest.TestCase):
             'Haskell-1-2-3',
         ]:
             assert kernelspec._is_valid_kernel_name(good)
-        
+
         for bad in [
             'has space',
             u'ünicode',
@@ -165,4 +167,33 @@ class KernelSpecTests(unittest.TestCase):
         ]:
             assert not kernelspec._is_valid_kernel_name(bad)
 
-
+    def test_subclass(self):
+        """Test get_all_specs in subclasses that override find_kernel_specs"""
+        ksm = self.ksm
+        resource_dir = tempfile.gettempdir()
+        native_name = kernelspec.NATIVE_KERNEL_NAME
+        native_kernel = ksm.get_kernel_spec(native_name)
+
+        class MyKSM(kernelspec.KernelSpecManager):
+            def get_kernel_spec(self, name):
+                spec = copy.copy(native_kernel)
+                if name == 'fake':
+                    spec.name = name
+                    spec.resource_dir = resource_dir
+                elif name == native_name:
+                    pass
+                else:
+                    raise KeyError(name)
+                return spec
+
+            def find_kernel_specs(self):
+                return {
+                    'fake': resource_dir,
+                    native_name: native_kernel.resource_dir,
+                }
+
+        # ensure that get_all_specs doesn't raise if only
+        # find_kernel_specs and get_kernel_spec are defined
+        myksm = MyKSM()
+        specs = myksm.get_all_specs()
+        assert sorted(specs) == ['fake', native_name]