Skip to content

Commit a877478

Browse files
committed
simplify pending state logic and require multikernelmanager to take more responsibility
1 parent 659330f commit a877478

File tree

1 file changed

+35
-25
lines changed

1 file changed

+35
-25
lines changed

jupyter_client/manager.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Distributed under the terms of the Modified BSD License.
44
import asyncio
55
import os
6+
import functools
67
import re
78
import signal
89
import sys
@@ -51,6 +52,32 @@ class _ShutdownStatus(Enum):
5152
SigkillRequest = "SigkillRequest"
5253

5354

55+
def in_pending_state(method):
56+
"""Sets the kernel to a pending state by
57+
creating a fresh Future for the KernelManager's `ready`
58+
attribute. Once the method is finished, set the Future's results.
59+
"""
60+
@functools.wraps(method)
61+
async def wrapper(self, *args, **kwargs):
62+
# Create a future for the decorated method
63+
try:
64+
self._ready = Future()
65+
except RuntimeError:
66+
# No event loop running, use concurrent future
67+
self._ready = CFuture()
68+
try:
69+
# call wrapped method, await, and set the result or exception.
70+
out = await method(self, *args, **kwargs)
71+
# Add a small sleep to ensure tests can capture the state before done
72+
await asyncio.sleep(0.01)
73+
self._ready.set_result(None)
74+
return out
75+
except Exception as e:
76+
self._ready.set_exception(e)
77+
self.log.exception(self._ready.exception())
78+
return wrapper
79+
80+
5481
class KernelManager(ConnectionFileMixin):
5582
"""Manages a single kernel in a subprocess on this host.
5683
@@ -329,6 +356,7 @@ async def _async_post_start_kernel(self, **kw) -> None:
329356

330357
post_start_kernel = run_sync(_async_post_start_kernel)
331358

359+
@in_pending_state
332360
async def _async_start_kernel(self, **kw):
333361
"""Starts a kernel on this host in a separate process.
334362
@@ -341,25 +369,12 @@ async def _async_start_kernel(self, **kw):
341369
keyword arguments that are passed down to build the kernel_cmd
342370
and launching the kernel (e.g. Popen kwargs).
343371
"""
344-
done = self._ready.done()
345-
346-
try:
347-
kernel_cmd, kw = await ensure_async(self.pre_start_kernel(**kw))
372+
kernel_cmd, kw = await ensure_async(self.pre_start_kernel(**kw))
348373

349-
# launch the kernel subprocess
350-
self.log.debug("Starting kernel: %s", kernel_cmd)
351-
await ensure_async(self._launch_kernel(kernel_cmd, **kw))
352-
await ensure_async(self.post_start_kernel(**kw))
353-
if not done:
354-
# Add a small sleep to ensure tests can capture the state before done
355-
await asyncio.sleep(0.01)
356-
self._ready.set_result(None)
357-
358-
except Exception as e:
359-
if not done:
360-
self._ready.set_exception(e)
361-
self.log.exception(self._ready.exception())
362-
raise e
374+
# launch the kernel subprocess
375+
self.log.debug("Starting kernel: %s", kernel_cmd)
376+
await ensure_async(self._launch_kernel(kernel_cmd, **kw))
377+
await ensure_async(self.post_start_kernel(**kw))
363378

364379
start_kernel = run_sync(_async_start_kernel)
365380

@@ -434,6 +449,7 @@ async def _async_cleanup_resources(self, restart: bool = False) -> None:
434449

435450
cleanup_resources = run_sync(_async_cleanup_resources)
436451

452+
@in_pending_state
437453
async def _async_shutdown_kernel(self, now: bool = False, restart: bool = False):
438454
"""Attempts to stop the kernel process cleanly.
439455
@@ -452,10 +468,6 @@ async def _async_shutdown_kernel(self, now: bool = False, restart: bool = False)
452468
Will this kernel be restarted after it is shutdown. When this
453469
is True, connection files will not be cleaned up.
454470
"""
455-
# Shutdown is a no-op for a kernel that had a failed startup
456-
if self._ready.exception():
457-
return
458-
459471
self.shutting_down = True # Used by restarter to prevent race condition
460472
# Stop monitoring for restarting while we shutdown.
461473
self.stop_restarter()
@@ -473,6 +485,7 @@ async def _async_shutdown_kernel(self, now: bool = False, restart: bool = False)
473485

474486
await ensure_async(self.cleanup_resources(restart=restart))
475487

488+
476489
shutdown_kernel = run_sync(_async_shutdown_kernel)
477490

478491
async def _async_restart_kernel(self, now: bool = False, newports: bool = False, **kw) -> None:
@@ -503,9 +516,6 @@ async def _async_restart_kernel(self, now: bool = False, newports: bool = False,
503516
if self._launch_args is None:
504517
raise RuntimeError("Cannot restart the kernel. " "No previous call to 'start_kernel'.")
505518

506-
if not self._ready.done():
507-
raise RuntimeError("Cannot restart the kernel. " "Kernel has not fully started.")
508-
509519
# Stop currently running kernel.
510520
await ensure_async(self.shutdown_kernel(now=now, restart=True))
511521

0 commit comments

Comments
 (0)