bpo-32751: Wait for task cancel in asyncio.wait_for() when timeout <= 0 (GH-21895) (GH-21963)

When I was fixing bpo-32751 back in GH-7216 I missed the case when
*timeout* is zero or negative.  This takes care of that.

Props to @aaliddell for noticing the inconsistency.
(cherry picked from commit c517fc7121)

Co-authored-by: Elvis Pranskevichus <elvis@magic.io>
This commit is contained in:
Miss Islington (bot) 2020-08-26 10:14:59 -07:00 committed by GitHub
parent d7cd1164c1
commit 1036ccb55d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 2 deletions

View File

@ -445,8 +445,13 @@ async def wait_for(fut, timeout, *, loop=None):
if fut.done():
return fut.result()
fut.cancel()
raise exceptions.TimeoutError()
await _cancel_and_wait(fut, loop=loop)
try:
fut.result()
except exceptions.CancelledError as exc:
raise exceptions.TimeoutError() from exc
else:
raise exceptions.TimeoutError()
waiter = loop.create_future()
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)

View File

@ -1131,6 +1131,9 @@ class BaseTaskTests:
nonlocal task_done
try:
await asyncio.sleep(0.2)
except asyncio.CancelledError:
await asyncio.sleep(_EPSILON)
raise
finally:
task_done = True
@ -1145,6 +1148,34 @@ class BaseTaskTests:
chained = cm.exception.__context__
self.assertEqual(type(chained), asyncio.CancelledError)
def test_wait_for_waits_for_task_cancellation_w_timeout_0(self):
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
task_done = False
async def foo():
async def inner():
nonlocal task_done
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
await asyncio.sleep(_EPSILON)
raise
finally:
task_done = True
inner_task = self.new_task(loop, inner())
await asyncio.sleep(_EPSILON)
await asyncio.wait_for(inner_task, timeout=0)
with self.assertRaises(asyncio.TimeoutError) as cm:
loop.run_until_complete(foo())
self.assertTrue(task_done)
chained = cm.exception.__context__
self.assertEqual(type(chained), asyncio.CancelledError)
def test_wait_for_reraises_exception_during_cancellation(self):
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)

View File

@ -0,0 +1,3 @@
When cancelling the task due to a timeout, :meth:`asyncio.wait_for` will now
wait until the cancellation is complete also in the case when *timeout* is
<= 0, like it does with positive timeouts.