https://github.com/python/cpython/commit/43b4a9c4bb2a7dd45c56aaff059b7ae7a7a93efd
commit: 43b4a9c4bb2a7dd45c56aaff059b7ae7a7a93efd
branch: main
author: Timofei Ivankov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-09-11T13:54:27Z
summary:

gh-157058: Fix missing awaited-by edge in asyncio.wait_for(fut, 0) (#157059)

files:
A Misc/NEWS.d/next/Library/2026-09-07-12-24-11.gh-issue-157058.2SoQU7.rst
M Lib/asyncio/tasks.py
M Lib/test/test_asyncio/test_graph.py

diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index 3ef3a578d93ab4..29c5d9af9b4029 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -543,6 +543,10 @@ async def _cancel_and_wait(fut):
     cb = functools.partial(_release_waiter, waiter)
     fut.add_done_callback(cb)
 
+    # gh-157058: awaiting the waiter leaves no edge on fut, add it here
+    cur_task = current_task()
+    futures.future_add_to_awaited_by(fut, cur_task)
+
     try:
         fut.cancel()
         # We cannot wait on *fut* directly to make
@@ -550,6 +554,7 @@ async def _cancel_and_wait(fut):
         await waiter
     finally:
         fut.remove_done_callback(cb)
+        futures.future_discard_from_awaited_by(fut, cur_task)
 
 
 class _AsCompletedIterator:
diff --git a/Lib/test/test_asyncio/test_graph.py 
b/Lib/test/test_asyncio/test_graph.py
index f0fda848927dd4..a442a346ff06d9 100644
--- a/Lib/test/test_asyncio/test_graph.py
+++ b/Lib/test/test_asyncio/test_graph.py
@@ -173,6 +173,33 @@ class FakeCoro:
 
         self.assertEqual(len(result.call_stack), 2)
 
+    async def test_stack_wait_for_non_positive_timeout(self):
+        # gh-157058: wait_for(fut, 0) must still record the waiter
+        cleanup = asyncio.Future()
+
+        async def worker():
+            try:
+                await asyncio.Future()
+            finally:
+                await cleanup
+
+        async def probe(t):
+            await asyncio.wait_for(t, 0)
+
+        t = asyncio.ensure_future(worker())
+        p = asyncio.create_task(probe(t), name='probe')
+        for _ in range(5):
+            await asyncio.sleep(0)
+
+        stack = capture_test_stack(fut=t)
+
+        cleanup.set_result(None)
+        await asyncio.gather(p, t, return_exceptions=True)
+
+        self.assertEqual(stack[0][2], [
+            ['T<probe>', ['a _cancel_and_wait', 'a wait_for', 'a probe'], []],
+        ])
+
     async def test_stack_gather(self):
 
         stack_for_deep = None
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-07-12-24-11.gh-issue-157058.2SoQU7.rst 
b/Misc/NEWS.d/next/Library/2026-09-07-12-24-11.gh-issue-157058.2SoQU7.rst
new file mode 100644
index 00000000000000..9122ae0919dcb1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-07-12-24-11.gh-issue-157058.2SoQU7.rst
@@ -0,0 +1,2 @@
+:func:`asyncio.wait_for` with a non-positive timeout now records the waiter
+in the call graph.

_______________________________________________
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