From 31dd71890f7237548d2749587c27274d4405bbd4 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Sat, 9 Sep 2017 09:48:34 -0700 Subject: [PATCH] Add the remaining property #20 (#21) - If timeout is not started yet or started unconstrained: remaining is None - If timeout is expired: remaining is 0.0 - All other: roughly amount of time before TimeoutError is triggered --- async_timeout/__init__.py | 12 ++++++++++-- tests/test_timeout.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/async_timeout/__init__.py b/async_timeout/__init__.py index cab6dba..addccb6 100644 --- a/async_timeout/__init__.py +++ b/async_timeout/__init__.py @@ -28,6 +28,7 @@ def __init__(self, timeout, *, loop=None): self._task = None self._cancelled = False self._cancel_handler = None + self._cancel_at = None def __enter__(self): return self._do_enter() @@ -47,14 +48,21 @@ def __aexit__(self, exc_type, exc_val, exc_tb): def expired(self): return self._cancelled + @property + def remaining(self): + if self._cancel_at is not None: + return max(self._cancel_at - self._loop.time(), 0.0) + else: + return None + def _do_enter(self): if self._timeout is not None: self._task = current_task(self._loop) if self._task is None: raise RuntimeError('Timeout context manager should be used ' 'inside a task') - self._cancel_handler = self._loop.call_later( - self._timeout, self._cancel_task) + self._cancel_at = self._loop.time() + self._timeout + self._cancel_handler = self._loop.call_at(self._cancel_at, self._cancel_task) return self def _do_exit(self, exc_type): diff --git a/tests/test_timeout.py b/tests/test_timeout.py index 95de1f3..7160bfe 100644 --- a/tests/test_timeout.py +++ b/tests/test_timeout.py @@ -227,3 +227,21 @@ def test_timeout_inner_other_error(loop): with timeout(0.01, loop=loop) as cm: raise RuntimeError assert not cm.expired + +@asyncio.coroutine +def test_timeout_remaining(loop): + with timeout(None, loop=loop) as cm: + assert cm.remaining is None + + t = timeout(1.0, loop=loop) + assert t.remaining is None + + with timeout(1.0, loop=loop) as cm: + yield from asyncio.sleep(0.1, loop=loop) + assert cm.remaining < 1.0 + + with pytest.raises(asyncio.TimeoutError): + with timeout(0.1, loop=loop) as cm: + yield from asyncio.sleep(0.5, loop=loop) + + assert cm.remaining == 0.0