From 98a86a4e27e2d36a1a8a30f5ae49cf1bc96f8d11 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 9 Dec 2022 15:00:56 -0600 Subject: [PATCH] allow interrupt during restart --- jupyter_client/consoleapp.py | 8 ++++---- jupyter_client/kernelspecapp.py | 4 ++-- jupyter_client/manager.py | 12 ++++++++++++ tests/test_multikernelmanager.py | 6 ++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/jupyter_client/consoleapp.py b/jupyter_client/consoleapp.py index 526f79fd2..42814febd 100644 --- a/jupyter_client/consoleapp.py +++ b/jupyter_client/consoleapp.py @@ -32,12 +32,12 @@ # Aliases and Flags # ----------------------------------------------------------------------------- -flags = {} +flags: dict = {} flags.update(base_flags) # the flags that are specific to the frontend # these must be scrubbed before being passed to the kernel, # or it will raise an error on unrecognized flags -app_flags = { +app_flags: dict = { "existing": ( {"JupyterConsoleApp": {"existing": "kernel*.json"}}, "Connect to an existing kernel. If no argument specified, guess most recent", @@ -61,11 +61,11 @@ ) flags.update(app_flags) -aliases = {} +aliases: dict = {} aliases.update(base_aliases) # also scrub aliases from the frontend -app_aliases = { +app_aliases: dict = { "ip": "JupyterConsoleApp.ip", "transport": "JupyterConsoleApp.transport", "hb": "JupyterConsoleApp.hb_port", diff --git a/jupyter_client/kernelspecapp.py b/jupyter_client/kernelspecapp.py index 1ee076c0b..c4e4fa974 100644 --- a/jupyter_client/kernelspecapp.py +++ b/jupyter_client/kernelspecapp.py @@ -301,8 +301,8 @@ class KernelSpecApp(Application): } ) - aliases: t.Dict[str, object] = {} - flags: t.Dict[str, object] = {} + aliases: t.Dict[str, object] = {} # type:ignore[assignment] + flags: t.Dict[str, object] = {} # type:ignore[assignment] def start(self): if self.subapp is None: diff --git a/jupyter_client/manager.py b/jupyter_client/manager.py index 1271b692a..113db1495 100644 --- a/jupyter_client/manager.py +++ b/jupyter_client/manager.py @@ -594,6 +594,18 @@ async def _async_interrupt_kernel(self) -> None: Unlike ``signal_kernel``, this operation is well supported on all platforms. """ + if not self.has_kernel: + if self._ready is not None: + if isinstance(self._ready, CFuture): + ready = asyncio.ensure_future(self._ready) # type:ignore + else: + ready = self._ready + # Wait for a shutdown if one is in progress. + if self.shutting_down: + await ready + # Wait for a startup. + await ready + if self.has_kernel: assert self.kernel_spec is not None interrupt_mode = self.kernel_spec.interrupt_mode diff --git a/tests/test_multikernelmanager.py b/tests/test_multikernelmanager.py index e6e6576a0..882529aa9 100644 --- a/tests/test_multikernelmanager.py +++ b/tests/test_multikernelmanager.py @@ -311,10 +311,12 @@ async def _run_lifecycle(km, test_kid=None): assert kid in km assert kid in km.list_kernel_ids() assert len(km) == 1, f"{len(km)} != {1}" - await km.restart_kernel(kid, now=True) + # Ensure we can interrupt during a restart. + fut = km.restart_kernel(kid, now=True) + await km.interrupt_kernel(kid) assert await km.is_alive(kid) + await fut assert kid in km.list_kernel_ids() - await km.interrupt_kernel(kid) k = km.get_kernel(kid) assert isinstance(k, AsyncKernelManager) await km.shutdown_kernel(kid, now=True)