Skip to content

Commit

Permalink
Adopt ExceptionGroup handling without an external library (#894)
Browse files Browse the repository at this point in the history
* Adopt ExceptionGroup handling without an external library

* Python <3.11 behaviour not changed

Fix: #893

---------

Co-authored-by: Aleksei Stepanov <alekseis@nvidia.com>
  • Loading branch information
penguinolog and Aleksei Stepanov committed Jun 7, 2024
1 parent 58d3996 commit 5a40654
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 9 deletions.
4 changes: 2 additions & 2 deletions test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ tornado>=5
twisted
trio
zmq
exceptiongroups
exceptiongroups;python_version<'3.11'
windows-curses;sys_platform=="win32"
pyserial
# for test run
coverage[toml]
coverage[toml]
20 changes: 13 additions & 7 deletions urwid/event_loop/trio_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@
from __future__ import annotations

import logging
import sys
import typing

import exceptiongroup
import trio

from .abstract_loop import EventLoop, ExitMainLoop

if sys.version_info < (3, 11):
from exceptiongroup import BaseExceptionGroup # pylint: disable=redefined-builtin # backport

if typing.TYPE_CHECKING:
import io
from collections.abc import Awaitable, Callable, Hashable, Mapping
Expand Down Expand Up @@ -156,8 +159,10 @@ def run(self) -> None:

emulate_idle_callbacks = _TrioIdleCallbackInstrument(self._idle_callbacks)

with exceptiongroup.catch({BaseException: self._handle_main_loop_exception}):
try:
trio.run(self._main_task, instruments=[emulate_idle_callbacks])
except BaseException as exc:
self._handle_main_loop_exception(exc)

async def run_async(self) -> None:
"""Starts the main loop and blocks asynchronously until the main loop exits.
Expand All @@ -179,12 +184,14 @@ async def run_async(self) -> None:

emulate_idle_callbacks = _TrioIdleCallbackInstrument(self._idle_callbacks)

with exceptiongroup.catch({BaseException: self._handle_main_loop_exception}):
try:
trio.lowlevel.add_instrument(emulate_idle_callbacks)
try:
await self._main_task()
finally:
trio.lowlevel.remove_instrument(emulate_idle_callbacks)
except BaseException as exc:
self._handle_main_loop_exception(exc)

def watch_file(
self,
Expand Down Expand Up @@ -225,12 +232,11 @@ def _handle_main_loop_exception(self, exc: BaseException) -> None:
"""Handles exceptions raised from the main loop, catching ExitMainLoop
instead of letting it propagate through.
Note that since Trio may collect multiple exceptions from tasks into a
Trio MultiError, we cannot simply use a try..catch clause, we need a
helper function like this.
Note that since Trio may collect multiple exceptions from tasks into an ExceptionGroup,
we cannot simply use a try..catch clause, we need a helper function like this.
"""
self._idle_callbacks.clear()
if isinstance(exc, exceptiongroup.BaseExceptionGroup) and len(exc.exceptions) == 1:
if isinstance(exc, BaseExceptionGroup) and len(exc.exceptions) == 1:
exc = exc.exceptions[0]

if isinstance(exc, ExitMainLoop):
Expand Down

0 comments on commit 5a40654

Please sign in to comment.