Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lib/asyncio/base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ async def shutdown_asyncgens(self):
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}',
Expand Down
54 changes: 54 additions & 0 deletions Lib/test/test_asyncio/test_base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,60 @@ async def iter_one():
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()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :meth:`asyncio.loop.shutdown_asyncgens` to report any
:exc:`BaseException` raised during asynchronous generator cleanup.
Loading