https://github.com/python/cpython/commit/dff7a46d2134ea39106e208b1c4fef9c2758aa62
commit: dff7a46d2134ea39106e208b1c4fef9c2758aa62
branch: 3.15
author: Timofei Ivankov <[email protected]>
committer: hugovk <[email protected]>
date: 2026-09-15T16:48:18+03:00
summary:

[3.15] gh-155433: Fix TaskGroup losing outside cancellation after cancel() 
(#157329)

files:
A Misc/NEWS.d/next/Library/2026-08-09-16-14-38.gh-issue-155433.KL7tHV.rst
M Lib/asyncio/taskgroups.py
M Lib/test/test_asyncio/test_taskgroups.py

diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py
index 1fdffcc81a88655..1d3d26a5a56f48b 100644
--- a/Lib/asyncio/taskgroups.py
+++ b/Lib/asyncio/taskgroups.py
@@ -116,6 +116,7 @@ async def _aexit(self, et, exc):
         # can be cancelled multiple times if our parent task
         # is being cancelled repeatedly (or even once, when
         # our own cancellation is already in progress)
+        pending_cancellation_error = None
         while self._tasks:
             if self._on_completed_fut is None:
                 self._on_completed_fut = self._loop.create_future()
@@ -123,6 +124,7 @@ async def _aexit(self, et, exc):
             try:
                 await self._on_completed_fut
             except exceptions.CancelledError as ex:
+                pending_cancellation_error = ex
                 if not self._aborting:
                     # Our parent task is being cancelled:
                     #
@@ -151,6 +153,9 @@ async def _aexit(self, et, exc):
                 # If there are no pending cancellations left,
                 # don't propagate CancelledError.
                 propagate_cancellation_error = None
+            elif propagate_cancellation_error is None:
+                # gh-155433: the remaining cancellation is not ours, don't 
drop it
+                propagate_cancellation_error = pending_cancellation_error
 
         # Propagate CancelledError if there is one, except if there
         # are other errors -- those have priority.
diff --git a/Lib/test/test_asyncio/test_taskgroups.py 
b/Lib/test/test_asyncio/test_taskgroups.py
index b1bff6b476c5dd0..12438f1b8eb55c0 100644
--- a/Lib/test/test_asyncio/test_taskgroups.py
+++ b/Lib/test/test_asyncio/test_taskgroups.py
@@ -1165,6 +1165,34 @@ async def child(tg):
             task = tg.create_task(child(tg))
         self.assertTrue(task.cancelled())
 
+    async def test_taskgroup_cancel_keeps_outer_cancellation(self):
+        # gh-155433: any cancellation from outside the group must propagate.
+        cancelling = asyncio.Event()
+        release = asyncio.Event()
+
+        async def child():
+            try:
+                await asyncio.sleep(10)
+            finally:
+                # The group is cancelling: it has cancelled its parent task
+                # and is waiting for this task to finish.
+                cancelling.set()
+                await release.wait()
+
+        async def body():
+            async with asyncio.TaskGroup() as tg:
+                tg.create_task(child())
+                await asyncio.sleep(0)
+                tg.cancel()
+
+        task = asyncio.create_task(body())
+        await cancelling.wait()
+        task.cancel('message')
+        release.set()
+        with self.assertRaises(asyncio.CancelledError) as cm:
+            await task
+        self.assertEqual('message', cm.exception.args[0])
+
     async def test_taskgroup_cancel_before_exception(self):
         async def raise_exc(parent_tg: asyncio.TaskGroup):
             parent_tg.cancel()
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-09-16-14-38.gh-issue-155433.KL7tHV.rst 
b/Misc/NEWS.d/next/Library/2026-08-09-16-14-38.gh-issue-155433.KL7tHV.rst
new file mode 100644
index 000000000000000..dc8961e976abbc2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-09-16-14-38.gh-issue-155433.KL7tHV.rst
@@ -0,0 +1,2 @@
+Fix :class:`asyncio.TaskGroup` losing outside cancellation after
+``cancel()``.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to