Skip to content

Commit 73b8575

Browse files
elpransmiss-islington
authored andcommitted
bpo-37658: Fix asyncio.wait_for() to respect waited task status (pythonGH-21894)
Currently, if `asyncio.wait_for()` itself is cancelled it will always raise `CancelledError` regardless if the underlying task is still running. This is similar to a race with the timeout, which is handled already. (cherry picked from commit a2118a1) Co-authored-by: Elvis Pranskevichus <elvis@magic.io>
1 parent d7cd116 commit 73b8575

3 files changed

Lines changed: 24 additions & 3 deletions

File tree

Lib/asyncio/tasks.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,12 @@ async def wait_for(fut, timeout, *, loop=None):
460460
try:
461461
await waiter
462462
except exceptions.CancelledError:
463-
fut.remove_done_callback(cb)
464-
fut.cancel()
465-
raise
463+
if fut.done():
464+
return fut.result()
465+
else:
466+
fut.remove_done_callback(cb)
467+
fut.cancel()
468+
raise
466469

467470
if fut.done():
468471
return fut.result()

Lib/test/test_asyncio/test_tasks.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,22 @@ def gen():
11201120
res = loop.run_until_complete(task)
11211121
self.assertEqual(res, "ok")
11221122

1123+
def test_wait_for_cancellation_race_condition(self):
1124+
def gen():
1125+
yield 0.1
1126+
yield 0.1
1127+
yield 0.1
1128+
yield 0.1
1129+
1130+
loop = self.new_test_loop(gen)
1131+
1132+
fut = self.new_future(loop)
1133+
loop.call_later(0.1, fut.set_result, "ok")
1134+
task = loop.create_task(asyncio.wait_for(fut, timeout=1))
1135+
loop.call_later(0.1, task.cancel)
1136+
res = loop.run_until_complete(task)
1137+
self.assertEqual(res, "ok")
1138+
11231139
def test_wait_for_waits_for_task_cancellation(self):
11241140
loop = asyncio.new_event_loop()
11251141
self.addCleanup(loop.close)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:meth:`asyncio.wait_for` now properly handles races between cancellation of
2+
itself and the completion of the wrapped awaitable.

0 commit comments

Comments
 (0)