1 2# Copyright (c) Jupyter Development Team. 3# Distributed under the terms of the Modified BSD License. 4 5import logging 6import signal 7import queue 8import time 9import sys 10 11from traitlets.config import catch_config_error 12from traitlets import ( 13 Instance, Dict, Unicode, Bool, List, CUnicode, Any, Float 14) 15from jupyter_core.application import ( 16 JupyterApp, base_flags, base_aliases 17) 18 19from . import __version__ 20from .consoleapp import JupyterConsoleApp, app_aliases, app_flags 21 22OUTPUT_TIMEOUT = 10 23 24# copy flags from mixin: 25flags = dict(base_flags) 26# start with mixin frontend flags: 27frontend_flags = dict(app_flags) 28# update full dict with frontend flags: 29flags.update(frontend_flags) 30 31# copy flags from mixin 32aliases = dict(base_aliases) 33# start with mixin frontend flags 34frontend_aliases = dict(app_aliases) 35# load updated frontend flags into full dict 36aliases.update(frontend_aliases) 37 38# get flags&aliases into sets, and remove a couple that 39# shouldn't be scrubbed from backend flags: 40frontend_aliases = set(frontend_aliases.keys()) 41frontend_flags = set(frontend_flags.keys()) 42 43class RunApp(JupyterApp, JupyterConsoleApp): 44 version = __version__ 45 name = "jupyter run" 46 description = """Run Jupyter kernel code.""" 47 flags = Dict(flags) 48 aliases = Dict(aliases) 49 frontend_aliases = Any(frontend_aliases) 50 frontend_flags = Any(frontend_flags) 51 kernel_timeout = Float(60, config=True, 52 help="""Timeout for giving up on a kernel (in seconds). 53 54 On first connect and restart, the console tests whether the 55 kernel is running and responsive by sending kernel_info_requests. 56 This sets the timeout in seconds for how long the kernel can take 57 before being presumed dead. 58 """ 59 ) 60 61 def parse_command_line(self, argv=None): 62 super().parse_command_line(argv) 63 self.build_kernel_argv(self.extra_args) 64 self.filenames_to_run = self.extra_args[:] 65 66 @catch_config_error 67 def initialize(self, argv=None): 68 self.log.debug("jupyter run: initialize...") 69 super().initialize(argv) 70 JupyterConsoleApp.initialize(self) 71 signal.signal(signal.SIGINT, self.handle_sigint) 72 self.init_kernel_info() 73 74 def handle_sigint(self, *args): 75 if self.kernel_manager: 76 self.kernel_manager.interrupt_kernel() 77 else: 78 self.log.error("Cannot interrupt kernels we didn't start.\n") 79 80 def init_kernel_info(self): 81 """Wait for a kernel to be ready, and store kernel info""" 82 timeout = self.kernel_timeout 83 tic = time.time() 84 self.kernel_client.hb_channel.unpause() 85 msg_id = self.kernel_client.kernel_info() 86 while True: 87 try: 88 reply = self.kernel_client.get_shell_msg(timeout=1) 89 except queue.Empty as e: 90 if (time.time() - tic) > timeout: 91 raise RuntimeError("Kernel didn't respond to kernel_info_request") from e 92 else: 93 if reply['parent_header'].get('msg_id') == msg_id: 94 self.kernel_info = reply['content'] 95 return 96 97 def start(self): 98 self.log.debug("jupyter run: starting...") 99 super().start() 100 if self.filenames_to_run: 101 for filename in self.filenames_to_run: 102 self.log.debug("jupyter run: executing `%s`" % filename) 103 with open(filename) as fp: 104 code = fp.read() 105 reply = self.kernel_client.execute_interactive(code, timeout=OUTPUT_TIMEOUT) 106 return_code = 0 if reply['content']['status'] == 'ok' else 1 107 if return_code: 108 raise Exception("jupyter-run error running '%s'" % filename) 109 else: 110 code = sys.stdin.read() 111 reply = self.kernel_client.execute_interactive(code, timeout=OUTPUT_TIMEOUT) 112 return_code = 0 if reply['content']['status'] == 'ok' else 1 113 if return_code: 114 raise Exception("jupyter-run error running 'stdin'") 115 116main = launch_new_instance = RunApp.launch_instance 117 118if __name__ == '__main__': 119 main() 120