diff --git a/IPython/html/services/kernelspecs/handlers.py b/IPython/html/services/kernelspecs/handlers.py index 2d1a75fc2..72397788f 100644 --- a/IPython/html/services/kernelspecs/handlers.py +++ b/IPython/html/services/kernelspecs/handlers.py @@ -2,11 +2,43 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. + +import glob import json +import os +pjoin = os.path.join + from tornado import web from ...base.handlers import IPythonHandler, json_errors +from ...utils import url_path_join +def kernelspec_model(handler, name): + """Load a KernelSpec by name and return the REST API model""" + ksm = handler.kernel_spec_manager + spec = ksm.get_kernel_spec(name) + d = {'name': name} + d['spec'] = spec.to_dict() + d['resources'] = resources = {} + resource_dir = spec.resource_dir + for resource in ['kernel.js', 'kernel.css']: + if os.path.exists(pjoin(resource_dir, resource)): + resources[resource] = url_path_join( + handler.base_url, + 'kernelspecs', + name, + resource + ) + for logo_file in glob.glob(pjoin(resource_dir, 'logo-*')): + fname = os.path.basename(logo_file) + no_ext, _ = os.path.splitext(fname) + resources[no_ext] = url_path_join( + handler.base_url, + 'kernelspecs', + name, + fname + ) + return d class MainKernelSpecHandler(IPythonHandler): SUPPORTED_METHODS = ('GET',) @@ -21,13 +53,11 @@ class MainKernelSpecHandler(IPythonHandler): model['kernelspecs'] = specs = {} for kernel_name in ksm.find_kernel_specs(): try: - d = ksm.get_kernel_spec(kernel_name).to_dict() + d = kernelspec_model(self, kernel_name) except Exception: self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True) continue - d['name'] = kernel_name specs[kernel_name] = d - self.set_header("Content-Type", 'application/json') self.finish(json.dumps(model)) @@ -38,13 +68,12 @@ class KernelSpecHandler(IPythonHandler): @web.authenticated @json_errors def get(self, kernel_name): - ksm = self.kernel_spec_manager try: - kernelspec = ksm.get_kernel_spec(kernel_name) + model = kernelspec_model(self, kernel_name) except KeyError: raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name) self.set_header("Content-Type", 'application/json') - self.finish(kernelspec.to_json()) + self.finish(json.dumps(model)) # URL to handler mappings diff --git a/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py b/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py index 0d43fb1de..485948dd1 100644 --- a/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py +++ b/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py @@ -99,17 +99,20 @@ class APITest(NotebookTestBase): self.assertGreaterEqual(len(specs), 2) def is_sample_kernelspec(s): - return s['name'] == 'sample' and s['display_name'] == 'Test kernel' + return s['name'] == 'sample' and s['spec']['display_name'] == 'Test kernel' def is_default_kernelspec(s): - return s['name'] == NATIVE_KERNEL_NAME and s['display_name'].startswith("IPython") + return s['name'] == NATIVE_KERNEL_NAME and s['spec']['display_name'].startswith("IPython") assert any(is_sample_kernelspec(s) for s in specs.values()), specs assert any(is_default_kernelspec(s) for s in specs.values()), specs def test_get_kernelspec(self): - spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive - self.assertEqual(spec['display_name'], 'Test kernel') + model = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive + self.assertEqual(model['name'].lower(), 'sample') + self.assertIsInstance(model['spec'], dict) + self.assertEqual(model['spec']['display_name'], 'Test kernel') + self.assertIsInstance(model['resources'], dict) def test_get_nonexistant_kernelspec(self): with assert_http_error(404): diff --git a/IPython/html/static/notebook/js/kernelselector.js b/IPython/html/static/notebook/js/kernelselector.js index a9165b8a7..3cd13d39e 100644 --- a/IPython/html/static/notebook/js/kernelselector.js +++ b/IPython/html/static/notebook/js/kernelselector.js @@ -35,8 +35,8 @@ define([ var change_kernel_submenu = $("#menu-change-kernel-submenu"); var keys = Object.keys(data.kernelspecs).sort(function (a, b) { // sort by display_name - var da = data.kernelspecs[a].display_name; - var db = data.kernelspecs[b].display_name; + var da = data.kernelspecs[a].spec.display_name; + var db = data.kernelspecs[b].spec.display_name; if (da === db) { return 0; } else if (da > db) { @@ -50,7 +50,7 @@ define([ var ks_submenu_entry = $("