Viewing file: test_waitfor.py (8.47 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
import asyncio import unittest import time
def tearDownModule(): asyncio.set_event_loop_policy(None)
# The following value can be used as a very small timeout: # it passes check "timeout > 0", but has almost # no effect on the test performance _EPSILON = 0.0001
class SlowTask: """ Task will run for this defined time, ignoring cancel requests """ TASK_TIMEOUT = 0.2
def __init__(self): self.exited = False
async def run(self): exitat = time.monotonic() + self.TASK_TIMEOUT
while True: tosleep = exitat - time.monotonic() if tosleep <= 0: break
try: await asyncio.sleep(tosleep) except asyncio.CancelledError: pass
self.exited = True
class AsyncioWaitForTest(unittest.IsolatedAsyncioTestCase):
async def test_asyncio_wait_for_cancelled(self): t = SlowTask()
waitfortask = asyncio.create_task( asyncio.wait_for(t.run(), t.TASK_TIMEOUT * 2)) await asyncio.sleep(0) waitfortask.cancel() await asyncio.wait({waitfortask})
self.assertTrue(t.exited)
async def test_asyncio_wait_for_timeout(self): t = SlowTask()
try: await asyncio.wait_for(t.run(), t.TASK_TIMEOUT / 2) except asyncio.TimeoutError: pass
self.assertTrue(t.exited)
async def test_wait_for_timeout_less_then_0_or_0_future_done(self): loop = asyncio.get_running_loop()
fut = loop.create_future() fut.set_result('done')
t0 = loop.time() ret = await asyncio.wait_for(fut, 0) t1 = loop.time()
self.assertEqual(ret, 'done') self.assertTrue(fut.done()) self.assertLess(t1 - t0, 0.1)
async def test_wait_for_timeout_less_then_0_or_0_coroutine_do_not_started(self): loop = asyncio.get_running_loop()
foo_started = False
async def foo(): nonlocal foo_started foo_started = True
with self.assertRaises(asyncio.TimeoutError): t0 = loop.time() await asyncio.wait_for(foo(), 0) t1 = loop.time()
self.assertEqual(foo_started, False) self.assertLess(t1 - t0, 0.1)
async def test_wait_for_timeout_less_then_0_or_0(self): loop = asyncio.get_running_loop()
for timeout in [0, -1]: with self.subTest(timeout=timeout): foo_running = None started = loop.create_future()
async def foo(): nonlocal foo_running foo_running = True started.set_result(None) try: await asyncio.sleep(10) finally: foo_running = False return 'done'
fut = asyncio.create_task(foo()) await started
with self.assertRaises(asyncio.TimeoutError): t0 = loop.time() await asyncio.wait_for(fut, timeout) t1 = loop.time()
self.assertTrue(fut.done()) # it should have been cancelled due to the timeout self.assertTrue(fut.cancelled()) self.assertEqual(foo_running, False) self.assertLess(t1 - t0, 0.1)
async def test_wait_for(self): loop = asyncio.get_running_loop() foo_running = None
async def foo(): nonlocal foo_running foo_running = True try: await asyncio.sleep(10) finally: foo_running = False return 'done'
fut = asyncio.create_task(foo())
with self.assertRaises(asyncio.TimeoutError): t0 = loop.time() await asyncio.wait_for(fut, 0.1) t1 = loop.time() self.assertTrue(fut.done()) # it should have been cancelled due to the timeout self.assertTrue(fut.cancelled()) self.assertLess(t1 - t0, 0.5) self.assertEqual(foo_running, False)
async def test_wait_for_blocking(self): async def coro(): return 'done'
res = await asyncio.wait_for(coro(), timeout=None) self.assertEqual(res, 'done')
async def test_wait_for_race_condition(self): loop = asyncio.get_running_loop()
fut = loop.create_future() task = asyncio.wait_for(fut, timeout=0.2) loop.call_later(0.1, fut.set_result, "ok") res = await task self.assertEqual(res, "ok")
async def test_wait_for_cancellation_race_condition(self): async def inner(): with self.assertRaises(asyncio.CancelledError): await asyncio.sleep(1) return 1
result = await asyncio.wait_for(inner(), timeout=.01) self.assertEqual(result, 1)
async def test_wait_for_waits_for_task_cancellation(self): task_done = False
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 = asyncio.create_task(inner())
with self.assertRaises(asyncio.TimeoutError) as cm: await asyncio.wait_for(inner_task, timeout=_EPSILON)
self.assertTrue(task_done) chained = cm.exception.__context__ self.assertEqual(type(chained), asyncio.CancelledError)
async def test_wait_for_waits_for_task_cancellation_w_timeout_0(self): 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 = asyncio.create_task(inner()) await asyncio.sleep(_EPSILON) await asyncio.wait_for(inner_task, timeout=0)
with self.assertRaises(asyncio.TimeoutError) as cm: await foo()
self.assertTrue(task_done) chained = cm.exception.__context__ self.assertEqual(type(chained), asyncio.CancelledError)
async def test_wait_for_reraises_exception_during_cancellation(self): class FooException(Exception): pass
async def foo(): async def inner(): try: await asyncio.sleep(0.2) finally: raise FooException
inner_task = asyncio.create_task(inner())
await asyncio.wait_for(inner_task, timeout=_EPSILON)
with self.assertRaises(FooException): await foo()
async def test_wait_for_self_cancellation(self): async def inner(): try: await asyncio.sleep(0.3) except asyncio.CancelledError: try: await asyncio.sleep(0.3) except asyncio.CancelledError: await asyncio.sleep(0.3)
return 42
inner_task = asyncio.create_task(inner())
wait = asyncio.wait_for(inner_task, timeout=0.1)
# Test that wait_for itself is properly cancellable # even when the initial task holds up the initial cancellation. task = asyncio.create_task(wait) await asyncio.sleep(0.2) task.cancel()
with self.assertRaises(asyncio.CancelledError): await task
self.assertEqual(await inner_task, 42)
async def _test_cancel_wait_for(self, timeout): loop = asyncio.get_running_loop()
async def blocking_coroutine(): fut = loop.create_future() # Block: fut result is never set await fut
task = asyncio.create_task(blocking_coroutine())
wait = asyncio.create_task(asyncio.wait_for(task, timeout)) loop.call_soon(wait.cancel)
with self.assertRaises(asyncio.CancelledError): await wait
# Python issue #23219: cancelling the wait must also cancel the task self.assertTrue(task.cancelled())
async def test_cancel_blocking_wait_for(self): await self._test_cancel_wait_for(None)
async def test_cancel_wait_for(self): await self._test_cancel_wait_for(60.0)
if __name__ == '__main__': unittest.main()
|