mirror of
https://github.com/python/cpython.git
synced 2025-11-03 03:22:27 +00:00
GH-95289: Always call uncancel() when parent cancellation is requested (GH-95602)
Co-authored-by: Guido van Rossum <guido@python.org>
(cherry picked from commit 2fef27589e)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
This commit is contained in:
parent
f2926358d1
commit
2d84fe59c0
3 changed files with 42 additions and 9 deletions
|
|
@ -54,21 +54,22 @@ class TaskGroup:
|
||||||
|
|
||||||
async def __aexit__(self, et, exc, tb):
|
async def __aexit__(self, et, exc, tb):
|
||||||
self._exiting = True
|
self._exiting = True
|
||||||
propagate_cancellation_error = None
|
|
||||||
|
|
||||||
if (exc is not None and
|
if (exc is not None and
|
||||||
self._is_base_error(exc) and
|
self._is_base_error(exc) and
|
||||||
self._base_error is None):
|
self._base_error is None):
|
||||||
self._base_error = exc
|
self._base_error = exc
|
||||||
|
|
||||||
if et is not None:
|
propagate_cancellation_error = \
|
||||||
if et is exceptions.CancelledError:
|
exc if et is exceptions.CancelledError else None
|
||||||
if self._parent_cancel_requested and not self._parent_task.uncancel():
|
if self._parent_cancel_requested:
|
||||||
# Do nothing, i.e. swallow the error.
|
# If this flag is set we *must* call uncancel().
|
||||||
pass
|
if self._parent_task.uncancel() == 0:
|
||||||
else:
|
# If there are no pending cancellations left,
|
||||||
propagate_cancellation_error = exc
|
# don't propagate CancelledError.
|
||||||
|
propagate_cancellation_error = None
|
||||||
|
|
||||||
|
if et is not None:
|
||||||
if not self._aborting:
|
if not self._aborting:
|
||||||
# Our parent task is being cancelled:
|
# Our parent task is being cancelled:
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextvars
|
import contextvars
|
||||||
|
import contextlib
|
||||||
from asyncio import taskgroups
|
from asyncio import taskgroups
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
@ -741,6 +741,37 @@ class TestTaskGroup(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
self.assertEqual(get_error_types(cm.exception), {ZeroDivisionError})
|
self.assertEqual(get_error_types(cm.exception), {ZeroDivisionError})
|
||||||
|
|
||||||
|
async def test_taskgroup_context_manager_exit_raises(self):
|
||||||
|
# See https://github.com/python/cpython/issues/95289
|
||||||
|
class CustomException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def raise_exc():
|
||||||
|
raise CustomException
|
||||||
|
|
||||||
|
@contextlib.asynccontextmanager
|
||||||
|
async def database():
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
raise CustomException
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
task = asyncio.current_task()
|
||||||
|
try:
|
||||||
|
async with taskgroups.TaskGroup() as tg:
|
||||||
|
async with database():
|
||||||
|
tg.create_task(raise_exc())
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
except* CustomException as err:
|
||||||
|
self.assertEqual(task.cancelling(), 0)
|
||||||
|
self.assertEqual(len(err.exceptions), 2)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.fail('CustomException not raised')
|
||||||
|
|
||||||
|
await asyncio.create_task(main())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Fix :class:`asyncio.TaskGroup` to propagate exception when :exc:`asyncio.CancelledError` was replaced with another exception by a context manger. Patch by Kumar Aditya and Guido van Rossum.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue