From c42e2924051a2f234be75a942ca7607ff350b10d Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 12 Jul 2023 14:56:01 +0200 Subject: [PATCH 1/5] catch now raises typeerror on async handler --- CHANGES.rst | 3 +++ src/exceptiongroup/_catch.py | 9 ++++++++- tests/test_catch.py | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 040c6f9..6780ff9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. +**1.1.3** +- `catch` now raises a `TypeError` if passed an async exception handler instead of just giving a `RuntimeWarning` about the coroutine never being awaited. (#66, PR by John Litborn) + **1.1.2** - Changed handling of exceptions in exception group handler callbacks to not wrap a diff --git a/src/exceptiongroup/_catch.py b/src/exceptiongroup/_catch.py index 0be39b4..728cdd2 100644 --- a/src/exceptiongroup/_catch.py +++ b/src/exceptiongroup/_catch.py @@ -1,5 +1,6 @@ from __future__ import annotations +import inspect import sys from collections.abc import Callable, Iterable, Mapping from contextlib import AbstractContextManager @@ -49,9 +50,15 @@ def handle_exception(self, exc: BaseException) -> BaseException | None: matched, excgroup = excgroup.split(exc_types) if matched: try: - handler(matched) + result = handler(matched) except BaseException as new_exc: new_exceptions.append(new_exc) + else: + if inspect.iscoroutine(result): + raise TypeError( + f"Error trying to handle {matched!r} with {handler!r}. " + "Exception handler must be a sync function." + ) if not excgroup: break diff --git a/tests/test_catch.py b/tests/test_catch.py index 0af2fa0..82b58e5 100644 --- a/tests/test_catch.py +++ b/tests/test_catch.py @@ -162,3 +162,20 @@ def test_catch_subclass(): assert isinstance(lookup_errors[0], ExceptionGroup) exceptions = lookup_errors[0].exceptions assert isinstance(exceptions[0], KeyError) + + +def test_async_handler(): + import asyncio + + from exceptiongroup import ExceptionGroup, catch + + async def handler(eg): + # Log some stuff, then re-raise + raise eg + + async def main(): + with catch({TypeError: handler}): + raise ExceptionGroup("message", TypeError("uh-oh")) + + with pytest.raises(TypeError): + asyncio.run(main()) From 086920b214e04602285ed855a86a8b620ee99260 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 12 Jul 2023 15:59:13 +0200 Subject: [PATCH 2/5] fixes after review from agronholm --- CHANGES.rst | 2 +- src/exceptiongroup/_catch.py | 2 +- tests/test_catch.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6780ff9..0aa8e73 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,7 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. -**1.1.3** +**UNRELEASED** - `catch` now raises a `TypeError` if passed an async exception handler instead of just giving a `RuntimeWarning` about the coroutine never being awaited. (#66, PR by John Litborn) **1.1.2** diff --git a/src/exceptiongroup/_catch.py b/src/exceptiongroup/_catch.py index 728cdd2..f010d07 100644 --- a/src/exceptiongroup/_catch.py +++ b/src/exceptiongroup/_catch.py @@ -58,7 +58,7 @@ def handle_exception(self, exc: BaseException) -> BaseException | None: raise TypeError( f"Error trying to handle {matched!r} with {handler!r}. " "Exception handler must be a sync function." - ) + ) from excgroup if not excgroup: break diff --git a/tests/test_catch.py b/tests/test_catch.py index 82b58e5..f0cb835 100644 --- a/tests/test_catch.py +++ b/tests/test_catch.py @@ -177,5 +177,5 @@ async def main(): with catch({TypeError: handler}): raise ExceptionGroup("message", TypeError("uh-oh")) - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="Exception handler must be a sync function."): asyncio.run(main()) From 7702cecad26a4fb9dc34e93ebac8fec664b96ad8 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 12 Jul 2023 17:02:04 +0200 Subject: [PATCH 3/5] from exc instead of from excgroup --- src/exceptiongroup/_catch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exceptiongroup/_catch.py b/src/exceptiongroup/_catch.py index f010d07..2d82be1 100644 --- a/src/exceptiongroup/_catch.py +++ b/src/exceptiongroup/_catch.py @@ -58,7 +58,7 @@ def handle_exception(self, exc: BaseException) -> BaseException | None: raise TypeError( f"Error trying to handle {matched!r} with {handler!r}. " "Exception handler must be a sync function." - ) from excgroup + ) from exc if not excgroup: break From 1e3c95e1743666b1315f2f27089484c3033e9632 Mon Sep 17 00:00:00 2001 From: John Litborn <11260241+jakkdl@users.noreply.github.com> Date: Wed, 12 Jul 2023 17:10:28 +0200 Subject: [PATCH 4/5] Update tests/test_catch.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alex Grönholm --- tests/test_catch.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_catch.py b/tests/test_catch.py index f0cb835..caf1368 100644 --- a/tests/test_catch.py +++ b/tests/test_catch.py @@ -164,18 +164,15 @@ def test_catch_subclass(): assert isinstance(exceptions[0], KeyError) -def test_async_handler(): - import asyncio - - from exceptiongroup import ExceptionGroup, catch - +def test_async_handler(request): async def handler(eg): - # Log some stuff, then re-raise - raise eg + pass - async def main(): - with catch({TypeError: handler}): - raise ExceptionGroup("message", TypeError("uh-oh")) + def delegate(eg): + coro = handler(eg) + request.addfinalizer(coro.close) + return coro with pytest.raises(TypeError, match="Exception handler must be a sync function."): - asyncio.run(main()) + with catch({TypeError: delegate}): + raise ExceptionGroup("message", TypeError("uh-oh")) From 3aa76d8459e0b517803dd79c22d0c509c29e58f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Wed, 12 Jul 2023 18:42:37 +0300 Subject: [PATCH 5/5] Update CHANGES.rst --- CHANGES.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0aa8e73..eccb5b3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,9 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. **UNRELEASED** -- `catch` now raises a `TypeError` if passed an async exception handler instead of just giving a `RuntimeWarning` about the coroutine never being awaited. (#66, PR by John Litborn) +- `catch()` now raises a `TypeError` if passed an async exception handler instead of + just giving a `RuntimeWarning` about the coroutine never being awaited. (#66, PR by + John Litborn) **1.1.2**