kernelmanager.py
123 lines
| 4.6 KiB
| text/x-python
|
PythonLexer
Min RK
|
r18963 | """A MultiKernelManager for use in the notebook webserver | ||
- raises HTTPErrors | ||||
- creates REST API models | ||||
""" | ||||
Brian E. Granger
|
r4609 | |||
Min RK
|
r18933 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Brian E. Granger
|
r4344 | |||
MinRK
|
r15420 | import os | ||
Brian E. Granger
|
r4494 | from tornado import web | ||
MinRK
|
r9371 | from IPython.kernel.multikernelmanager import MultiKernelManager | ||
Thomas Kluyver
|
r18970 | from IPython.utils.traitlets import List, Unicode, TraitError | ||
Brian E. Granger
|
r10273 | |||
MinRK
|
r15420 | from IPython.html.utils import to_os_path | ||
from IPython.utils.py3compat import getcwd | ||||
Brian E. Granger
|
r4545 | |||
MinRK
|
r4960 | class MappingKernelManager(MultiKernelManager): | ||
MinRK
|
r10355 | """A KernelManager that handles notebook mapping and HTTP error handling""" | ||
Brian E. Granger
|
r4494 | |||
MinRK
|
r10295 | def _kernel_manager_class_default(self): | ||
return "IPython.kernel.ioloop.IOLoopKernelManager" | ||||
Thomas Kluyver
|
r18970 | kernel_argv = List(Unicode) | ||
root_dir = Unicode(config=True) | ||||
def _root_dir_default(self): | ||||
Thomas Kluyver
|
r18971 | try: | ||
return self.parent.notebook_dir | ||||
except AttributeError: | ||||
return getcwd() | ||||
MinRK
|
r15420 | |||
def _root_dir_changed(self, name, old, new): | ||||
"""Do a bit of validation of the root dir.""" | ||||
if not os.path.isabs(new): | ||||
# If we receive a non-absolute path, make it absolute. | ||||
self.root_dir = os.path.abspath(new) | ||||
return | ||||
if not os.path.exists(new) or not os.path.isdir(new): | ||||
raise TraitError("kernel root dir %r is not a directory" % new) | ||||
Brian E. Granger
|
r4494 | |||
#------------------------------------------------------------------------- | ||||
# Methods for managing kernels and sessions | ||||
#------------------------------------------------------------------------- | ||||
MinRK
|
r10321 | def _handle_kernel_died(self, kernel_id): | ||
"""notice that a kernel died""" | ||||
self.log.warn("Kernel %s died, removing from map.", kernel_id) | ||||
Thomas Kluyver
|
r11074 | self.remove_kernel(kernel_id) | ||
Dale Jung
|
r16052 | |||
MinRK
|
r15420 | def cwd_for_path(self, path): | ||
"""Turn API path into absolute OS path.""" | ||||
os_path = to_os_path(path, self.root_dir) | ||||
# in the case of notebooks and kernels not being on the same filesystem, | ||||
# walk up to root_dir if the paths don't exist | ||||
Thomas Kluyver
|
r19136 | while not os.path.isdir(os_path) and os_path != self.root_dir: | ||
MinRK
|
r15420 | os_path = os.path.dirname(os_path) | ||
return os_path | ||||
Thomas Kluyver
|
r17221 | def start_kernel(self, kernel_id=None, path=None, kernel_name='python', **kwargs): | ||
"""Start a kernel for a session and return its kernel_id. | ||||
Brian E. Granger
|
r4495 | |||
Parameters | ||||
---------- | ||||
Zachary Sailer
|
r13035 | kernel_id : uuid | ||
The uuid to associate the new kernel with. If this | ||||
Dale Jung
|
r16052 | is not None, this kernel will be persistent whenever it is | ||
Zachary Sailer
|
r13035 | requested. | ||
MinRK
|
r15420 | path : API path | ||
The API path (unicode, '/' delimited) for the cwd. | ||||
Will be transformed to an OS path relative to root_dir. | ||||
Thomas Kluyver
|
r17221 | kernel_name : str | ||
The name identifying which kernel spec to launch. This is ignored if | ||||
an existing kernel is returned, but it may be checked in the future. | ||||
Brian E. Granger
|
r4495 | """ | ||
Brian E. Granger
|
r4494 | if kernel_id is None: | ||
MinRK
|
r15420 | if path is not None: | ||
kwargs['cwd'] = self.cwd_for_path(path) | ||||
Thomas Kluyver
|
r17221 | kernel_id = super(MappingKernelManager, self).start_kernel( | ||
kernel_name=kernel_name, **kwargs) | ||||
Brian E. Granger
|
r4495 | self.log.info("Kernel started: %s" % kernel_id) | ||
Brian E. Granger
|
r4494 | self.log.debug("Kernel args: %r" % kwargs) | ||
MinRK
|
r10321 | # register callback for failed auto-restart | ||
self.add_restart_callback(kernel_id, | ||||
lambda : self._handle_kernel_died(kernel_id), | ||||
'dead', | ||||
) | ||||
Brian E. Granger
|
r4494 | else: | ||
Zachary Sailer
|
r13058 | self._check_kernel_id(kernel_id) | ||
Brian E. Granger
|
r4494 | self.log.info("Using existing kernel: %s" % kernel_id) | ||
return kernel_id | ||||
MinRK
|
r10330 | def shutdown_kernel(self, kernel_id, now=False): | ||
"""Shutdown a kernel by kernel_id""" | ||||
Zachary Sailer
|
r13058 | self._check_kernel_id(kernel_id) | ||
MinRK
|
r10330 | super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now) | ||
Zachary Sailer
|
r12983 | |||
MinRK
|
r15400 | def kernel_model(self, kernel_id): | ||
Zachary Sailer
|
r13037 | """Return a dictionary of kernel information described in the | ||
JSON standard model.""" | ||||
Zachary Sailer
|
r13058 | self._check_kernel_id(kernel_id) | ||
Thomas Kluyver
|
r17221 | model = {"id":kernel_id, | ||
"name": self._kernels[kernel_id].kernel_name} | ||||
Zachary Sailer
|
r12983 | return model | ||
MinRK
|
r15400 | def list_kernels(self): | ||
Zachary Sailer
|
r13037 | """Returns a list of kernel_id's of kernels running.""" | ||
Zachary Sailer
|
r13052 | kernels = [] | ||
kernel_ids = super(MappingKernelManager, self).list_kernel_ids() | ||||
for kernel_id in kernel_ids: | ||||
MinRK
|
r15400 | model = self.kernel_model(kernel_id) | ||
Zachary Sailer
|
r13052 | kernels.append(model) | ||
return kernels | ||||
MinRK
|
r10330 | |||
MinRK
|
r10295 | # override _check_kernel_id to raise 404 instead of KeyError | ||
Brian E. Granger
|
r4676 | def _check_kernel_id(self, kernel_id): | ||
"""Check a that a kernel_id exists and raise 404 if not.""" | ||||
if kernel_id not in self: | ||||
raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id) | ||||