Skip to content

Commit 46bcf78

Browse files
committed
Update gateway support with recent changes
Convey notebook working directory to the gateway (nb2kg pr-21) Support retrieval of kernelspec resources from the gateway (nb2kg pr-23)
1 parent d145301 commit 46bcf78

File tree

3 files changed

+80
-15
lines changed

3 files changed

+80
-15
lines changed

notebook/gateway/handlers.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
import os
55
import logging
6+
import mimetypes
67

7-
from ..base.handlers import IPythonHandler
8+
from ..base.handlers import APIHandler, IPythonHandler
89
from ..utils import url_path_join
910

1011
from tornado import gen, web
@@ -200,8 +201,26 @@ def on_close(self):
200201
self._disconnect()
201202

202203

204+
class GatewayResourceHandler(APIHandler):
205+
"""Retrieves resources for specific kernelspec definitions from kernel/enterprise gateway."""
206+
207+
@web.authenticated
208+
@gen.coroutine
209+
def get(self, kernel_name, path, include_body=True):
210+
ksm = self.kernel_spec_manager
211+
kernel_spec_res = yield ksm.get_kernel_spec_resource(kernel_name, path)
212+
if kernel_spec_res is None:
213+
self.log.warning("Kernelspec resource '{}' for '{}' not found. Gateway may not support"
214+
" resource serving.".format(path, kernel_name))
215+
else:
216+
self.set_header("Content-Type", mimetypes.guess_type(path)[0])
217+
self.finish(kernel_spec_res)
218+
219+
203220
from ..services.kernels.handlers import _kernel_id_regex
221+
from ..services.kernelspecs.handlers import kernel_name_regex
204222

205223
default_handlers = [
206224
(r"/api/kernels/%s/channels" % _kernel_id_regex, WebSocketChannelsHandler),
225+
(r"/kernelspecs/%s/(?P<path>.*)" % kernel_name_regex, GatewayResourceHandler),
207226
]

notebook/gateway/managers.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ def _kernels_endpoint_default(self):
9191
def _kernelspecs_endpoint_default(self):
9292
return os.environ.get(self.kernelspecs_endpoint_env, self.kernelspecs_endpoint_default_value)
9393

94+
kernelspecs_resource_endpoint_default_value = '/kernelspecs'
95+
kernelspecs_resource_endpoint_env = 'JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT'
96+
kernelspecs_resource_endpoint = Unicode(default_value=kernelspecs_resource_endpoint_default_value, config=True,
97+
help="""The gateway endpoint for accessing kernelspecs resources
98+
(JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT env var)""")
99+
100+
@default('kernelspecs_resource_endpoint')
101+
def _kernelspecs_resource_endpoint_default(self):
102+
return os.environ.get(self.kernelspecs_resource_endpoint_env, self.kernelspecs_resource_endpoint_default_value)
103+
94104
connect_timeout_default_value = 20.0
95105
connect_timeout_env = 'JUPYTER_GATEWAY_CONNECT_TIMEOUT'
96106
connect_timeout = Float(default_value=connect_timeout_default_value, config=True,
@@ -333,6 +343,11 @@ def start_kernel(self, kernel_id=None, path=None, **kwargs):
333343

334344
kernel_env = {k: v for (k, v) in dict(os.environ).items() if k.startswith('KERNEL_')
335345
or k in GatewayClient.instance().env_whitelist.split(",")}
346+
347+
# Convey the full path to where this notebook file is located.
348+
if path is not None and kernel_env.get('KERNEL_WORKING_DIR') is None:
349+
kernel_env['KERNEL_WORKING_DIR'] = kwargs['cwd']
350+
336351
json_body = json_encode({'name': kernel_name, 'env': kernel_env})
337352

338353
response = yield gateway_request(kernel_url, method='POST', body=json_body)
@@ -467,7 +482,10 @@ class GatewayKernelSpecManager(KernelSpecManager):
467482

468483
def __init__(self, **kwargs):
469484
super(GatewayKernelSpecManager, self).__init__(**kwargs)
470-
self.base_endpoint = url_path_join(GatewayClient.instance().url, GatewayClient.instance().kernelspecs_endpoint)
485+
self.base_endpoint = url_path_join(GatewayClient.instance().url,
486+
GatewayClient.instance().kernelspecs_endpoint)
487+
self.base_resource_endpoint = url_path_join(GatewayClient.instance().url,
488+
GatewayClient.instance().kernelspecs_resource_endpoint)
471489

472490
def _get_kernelspecs_endpoint_url(self, kernel_name=None):
473491
"""Builds a url for the kernels endpoint
@@ -498,14 +516,7 @@ def get_all_specs(self):
498516
notebook_default=km.default_kernel_name))
499517
km.default_kernel_name = remote_default_kernel_name
500518

501-
# gateway doesn't support resources (requires transfer for use by NB client)
502-
# so add `resource_dir` to each kernelspec and value of 'not supported in gateway mode'
503519
remote_kspecs = fetched_kspecs.get('kernelspecs')
504-
for kernel_name, kspec_info in remote_kspecs.items():
505-
if not kspec_info.get('resource_dir'):
506-
kspec_info['resource_dir'] = 'not supported in gateway mode'
507-
remote_kspecs[kernel_name].update(kspec_info)
508-
509520
raise gen.Return(remote_kspecs)
510521

511522
@gen.coroutine
@@ -540,9 +551,32 @@ def get_kernel_spec(self, kernel_name, **kwargs):
540551
raise
541552
else:
542553
kernel_spec = json_decode(response.body)
543-
# Convert to instance of Kernelspec
544-
kspec_instance = self.kernel_spec_class(resource_dir=u'', **kernel_spec['spec'])
545-
raise gen.Return(kspec_instance)
554+
555+
raise gen.Return(kernel_spec)
556+
557+
@gen.coroutine
558+
def get_kernel_spec_resource(self, kernel_name, path):
559+
"""Get kernel spec for kernel_name.
560+
561+
Parameters
562+
----------
563+
kernel_name : str
564+
The name of the kernel.
565+
path : str
566+
The name of the desired resource
567+
"""
568+
kernel_spec_resource_url = url_path_join(self.base_resource_endpoint, str(kernel_name), str(path))
569+
self.log.debug("Request kernel spec resource '{}' at: {}".format(path, kernel_spec_resource_url))
570+
try:
571+
response = yield gateway_request(kernel_spec_resource_url, method='GET')
572+
except HTTPError as error:
573+
if error.code == 404:
574+
kernel_spec_resource = None
575+
else:
576+
raise
577+
else:
578+
kernel_spec_resource = response.body
579+
raise gen.Return(kernel_spec_resource)
546580

547581

548582
class GatewaySessionManager(SessionManager):

notebook/services/kernelspecs/handlers.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ...base.handlers import APIHandler
1717
from ...utils import url_path_join, url_unescape
1818

19+
1920
def kernelspec_model(handler, name, spec_dict, resource_dir):
2021
"""Load a KernelSpec by name and return the REST API model"""
2122
d = {
@@ -45,6 +46,12 @@ def kernelspec_model(handler, name, spec_dict, resource_dir):
4546
)
4647
return d
4748

49+
50+
def is_kernelspec_model(spec_dict):
51+
"""Returns True if spec_dict is already in proper form. This will occur when using a gateway."""
52+
return isinstance(spec_dict, dict) and 'name' in spec_dict and 'spec' in spec_dict and 'resources' in spec_dict
53+
54+
4855
class MainKernelSpecHandler(APIHandler):
4956

5057
@web.authenticated
@@ -58,8 +65,10 @@ def get(self):
5865
kspecs = yield gen.maybe_future(ksm.get_all_specs())
5966
for kernel_name, kernel_info in kspecs.items():
6067
try:
61-
d = kernelspec_model(self, kernel_name, kernel_info['spec'],
62-
kernel_info['resource_dir'])
68+
if is_kernelspec_model(kernel_info):
69+
d = kernel_info
70+
else:
71+
d = kernelspec_model(self, kernel_name, kernel_info['spec'], kernel_info['resource_dir'])
6372
except Exception:
6473
self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
6574
continue
@@ -79,7 +88,10 @@ def get(self, kernel_name):
7988
spec = yield gen.maybe_future(ksm.get_kernel_spec(kernel_name))
8089
except KeyError:
8190
raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
82-
model = kernelspec_model(self, kernel_name, spec.to_dict(), spec.resource_dir)
91+
if is_kernelspec_model(spec):
92+
model = spec
93+
else:
94+
model = kernelspec_model(self, kernel_name, spec.to_dict(), spec.resource_dir)
8395
self.set_header("Content-Type", 'application/json')
8496
self.finish(json.dumps(model))
8597

0 commit comments

Comments
 (0)