Merge pull request #7283 from minrk/kernel-spec-model

add resource URLs to kernelspec model
This commit is contained in:
Matthias Bussonnier 2014-12-28 16:38:50 +01:00
commit c1d9f4d436
4 changed files with 77 additions and 51 deletions

View File

@ -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

View File

@ -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):

View File

@ -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 = $("<li>").attr("id", "kernel-submenu-"+ks.name).append($('<a>')
.attr('href', '#')
.click($.proxy(this.change_kernel, this, ks.name))
.text(ks.display_name));
.text(ks.spec.display_name));
change_kernel_submenu.append(ks_submenu_entry);
}
};
@ -59,25 +59,17 @@ define([
/**
* TODO, have a methods to set kernel spec directly ?
**/
var that = this;
if (kernel_name === this.current_selection) {
return;
}
var ks = this.kernelspecs[kernel_name];
var new_mode_url = 'kernelspecs/'+ks.name+'/kernel';
var css_url = this.notebook.base_url+new_mode_url+'.css';
$.ajax({
type: 'HEAD',
url: css_url,
success: function(){
$('#kernel-css')
.attr('href',css_url);
},
error:function(){
console.info("No custom kernel.css at URL:", css_url)
}
});
var css_url = ks.resources['kernel.css'];
if (css_url) {
$('#kernel-css').attr('href', css_url);
} else {
$('#kernel-css').attr('href', '');
}
try {
this.notebook.start_session(kernel_name);
@ -93,25 +85,22 @@ define([
}
this.events.trigger('spec_changed.Kernel', ks);
// load new mode kernel.js if exist
require([new_mode_url],
// if new mode has custom.js
function(new_mode){
that.lock_switch();
if(new_mode && new_mode.onload){
new_mode.onload();
} else {
console.warn("The current kernel defined a kernel.js file but does not contain "+
"any asynchronous module definition. This is undefined behavior "+
"which is not recommended");
if (ks.resources['kernel.js']) {
require([ks.resources['kernel.js']],
function (kernel_mod) {
if (kernel_mod && kernel_mod.onload) {
kernel_mod.onload();
} else {
console.warn("Kernel " + ks.name + " has a kernel.js file that does not contain "+
"any asynchronous module definition. This is undefined behavior "+
"and not recommended.");
}
}, function (err) {
console.warn("Failed to load kernel.js from ", ks.resources['kernel.js'], err);
}
},
function(err){
// if new mode does not have custom.js
console.info("No custom kernel.css at URL:", new_mode_url)
}
);
);
}
};
KernelSelector.prototype.lock_switch = function() {
@ -123,10 +112,16 @@ define([
KernelSelector.prototype.bind_events = function() {
var that = this;
var logo_img = this.element.find("img.current_kernel_logo");
this.events.on('spec_changed.Kernel', function(event, data) {
that.current_selection = data.name;
$("#kernel_indicator").find('.kernel_indicator_name').text(data.display_name);
that.element.find("img.current_kernel_logo").attr("src", that.notebook.base_url + "kernelspecs/" + data.name + "/logo-64x64.png");
$("#kernel_indicator").find('.kernel_indicator_name').text(data.spec.display_name);
if (data.resources['logo-64x64']) {
logo_img.attr("src", data.resources['logo-64x64']);
logo_img.show();
} else {
logo_img.hide();
}
});
this.events.on('kernel_created.Session', function(event, data) {
@ -139,7 +134,6 @@ define([
}
});
var logo_img = this.element.find("img.current_kernel_logo");
logo_img.on("load", function() {
logo_img.show();
});

View File

@ -247,7 +247,7 @@ define([
this.events.on('spec_changed.Kernel', function(event, data) {
that.metadata.kernelspec =
{name: data.name, display_name: data.display_name};
{name: data.name, display_name: data.spec.display_name};
});
this.events.on('kernel_ready.Kernel', function(event, data) {