bpo-31556: asyncio.wait_for can cancel futures faster with timeout <= 0 (#3703)

This commit is contained in:
Victor K 2017-10-05 19:04:39 +03:00 committed by Yury Selivanov
parent 11045c9d8a
commit 4d07189788
3 changed files with 80 additions and 0 deletions

View File

@ -334,6 +334,15 @@ def wait_for(fut, timeout, *, loop=None):
if timeout is None: if timeout is None:
return (yield from fut) return (yield from fut)
if timeout <= 0:
fut = ensure_future(fut, loop=loop)
if fut.done():
return fut.result()
fut.cancel()
raise futures.TimeoutError()
waiter = loop.create_future() waiter = loop.create_future()
timeout_handle = loop.call_later(timeout, _release_waiter, waiter) timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
cb = functools.partial(_release_waiter, waiter) cb = functools.partial(_release_waiter, waiter)

View File

@ -661,6 +661,76 @@ class BaseTaskTests:
t.cancel() t.cancel()
self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t) self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t)
def test_wait_for_timeout_less_then_0_or_0_future_done(self):
def gen():
when = yield
self.assertAlmostEqual(0, when)
loop = self.new_test_loop(gen)
fut = self.new_future(loop)
fut.set_result('done')
ret = loop.run_until_complete(asyncio.wait_for(fut, 0, loop=loop))
self.assertEqual(ret, 'done')
self.assertTrue(fut.done())
self.assertAlmostEqual(0, loop.time())
def test_wait_for_timeout_less_then_0_or_0_coroutine_do_not_started(self):
def gen():
when = yield
self.assertAlmostEqual(0, when)
loop = self.new_test_loop(gen)
foo_started = False
@asyncio.coroutine
def foo():
nonlocal foo_started
foo_started = True
with self.assertRaises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.wait_for(foo(), 0, loop=loop))
self.assertAlmostEqual(0, loop.time())
self.assertEqual(foo_started, False)
def test_wait_for_timeout_less_then_0_or_0(self):
def gen():
when = yield
self.assertAlmostEqual(0.2, when)
when = yield 0
self.assertAlmostEqual(0, when)
for timeout in [0, -1]:
with self.subTest(timeout=timeout):
loop = self.new_test_loop(gen)
foo_running = None
@asyncio.coroutine
def foo():
nonlocal foo_running
foo_running = True
try:
yield from asyncio.sleep(0.2, loop=loop)
finally:
foo_running = False
return 'done'
fut = self.new_task(loop, foo())
with self.assertRaises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.wait_for(
fut, timeout, loop=loop))
self.assertTrue(fut.done())
# it should have been cancelled due to the timeout
self.assertTrue(fut.cancelled())
self.assertAlmostEqual(0, loop.time())
self.assertEqual(foo_running, False)
def test_wait_for(self): def test_wait_for(self):
def gen(): def gen():

View File

@ -0,0 +1 @@
Cancel asyncio.wait_for future faster if timeout <= 0