##// END OF EJS Templates
Merge pull request #5827 from takluyver/kernelspec-rest-api...
Min RK -
r17022:410f2c0a merge
parent child Browse files
Show More
1 NO CONTENT: new file 100644
@@ -0,0 +1,27 b''
1 from tornado import web
2 from ..base.handlers import IPythonHandler
3 from ..services.kernelspecs.handlers import kernel_name_regex
4
5 class KernelSpecResourceHandler(web.StaticFileHandler, IPythonHandler):
6 SUPPORTED_METHODS = ('GET', 'HEAD')
7
8 def initialize(self):
9 web.StaticFileHandler.initialize(self, path='')
10
11 @web.authenticated
12 def get(self, kernel_name, path, include_body=True):
13 ksm = self.kernel_spec_manager
14 try:
15 self.root = ksm.get_kernel_spec(kernel_name).resource_dir
16 except KeyError:
17 raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
18 self.log.debug("Serving kernel resource from: %s", self.root)
19 return web.StaticFileHandler.get(self, path, include_body=include_body)
20
21 @web.authenticated
22 def head(self, kernel_name, path):
23 self.get(kernel_name, path, include_body=False)
24
25 default_handlers = [
26 (r"/kernelspecs/%s/(?P<path>.*)" % kernel_name_regex, KernelSpecResourceHandler),
27 ] No newline at end of file
1 NO CONTENT: new file 100644
@@ -0,0 +1,50 b''
1 """Tornado handlers for kernel specifications."""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5 import json
6 from tornado import web
7
8 from ...base.handlers import IPythonHandler, json_errors
9
10
11 class MainKernelSpecHandler(IPythonHandler):
12 SUPPORTED_METHODS = ('GET',)
13
14 @web.authenticated
15 @json_errors
16 def get(self):
17 ksm = self.kernel_spec_manager
18 results = []
19 for kernel_name in ksm.find_kernel_specs():
20 d = ksm.get_kernel_spec(kernel_name).to_dict()
21 d['name'] = kernel_name
22 results.append(d)
23
24 self.set_header("Content-Type", 'application/json')
25 self.finish(json.dumps(results))
26
27
28 class KernelSpecHandler(IPythonHandler):
29 SUPPORTED_METHODS = ('GET',)
30
31 @web.authenticated
32 @json_errors
33 def get(self, kernel_name):
34 ksm = self.kernel_spec_manager
35 try:
36 kernelspec = ksm.get_kernel_spec(kernel_name)
37 except KeyError:
38 raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
39 self.set_header("Content-Type", 'application/json')
40 self.finish(kernelspec.to_json())
41
42
43 # URL to handler mappings
44
45 kernel_name_regex = r"(?P<kernel_name>\w+)"
46
47 default_handlers = [
48 (r"/api/kernelspecs", MainKernelSpecHandler),
49 (r"/api/kernelspecs/%s" % kernel_name_regex, KernelSpecHandler),
50 ]
@@ -0,0 +1,97 b''
1 # coding: utf-8
2 """Test the kernel specs webservice API."""
3
4 import errno
5 import io
6 import json
7 import os
8
9 pjoin = os.path.join
10
11 import requests
12
13 from IPython.html.utils import url_path_join
14 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
15
16 # Copied from IPython.kernel.tests.test_kernelspec so updating that doesn't
17 # break these tests
18 sample_kernel_json = {'argv':['cat', '{connection_file}'],
19 'display_name':'Test kernel',
20 'language':'bash',
21 }
22
23 some_resource = u"The very model of a modern major general"
24
25
26 class KernelSpecAPI(object):
27 """Wrapper for notebook API calls."""
28 def __init__(self, base_url):
29 self.base_url = base_url
30
31 def _req(self, verb, path, body=None):
32 response = requests.request(verb,
33 url_path_join(self.base_url, path),
34 data=body,
35 )
36 response.raise_for_status()
37 return response
38
39 def list(self):
40 return self._req('GET', 'api/kernelspecs')
41
42 def kernel_spec_info(self, name):
43 return self._req('GET', url_path_join('api/kernelspecs', name))
44
45 def kernel_resource(self, name, path):
46 return self._req('GET', url_path_join('kernelspecs', name, path))
47
48 class APITest(NotebookTestBase):
49 """Test the kernelspec web service API"""
50 def setUp(self):
51 ipydir = self.ipython_dir.name
52 sample_kernel_dir = pjoin(ipydir, 'kernels', 'sample')
53 try:
54 os.makedirs(sample_kernel_dir)
55 except OSError as e:
56 if e.errno != errno.EEXIST:
57 raise
58
59 with open(pjoin(sample_kernel_dir, 'kernel.json'), 'w') as f:
60 json.dump(sample_kernel_json, f)
61
62 with io.open(pjoin(sample_kernel_dir, 'resource.txt'), 'w',
63 encoding='utf-8') as f:
64 f.write(some_resource)
65
66 self.ks_api = KernelSpecAPI(self.base_url())
67
68 def test_list_kernelspecs(self):
69 specs = self.ks_api.list().json()
70 assert isinstance(specs, list)
71
72 # 2: the sample kernelspec created in setUp, and the native Python kernel
73 self.assertEqual(len(specs), 2)
74
75 def is_sample_kernelspec(s):
76 return s['name'] == 'sample' and s['display_name'] == 'Test kernel'
77
78 assert any(is_sample_kernelspec(s) for s in specs), specs
79
80 def test_get_kernelspec(self):
81 spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive
82 self.assertEqual(spec['language'], 'bash')
83
84 def test_get_nonexistant_kernelspec(self):
85 with assert_http_error(404):
86 self.ks_api.kernel_spec_info('nonexistant')
87
88 def test_get_kernel_resource_file(self):
89 res = self.ks_api.kernel_resource('sAmple', 'resource.txt')
90 self.assertEqual(res.text, some_resource)
91
92 def test_get_nonexistant_resource(self):
93 with assert_http_error(404):
94 self.ks_api.kernel_resource('nonexistant', 'resource.txt')
95
96 with assert_http_error(404):
97 self.ks_api.kernel_resource('sample', 'nonexistant.txt')
@@ -159,6 +159,10 b' class IPythonHandler(AuthenticatedHandler):'
159 159 return self.settings['session_manager']
160 160
161 161 @property
162 def kernel_spec_manager(self):
163 return self.settings['kernel_spec_manager']
164
165 @property
162 166 def project_dir(self):
163 167 return self.notebook_manager.notebook_dir
164 168
@@ -67,12 +67,13 b' from IPython.core.application import ('
67 67 )
68 68 from IPython.core.profiledir import ProfileDir
69 69 from IPython.kernel import KernelManager
70 from IPython.kernel.kernelspec import KernelSpecManager
70 71 from IPython.kernel.zmq.session import default_secure, Session
71 72 from IPython.nbformat.sign import NotebookNotary
72 73 from IPython.utils.importstring import import_item
73 74 from IPython.utils import submodule
74 75 from IPython.utils.traitlets import (
75 Dict, Unicode, Integer, List, Bool, Bytes,
76 Dict, Unicode, Integer, List, Bool, Bytes, Instance,
76 77 DottedObjectName, TraitError,
77 78 )
78 79 from IPython.utils import py3compat
@@ -118,19 +119,21 b' def load_handlers(name):'
118 119 class NotebookWebApplication(web.Application):
119 120
120 121 def __init__(self, ipython_app, kernel_manager, notebook_manager,
121 cluster_manager, session_manager, log, base_url,
122 settings_overrides, jinja_env_options):
122 cluster_manager, session_manager, kernel_spec_manager, log,
123 base_url, settings_overrides, jinja_env_options):
123 124
124 125 settings = self.init_settings(
125 126 ipython_app, kernel_manager, notebook_manager, cluster_manager,
126 session_manager, log, base_url, settings_overrides, jinja_env_options)
127 session_manager, kernel_spec_manager, log, base_url,
128 settings_overrides, jinja_env_options)
127 129 handlers = self.init_handlers(settings)
128 130
129 131 super(NotebookWebApplication, self).__init__(handlers, **settings)
130 132
131 133 def init_settings(self, ipython_app, kernel_manager, notebook_manager,
132 cluster_manager, session_manager, log, base_url,
133 settings_overrides, jinja_env_options=None):
134 cluster_manager, session_manager, kernel_spec_manager,
135 log, base_url, settings_overrides,
136 jinja_env_options=None):
134 137 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
135 138 # base_url will always be unicode, which will in turn
136 139 # make the patterns unicode, and ultimately result in unicode
@@ -162,6 +165,7 b' class NotebookWebApplication(web.Application):'
162 165 notebook_manager=notebook_manager,
163 166 cluster_manager=cluster_manager,
164 167 session_manager=session_manager,
168 kernel_spec_manager=kernel_spec_manager,
165 169
166 170 # IPython stuff
167 171 nbextensions_path = ipython_app.nbextensions_path,
@@ -183,11 +187,13 b' class NotebookWebApplication(web.Application):'
183 187 handlers.extend(load_handlers('auth.logout'))
184 188 handlers.extend(load_handlers('notebook.handlers'))
185 189 handlers.extend(load_handlers('nbconvert.handlers'))
190 handlers.extend(load_handlers('kernelspecs.handlers'))
186 191 handlers.extend(load_handlers('services.kernels.handlers'))
187 192 handlers.extend(load_handlers('services.notebooks.handlers'))
188 193 handlers.extend(load_handlers('services.clusters.handlers'))
189 194 handlers.extend(load_handlers('services.sessions.handlers'))
190 195 handlers.extend(load_handlers('services.nbconvert.handlers'))
196 handlers.extend(load_handlers('services.kernelspecs.handlers'))
191 197 # FIXME: /files/ should be handled by the Contents service when it exists
192 198 nbm = settings['notebook_manager']
193 199 if hasattr(nbm, 'notebook_dir'):
@@ -510,6 +516,11 b' class NotebookApp(BaseIPythonApplication):'
510 516 help='The cluster manager class to use.'
511 517 )
512 518
519 kernel_spec_manager = Instance(KernelSpecManager)
520
521 def _kernel_spec_manager_default(self):
522 return KernelSpecManager(ipython_dir=self.ipython_dir)
523
513 524 trust_xheaders = Bool(False, config=True,
514 525 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
515 526 "sent by the upstream reverse proxy. Necessary if the proxy handles SSL")
@@ -616,7 +627,7 b' class NotebookApp(BaseIPythonApplication):'
616 627 """initialize tornado webapp and httpserver"""
617 628 self.web_app = NotebookWebApplication(
618 629 self, self.kernel_manager, self.notebook_manager,
619 self.cluster_manager, self.session_manager,
630 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
620 631 self.log, self.base_url, self.webapp_settings,
621 632 self.jinja_environment_options
622 633 )
@@ -50,6 +50,17 b' class KernelSpec(HasTraits):'
50 50 with io.open(kernel_file, 'r', encoding='utf-8') as f:
51 51 kernel_dict = json.load(f)
52 52 return cls(resource_dir=resource_dir, **kernel_dict)
53
54 def to_dict(self):
55 return dict(argv=self.argv,
56 env=self.env,
57 display_name=self.display_name,
58 language=self.language,
59 codemirror_mode=self.codemirror_mode,
60 )
61
62 def to_json(self):
63 return json.dumps(self.to_dict())
53 64
54 65 def _is_kernel_dir(path):
55 66 """Is ``path`` a kernel directory?"""
General Comments 0
You need to be logged in to leave comments. Login now