https://github.com/python/cpython/commit/54a349f472af1b225a654ad69419aafc62fef653
commit: 54a349f472af1b225a654ad69419aafc62fef653
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-09-11T19:06:03+05:30
summary:

[3.14] gh-157213: Fix stale asyncio.gather() edges in the await graph 
(GH-157214) (#157313)

gh-157213: Fix stale asyncio.gather() edges in the await graph (GH-157214)
(cherry picked from commit 392ad7ed46a4cbaa4bfe0bdc4a38ccd2911a599d)

Co-authored-by: Timofei Ivankov <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-09-09-13-46-38.gh-issue-157213.rYg7pO.rst
M Lib/asyncio/tasks.py
M Lib/test/test_asyncio/test_graph.py
M Lib/test/test_asyncio/test_tasks.py

diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index f432cf0afa895a..3cf9cb59fd6a0e 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -770,6 +770,11 @@ def cancel(self, msg=None):
         return ret
 
 
+def _discard_awaited_by(children, waiter, outer):
+    for fut in children:
+        futures.future_discard_from_awaited_by(fut, waiter)
+
+
 def gather(*coros_or_futures, return_exceptions=False):
     """Return a future aggregating results from the given coroutines/futures.
 
@@ -903,6 +908,10 @@ def _done_callback(fut, cur_task=cur_task):
         children.append(fut)
 
     outer = _GatheringFuture(children, loop=loop)
+    if cur_task is not None:
+        # gh-157213: a child outliving gather() must lose the awaited-by edge
+        outer.add_done_callback(
+            functools.partial(_discard_awaited_by, children, cur_task))
     # Run done callbacks after GatheringFuture created so any post-processing
     # can be performed at this point
     # optimization: in the special case that *all* futures finished eagerly,
diff --git a/Lib/test/test_asyncio/test_graph.py 
b/Lib/test/test_asyncio/test_graph.py
index 50fa18e888fd22..b555982beef61c 100644
--- a/Lib/test/test_asyncio/test_graph.py
+++ b/Lib/test/test_asyncio/test_graph.py
@@ -175,6 +175,29 @@ async def main():
             ]
         ])
 
+    async def test_stack_gather_survivor(self):
+        # gh-157213: a child that outlives gather() must not be shown as 
awaited
+
+        async def fail():
+            raise ValueError
+
+        async def survivor():
+            await asyncio.Future()
+
+        t = asyncio.create_task(survivor(), name='survivor')
+        with self.assertRaises(ValueError):
+            await asyncio.gather(t, fail())
+
+        self.assertEqual(capture_test_stack(fut=t)[0], [
+            'T<survivor>',
+            ['a survivor'],
+            []
+        ])
+
+        t.cancel()
+        with self.assertRaises(asyncio.CancelledError):
+            await t
+
     async def test_stack_shield(self):
 
         stack_for_shield = None
diff --git a/Lib/test/test_asyncio/test_tasks.py 
b/Lib/test/test_asyncio/test_tasks.py
index f6f52107ab4914..2eeedb1dee6b36 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1231,6 +1231,25 @@ async def coro():
 
         self.loop.run_until_complete(self.new_task(self.loop, coro()))
 
+    def test_gather_discards_awaited_by_for_pending(self):
+        # gh-157213: a child outliving gather() must lose the awaited-by edge
+        async def fail():
+            raise ValueError
+
+        async def survivor():
+            await asyncio.Future()
+
+        async def coro():
+            t = self.new_task(self.loop, survivor())
+            with self.assertRaises(ValueError):
+                await asyncio.gather(t, fail())
+            self.assertFalse(t._asyncio_awaited_by)
+            t.cancel()
+            with self.assertRaises(asyncio.CancelledError):
+                await t
+
+        self.loop.run_until_complete(self.new_task(self.loop, coro()))
+
     def test_wait_really_done(self):
         # there is possibility that some tasks in the pending list
         # became done but their callbacks haven't all been called yet
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-09-13-46-38.gh-issue-157213.rYg7pO.rst 
b/Misc/NEWS.d/next/Library/2026-09-09-13-46-38.gh-issue-157213.rYg7pO.rst
new file mode 100644
index 00000000000000..801a76c3f3e7ad
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-09-13-46-38.gh-issue-157213.rYg7pO.rst
@@ -0,0 +1,2 @@
+Fix :func:`asyncio.gather` leaving stale await-graph edges on children that
+outlive it.

_______________________________________________
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