kernelapp.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. """An application to launch a kernel by name in a local subprocess."""
  2. import os
  3. import signal
  4. import typing as t
  5. import uuid
  6. from jupyter_core.application import JupyterApp, base_flags
  7. from tornado.ioloop import IOLoop
  8. from traitlets import Unicode
  9. from . import __version__
  10. from .kernelspec import NATIVE_KERNEL_NAME, KernelSpecManager
  11. from .manager import KernelManager
  12. class KernelApp(JupyterApp):
  13. """Launch a kernel by name in a local subprocess."""
  14. version = __version__
  15. description = "Run a kernel locally in a subprocess"
  16. classes = [KernelManager, KernelSpecManager]
  17. aliases = {
  18. "kernel": "KernelApp.kernel_name",
  19. "ip": "KernelManager.ip",
  20. }
  21. flags = {"debug": base_flags["debug"]}
  22. kernel_name = Unicode(NATIVE_KERNEL_NAME, help="The name of a kernel type to start").tag(
  23. config=True
  24. )
  25. def initialize(self, argv: t.Union[str, t.Sequence[str], None] = None) -> None:
  26. """Initialize the application."""
  27. super().initialize(argv)
  28. cf_basename = "kernel-%s.json" % uuid.uuid4()
  29. self.config.setdefault("KernelManager", {}).setdefault(
  30. "connection_file", os.path.join(self.runtime_dir, cf_basename)
  31. )
  32. self.km = KernelManager(kernel_name=self.kernel_name, config=self.config)
  33. self.loop = IOLoop.current()
  34. self.loop.add_callback(self._record_started)
  35. def setup_signals(self) -> None:
  36. """Shutdown on SIGTERM or SIGINT (Ctrl-C)"""
  37. if os.name == "nt":
  38. return
  39. def shutdown_handler(signo: int, frame: t.Any) -> None:
  40. self.loop.add_callback_from_signal(self.shutdown, signo)
  41. for sig in [signal.SIGTERM, signal.SIGINT]:
  42. signal.signal(sig, shutdown_handler)
  43. def shutdown(self, signo: int) -> None:
  44. """Shut down the application."""
  45. self.log.info("Shutting down on signal %d", signo)
  46. self.km.shutdown_kernel()
  47. self.loop.stop()
  48. def log_connection_info(self) -> None:
  49. """Log the connection info for the kernel."""
  50. cf = self.km.connection_file
  51. self.log.info("Connection file: %s", cf)
  52. self.log.info("To connect a client: --existing %s", os.path.basename(cf))
  53. def _record_started(self) -> None:
  54. """For tests, create a file to indicate that we've started
  55. Do not rely on this except in our own tests!
  56. """
  57. fn = os.environ.get("JUPYTER_CLIENT_TEST_RECORD_STARTUP_PRIVATE")
  58. if fn is not None:
  59. with open(fn, "wb"):
  60. pass
  61. def start(self) -> None:
  62. """Start the application."""
  63. self.log.info("Starting kernel %r", self.kernel_name)
  64. try:
  65. self.km.start_kernel()
  66. self.log_connection_info()
  67. self.setup_signals()
  68. self.loop.start()
  69. finally:
  70. self.km.cleanup_resources()
  71. main = KernelApp.launch_instance