|
"""An application to launch a kernel by name in a local subprocess.""" |
|
import os |
|
import signal |
|
import typing as t |
|
import uuid |
|
|
|
from jupyter_core.application import JupyterApp, base_flags |
|
from tornado.ioloop import IOLoop |
|
from traitlets import Unicode |
|
|
|
from . import __version__ |
|
from .kernelspec import NATIVE_KERNEL_NAME, KernelSpecManager |
|
from .manager import KernelManager |
|
|
|
|
|
class KernelApp(JupyterApp): |
|
"""Launch a kernel by name in a local subprocess.""" |
|
|
|
version = __version__ |
|
description = "Run a kernel locally in a subprocess" |
|
|
|
classes = [KernelManager, KernelSpecManager] |
|
|
|
aliases = { |
|
"kernel": "KernelApp.kernel_name", |
|
"ip": "KernelManager.ip", |
|
} |
|
flags = {"debug": base_flags["debug"]} |
|
|
|
kernel_name = Unicode(NATIVE_KERNEL_NAME, help="The name of a kernel type to start").tag( |
|
config=True |
|
) |
|
|
|
def initialize(self, argv: t.Union[str, t.Sequence[str], None] = None) -> None: |
|
"""Initialize the application.""" |
|
super().initialize(argv) |
|
|
|
cf_basename = "kernel-%s.json" % uuid.uuid4() |
|
self.config.setdefault("KernelManager", {}).setdefault( |
|
"connection_file", os.path.join(self.runtime_dir, cf_basename) |
|
) |
|
self.km = KernelManager(kernel_name=self.kernel_name, config=self.config) |
|
|
|
self.loop = IOLoop.current() |
|
self.loop.add_callback(self._record_started) |
|
|
|
def setup_signals(self) -> None: |
|
"""Shutdown on SIGTERM or SIGINT (Ctrl-C)""" |
|
if os.name == "nt": |
|
return |
|
|
|
def shutdown_handler(signo: int, frame: t.Any) -> None: |
|
self.loop.add_callback_from_signal(self.shutdown, signo) |
|
|
|
for sig in [signal.SIGTERM, signal.SIGINT]: |
|
signal.signal(sig, shutdown_handler) |
|
|
|
def shutdown(self, signo: int) -> None: |
|
"""Shut down the application.""" |
|
self.log.info("Shutting down on signal %d", signo) |
|
self.km.shutdown_kernel() |
|
self.loop.stop() |
|
|
|
def log_connection_info(self) -> None: |
|
"""Log the connection info for the kernel.""" |
|
cf = self.km.connection_file |
|
self.log.info("Connection file: %s", cf) |
|
self.log.info("To connect a client: --existing %s", os.path.basename(cf)) |
|
|
|
def _record_started(self) -> None: |
|
"""For tests, create a file to indicate that we've started |
|
|
|
Do not rely on this except in our own tests! |
|
""" |
|
fn = os.environ.get("JUPYTER_CLIENT_TEST_RECORD_STARTUP_PRIVATE") |
|
if fn is not None: |
|
with open(fn, "wb"): |
|
pass |
|
|
|
def start(self) -> None: |
|
"""Start the application.""" |
|
self.log.info("Starting kernel %r", self.kernel_name) |
|
try: |
|
self.km.start_kernel() |
|
self.log_connection_info() |
|
self.setup_signals() |
|
self.loop.start() |
|
finally: |
|
self.km.cleanup_resources() |
|
|
|
|
|
main = KernelApp.launch_instance |
|
|