runapp.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. """A Jupyter console app to run files."""
  2. # Copyright (c) Jupyter Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from __future__ import annotations
  5. import atexit
  6. import signal
  7. import sys
  8. import typing as t
  9. from jupyter_core.application import JupyterApp, base_aliases, base_flags
  10. from traitlets import Any, Dict, Float
  11. from traitlets.config import catch_config_error
  12. from . import __version__
  13. from .consoleapp import JupyterConsoleApp, app_aliases, app_flags
  14. OUTPUT_TIMEOUT = 10
  15. # copy flags from mixin:
  16. flags = dict(base_flags)
  17. # start with mixin frontend flags:
  18. frontend_flags_dict = dict(app_flags)
  19. # update full dict with frontend flags:
  20. flags.update(frontend_flags_dict)
  21. # copy flags from mixin
  22. aliases = dict(base_aliases)
  23. # start with mixin frontend flags
  24. frontend_aliases_dict = dict(app_aliases)
  25. # load updated frontend flags into full dict
  26. aliases.update(frontend_aliases_dict)
  27. # get flags&aliases into sets, and remove a couple that
  28. # shouldn't be scrubbed from backend flags:
  29. frontend_aliases = set(frontend_aliases_dict.keys())
  30. frontend_flags = set(frontend_flags_dict.keys())
  31. class RunApp(JupyterApp, JupyterConsoleApp):
  32. """An Jupyter Console app to run files."""
  33. version = __version__
  34. name = "jupyter run"
  35. description = """Run Jupyter kernel code."""
  36. flags = Dict(flags)
  37. aliases = Dict(aliases)
  38. frontend_aliases = Any(frontend_aliases)
  39. frontend_flags = Any(frontend_flags)
  40. kernel_timeout = Float(
  41. 60,
  42. config=True,
  43. help="""Timeout for giving up on a kernel (in seconds).
  44. On first connect and restart, the console tests whether the
  45. kernel is running and responsive by sending kernel_info_requests.
  46. This sets the timeout in seconds for how long the kernel can take
  47. before being presumed dead.
  48. """,
  49. )
  50. def parse_command_line(self, argv: list[str] | None = None) -> None:
  51. """Parse the command line arguments."""
  52. super().parse_command_line(argv)
  53. self.build_kernel_argv(self.extra_args)
  54. self.filenames_to_run = self.extra_args[:]
  55. @catch_config_error
  56. def initialize(self, argv: list[str] | None = None) -> None: # type:ignore[override]
  57. """Initialize the app."""
  58. self.log.debug("jupyter run: initialize...")
  59. super().initialize(argv)
  60. JupyterConsoleApp.initialize(self)
  61. signal.signal(signal.SIGINT, self.handle_sigint)
  62. if self.kernel_manager:
  63. atexit.register(self.kernel_manager.shutdown_kernel)
  64. def handle_sigint(self, *args: t.Any) -> None:
  65. """Handle SIGINT."""
  66. if self.kernel_manager:
  67. self.kernel_manager.interrupt_kernel()
  68. else:
  69. self.log.error("Cannot interrupt kernels we didn't start.\n")
  70. def start(self) -> None:
  71. """Start the application."""
  72. self.log.debug("jupyter run: starting...")
  73. super().start()
  74. self.kernel_client.wait_for_ready(timeout=self.kernel_timeout)
  75. if self.filenames_to_run:
  76. for filename in self.filenames_to_run:
  77. self.log.debug("jupyter run: executing `%s`", filename)
  78. with open(filename) as fp:
  79. code = fp.read()
  80. reply = self.kernel_client.execute_interactive(code, timeout=OUTPUT_TIMEOUT)
  81. return_code = 0 if reply["content"]["status"] == "ok" else 1
  82. if return_code:
  83. msg = f"jupyter-run error running '{filename}'"
  84. raise Exception(msg)
  85. else:
  86. self.log.debug("jupyter run: executing from stdin")
  87. code = sys.stdin.read()
  88. reply = self.kernel_client.execute_interactive(code, timeout=OUTPUT_TIMEOUT)
  89. return_code = 0 if reply["content"]["status"] == "ok" else 1
  90. if return_code:
  91. msg = "jupyter-run error running 'stdin'"
  92. raise Exception(msg)
  93. main = launch_new_instance = RunApp.launch_instance
  94. if __name__ == "__main__":
  95. main()