Skip to content

Commit 353e6d3

Browse files
authored
gh-150866: Fix asyncio shutdown_asyncgens to report BaseException (#150867)
1 parent c8eb79d commit 353e6d3

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

Lib/asyncio/base_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ async def shutdown_asyncgens(self):
590590
return_exceptions=True)
591591

592592
for result, agen in zip(results, closing_agens):
593-
if isinstance(result, Exception):
593+
if isinstance(result, BaseException):
594594
self.call_exception_handler({
595595
'message': f'an error occurred during closing of '
596596
f'asynchronous generator {agen!r}',

Lib/test/test_asyncio/test_base_events.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,60 @@ async def iter_one():
10421042
asyncio.create_task(iter_one())
10431043
return status
10441044

1045+
def test_shutdown_asyncgens_reports_base_exceptions(self):
1046+
# gh-150866: shutdown_asyncgens silently swallowed exceptions that
1047+
# don't inherit from Exception raised during aclose() because the
1048+
# check was isinstance(result, Exception), but CancelledError inherits
1049+
# from BaseException.
1050+
self.loop._process_events = mock.Mock()
1051+
self.loop._write_to_self = mock.Mock()
1052+
1053+
class MyBaseException(BaseException):
1054+
pass
1055+
1056+
async def agen_cancel():
1057+
try:
1058+
yield 1
1059+
finally:
1060+
raise asyncio.CancelledError("agen got cancelled during cleanup")
1061+
1062+
async def agen_base():
1063+
try:
1064+
yield 1
1065+
finally:
1066+
raise MyBaseException("base exc during cleanup")
1067+
1068+
async def agen_value_error():
1069+
try:
1070+
yield 1
1071+
finally:
1072+
raise ValueError("agen failed during cleanup")
1073+
1074+
caught = []
1075+
1076+
def handler(loop, context):
1077+
caught.append(context['exception'])
1078+
1079+
async def main():
1080+
loop = asyncio.get_running_loop()
1081+
loop.set_exception_handler(handler)
1082+
1083+
g1 = agen_cancel()
1084+
g2 = agen_base()
1085+
g3 = agen_value_error()
1086+
await g1.__anext__()
1087+
await g2.__anext__()
1088+
await g3.__anext__()
1089+
1090+
await loop.shutdown_asyncgens()
1091+
1092+
self.loop.run_until_complete(main())
1093+
self.assertEqual(len(caught), 3)
1094+
self.assertEqual(
1095+
{type(exc) for exc in caught},
1096+
{asyncio.CancelledError, MyBaseException, ValueError},
1097+
)
1098+
10451099
def test_asyncgen_finalization_by_gc(self):
10461100
# Async generators should be finalized when garbage collected.
10471101
self.loop._process_events = mock.Mock()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`asyncio.loop.shutdown_asyncgens` to report any
2+
:exc:`BaseException` raised during asynchronous generator cleanup.

0 commit comments

Comments
 (0)