Skip to content

Commit 62d46b4

Browse files
committed
Provide a TaskGroup in the kernel with a shielded CancelScope
1 parent 4586e34 commit 62d46b4

File tree

2 files changed

+17
-15
lines changed

2 files changed

+17
-15
lines changed

ipykernel/eventloops.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,9 @@ def loop_cocoa_exit(kernel):
401401
def loop_asyncio(kernel):
402402
"""Verify the asyncio event loop is supported."""
403403

404-
if not kernel.asyncio_event_loop or not kernel.asyncio_event_loop.is_running():
405-
msg = "The asyncio event loop is not running so is not supported."
406-
raise RuntimeError(msg)
404+
import asyncio
405+
406+
asyncio.get_running_loop()
407407

408408

409409
def set_qt_api_env_from_gui(gui):

ipykernel/kernelbase.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@
4242
import zmq_anyio
4343
from anyio import (
4444
TASK_STATUS_IGNORED,
45+
CancelScope,
4546
Event,
4647
create_memory_object_stream,
4748
create_task_group,
4849
sleep,
4950
to_thread,
5051
)
51-
from anyio.abc import TaskStatus
52+
from anyio.abc import TaskGroup, TaskStatus
5253
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
5354
from IPython.core.error import StdinNotImplementedError
5455
from jupyter_client.session import Session
@@ -131,6 +132,7 @@ class Kernel(SingletonConfigurable):
131132
_send_exec_request: Dict[dict[zmq_anyio.Socket, MemoryObjectSendStream]] = Dict()
132133
_main_subshell_ready = Instance(Event, ())
133134
asyncio_event_loop = Instance(asyncio.AbstractEventLoop, allow_none=True, read_only=True) # type:ignore[call-overload]
135+
tg = Instance(TaskGroup, read_only=True)
134136

135137
log: logging.Logger = Instance(logging.Logger, allow_none=True) # type:ignore[assignment]
136138

@@ -441,23 +443,23 @@ async def shell_main(self, subshell_id: str | None):
441443
if not socket.started.is_set():
442444
await tg.start(socket.start)
443445
tg.start_soon(self._process_shell, socket)
444-
tg.start_soon(self._execute_request_handler, receive_stream, subshell_id)
446+
tg.start_soon(self._execute_request_loop, receive_stream)
445447
if subshell_id is None:
446448
# Main subshell.
447-
await to_thread.run_sync(self.shell_stop.wait)
448-
tg.cancel_scope.cancel()
449+
with contextlib.suppress(RuntimeError):
450+
self.set_trait("asyncio_event_loop", asyncio.get_running_loop())
451+
async with create_task_group() as tg_main:
452+
with CancelScope(shield=True) as scope:
453+
self.set_trait("tg", tg_main)
454+
self._main_subshell_ready.set()
455+
await to_thread.run_sync(self.shell_stop.wait)
456+
scope.cancel()
457+
tg.cancel_scope.cancel()
449458
self._send_exec_request.pop(socket, None)
450-
self.set_trait("asyncio_event_loop", None)
451459
await send_stream.aclose()
452460
await receive_stream.aclose()
453461

454-
async def _execute_request_handler(
455-
self, receive_stream: MemoryObjectReceiveStream, subshell_id: str | None
456-
):
457-
if subshell_id is None:
458-
with contextlib.suppress(RuntimeError):
459-
self.set_trait("asyncio_event_loop", asyncio.get_running_loop())
460-
self._main_subshell_ready.set()
462+
async def _execute_request_loop(self, receive_stream: MemoryObjectReceiveStream):
461463
async with receive_stream:
462464
async for handler, (received_time, socket, idents, msg) in receive_stream:
463465
try:

0 commit comments

Comments
 (0)