From: Timofei Ivankov <128279579+deadlovelll@users.noreply.github.com> Date: Tue, 28 Jul 2026 06:59:49 +0000 (+0300) Subject: gh-150866: Fix asyncio shutdown_asyncgens to report BaseException (#150867) X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=353e6d3eff1172bd7df9340436b55a505f850a06;p=thirdparty%2FPython%2Fcpython.git gh-150866: Fix asyncio shutdown_asyncgens to report BaseException (#150867) --- diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 3836aaad326f..f26fba175b63 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -590,7 +590,7 @@ class BaseEventLoop(events.AbstractEventLoop): return_exceptions=True) for result, agen in zip(results, closing_agens): - if isinstance(result, Exception): + if isinstance(result, BaseException): self.call_exception_handler({ 'message': f'an error occurred during closing of ' f'asynchronous generator {agen!r}', diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 1622f19f0213..18afdca23163 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -1042,6 +1042,60 @@ class BaseEventLoopTests(test_utils.TestCase): asyncio.create_task(iter_one()) return status + def test_shutdown_asyncgens_reports_base_exceptions(self): + # gh-150866: shutdown_asyncgens silently swallowed exceptions that + # don't inherit from Exception raised during aclose() because the + # check was isinstance(result, Exception), but CancelledError inherits + # from BaseException. + self.loop._process_events = mock.Mock() + self.loop._write_to_self = mock.Mock() + + class MyBaseException(BaseException): + pass + + async def agen_cancel(): + try: + yield 1 + finally: + raise asyncio.CancelledError("agen got cancelled during cleanup") + + async def agen_base(): + try: + yield 1 + finally: + raise MyBaseException("base exc during cleanup") + + async def agen_value_error(): + try: + yield 1 + finally: + raise ValueError("agen failed during cleanup") + + caught = [] + + def handler(loop, context): + caught.append(context['exception']) + + async def main(): + loop = asyncio.get_running_loop() + loop.set_exception_handler(handler) + + g1 = agen_cancel() + g2 = agen_base() + g3 = agen_value_error() + await g1.__anext__() + await g2.__anext__() + await g3.__anext__() + + await loop.shutdown_asyncgens() + + self.loop.run_until_complete(main()) + self.assertEqual(len(caught), 3) + self.assertEqual( + {type(exc) for exc in caught}, + {asyncio.CancelledError, MyBaseException, ValueError}, + ) + def test_asyncgen_finalization_by_gc(self): # Async generators should be finalized when garbage collected. self.loop._process_events = mock.Mock() diff --git a/Misc/NEWS.d/next/Library/2026-06-03-18-41-42.gh-issue-150866.gXGyX-.rst b/Misc/NEWS.d/next/Library/2026-06-03-18-41-42.gh-issue-150866.gXGyX-.rst new file mode 100644 index 000000000000..57af6e954384 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-03-18-41-42.gh-issue-150866.gXGyX-.rst @@ -0,0 +1,2 @@ +Fix :meth:`asyncio.loop.shutdown_asyncgens` to report any +:exc:`BaseException` raised during asynchronous generator cleanup.