Skip to content

Commit 73ae431

Browse files
committed
handle shutdown and restart for pending kernels in the multikernelmanager
1 parent 8076482 commit 73ae431

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

jupyter_client/multikernelmanager.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ async def _async_shutdown_kernel(
214214
restart : bool
215215
Will the kernel be restarted?
216216
"""
217+
kernel = self.get_kernel(kernel_id)
218+
if getattr(self, 'use_pending_kernels', False):
219+
# Make sure the previous kernel started before trying to shutdown.
220+
if not kernel.ready.done():
221+
raise RuntimeError("Kernel is in a pending state. Cannot shutdown.")
222+
# If the kernel didn't start properly, no need to shutdown.
223+
elif kernel.ready.exception():
224+
return
217225
self.log.info("Kernel shutdown: %s" % kernel_id)
218226
if kernel_id in self._starting_kernels:
219227
try:
@@ -291,8 +299,7 @@ def signal_kernel(self, kernel_id: str, signum: int) -> None:
291299
"""
292300
self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
293301

294-
@kernel_method
295-
def restart_kernel(self, kernel_id: str, now: bool = False) -> None:
302+
async def _async_restart_kernel(self, kernel_id: str, now: bool = False) -> None:
296303
"""Restart a kernel by its uuid, keeping the same ports.
297304
298305
Parameters
@@ -307,7 +314,15 @@ def restart_kernel(self, kernel_id: str, now: bool = False) -> None:
307314
In all cases the kernel is restarted, the only difference is whether
308315
it is given a chance to perform a clean shutdown or not.
309316
"""
317+
kernel = self.get_kernel(kernel_id)
318+
if getattr(self, 'use_pending_kernels', False):
319+
if not kernel.ready.done():
320+
raise RuntimeError("Kernel is in a pending state. Cannot restart.")
321+
out = await ensure_async(kernel.restart_kernel(now=now))
310322
self.log.info("Kernel restarted: %s" % kernel_id)
323+
return out
324+
325+
restart_kernel = run_sync(_async_restart_kernel)
311326

312327
@kernel_method
313328
def is_alive(self, kernel_id: str) -> bool:
@@ -475,5 +490,6 @@ class AsyncMultiKernelManager(MultiKernelManager):
475490
).tag(config=True)
476491

477492
start_kernel = MultiKernelManager._async_start_kernel
493+
restart_kernel = MultiKernelManager._async_restart_kernel
478494
shutdown_kernel = MultiKernelManager._async_shutdown_kernel
479495
shutdown_all = MultiKernelManager._async_shutdown_all

jupyter_client/tests/test_multikernelmanager.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
TIMEOUT = 30
3030

3131

32+
async def now(awaitable):
33+
"""Use this function ensure that this awaitable
34+
happens before other awaitables defined after it.
35+
"""
36+
(out,) = await asyncio.gather(awaitable)
37+
return out
38+
39+
3240
class TestKernelManager(TestCase):
3341
def setUp(self):
3442
self.env_patch = test_env()
@@ -357,32 +365,32 @@ async def test_shutdown_all_while_starting(self):
357365
@gen_test
358366
async def test_use_pending_kernels(self):
359367
km = self._get_pending_kernels_km()
360-
kid = await km.start_kernel(stdout=PIPE, stderr=PIPE)
368+
kid = await now(km.start_kernel(stdout=PIPE, stderr=PIPE))
361369
kernel = km.get_kernel(kid)
362370
assert not kernel.ready.done()
363371
assert kid in km
364372
assert kid in km.list_kernel_ids()
365373
assert len(km) == 1, f"{len(km)} != {1}"
366-
await kernel.ready
367-
await km.restart_kernel(kid, now=True)
374+
await now(kernel.ready)
375+
await now(km.restart_kernel(kid, now=True))
368376
assert await km.is_alive(kid)
369377
assert kid in km.list_kernel_ids()
370-
await km.interrupt_kernel(kid)
378+
await now(km.interrupt_kernel(kid))
371379
k = km.get_kernel(kid)
372380
assert isinstance(k, AsyncKernelManager)
373-
await km.shutdown_kernel(kid, now=True)
381+
await now(km.shutdown_kernel(kid, now=True))
374382
assert kid not in km, f"{kid} not in {km}"
375383

376384
@gen_test
377385
async def test_use_pending_kernels_early_restart(self):
378386
km = self._get_pending_kernels_km()
379-
kid = await km.start_kernel(stdout=PIPE, stderr=PIPE)
387+
kid = await now(km.start_kernel(stdout=PIPE, stderr=PIPE))
380388
kernel = km.get_kernel(kid)
381389
assert not kernel.ready.done()
382390
with pytest.raises(RuntimeError):
383391
await km.restart_kernel(kid, now=True)
384-
await kernel.ready
385-
await km.shutdown_kernel(kid, now=True)
392+
await now(kernel.ready)
393+
await now(km.shutdown_kernel(kid, now=True))
386394
assert kid not in km, f"{kid} not in {km}"
387395

388396
@gen_test

0 commit comments

Comments
 (0)