Skip to content

Commit 655b5d0

Browse files
authored
Refactor shift methods (#253)
1 parent 9ea9f9b commit 655b5d0

File tree

4 files changed

+59
-42
lines changed

4 files changed

+59
-42
lines changed

CHANGES.rst

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
CHANGES
22
=======
33

4-
4.0.0 (2019-11-XX)
4+
4.0.0 (2021-11-01)
55
------------------
66

7-
* Add the elapsed property (#24)
7+
* Implemented ``timeout_at(deadline)`` (#117)
88

9-
* Implement `timeout.at(when)` (#117)
9+
* Supported ``timeout.deadline`` and ``timeout.expired`` properties.
1010

11-
* Deprecate synchronous context manager usage
11+
* Drooped ``timeout.remaining`` property: it can be calculated as
12+
``timeout.deadline - loop.time()``
13+
14+
* Dropped ``timeout.timeout`` property that returns a relative timeout based on the
15+
timeout object creation time; the absolute ``timeout.deadline`` should be used
16+
instead.
17+
18+
* Added the deadline modification methods: ``timeout.reject()``,
19+
``timeout.shift(delay)``, ``timeout.update(deadline)``.
20+
21+
* Deprecated synchronous context manager usage
1222

1323
3.0.1 (2018-10-09)
1424
------------------
@@ -31,29 +41,29 @@ CHANGES
3141
2.0.0 (2017-10-09)
3242
------------------
3343

34-
* Changed `timeout <= 0` behaviour
44+
* Changed ``timeout <= 0`` behaviour
3545

36-
* Backward incompatibility change, prior this version `0` was
37-
shortcut for `None`
38-
* when timeout <= 0 `TimeoutError` raised faster
46+
* Backward incompatibility change, prior this version ``0`` was
47+
shortcut for ``None``
48+
* when timeout <= 0 ``TimeoutError`` raised faster
3949

4050
1.4.0 (2017-09-09)
4151
------------------
4252

43-
* Implement `remaining` property (#20)
53+
* Implement ``remaining`` property (#20)
4454

4555
* If timeout is not started yet or started unconstrained:
46-
`remaining` is `None`
47-
* If timeout is expired: `remaining` is `0.0`
48-
* All others: roughly amount of time before `TimeoutError` is triggered
56+
``remaining`` is ``None``
57+
* If timeout is expired: ``remaining`` is ``0.0``
58+
* All others: roughly amount of time before ``TimeoutError`` is triggered
4959

5060
1.3.0 (2017-08-23)
5161
------------------
5262

5363
* Don't suppress nested exception on timeout. Exception context points
54-
on cancelled line with suspended `await` (#13)
64+
on cancelled line with suspended ``await`` (#13)
5565

56-
* Introduce `.timeout` property (#16)
66+
* Introduce ``.timeout`` property (#16)
5767

5868
* Add methods for using as async context manager (#9)
5969

@@ -74,7 +84,7 @@ CHANGES
7484
1.1.0 (2016-10-20)
7585
------------------
7686

77-
* Rename to `async-timeout`
87+
* Rename to ``async-timeout``
7888

7989
1.0.0 (2016-09-09)
8090
------------------

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ Not finished yet timeout can be rescheduled by ``shift_by()``
7373
or ``shift_to()`` methods::
7474

7575
async with timeout(1.5) as cm:
76-
cm.shift_by(1) # add another second on waiting
77-
cm.shift_to(loop.time() + 5) # reschedule to now+5 seconds
76+
cm.shift(1) # add another second on waiting
77+
cm.update(loop.time() + 5) # reschedule to now+5 seconds
7878

7979
Rescheduling is forbidden if the timeout is expired or after exit from ``async with``
8080
code block.

async_timeout/__init__.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def timeout(delay: Optional[float]) -> "Timeout":
3838
def timeout_at(deadline: Optional[float]) -> "Timeout":
3939
"""Schedule the timeout at absolute time.
4040
41-
deadline arguments points on the time in the same clock system
41+
deadline argument points on the time in the same clock system
4242
as loop.time().
4343
4444
Please note: it is not POSIX time but a time with
@@ -95,7 +95,7 @@ def __init__(
9595
if deadline is None:
9696
self._deadline = None # type: Optional[float]
9797
else:
98-
self.shift_to(deadline)
98+
self.update(deadline)
9999

100100
def __enter__(self) -> "Timeout":
101101
warnings.warn(
@@ -150,19 +150,28 @@ def _reject(self) -> None:
150150
self._timeout_handler.cancel()
151151
self._timeout_handler = None
152152

153-
def shift_by(self, delay: float) -> None:
153+
def shift(self, delay: float) -> None:
154154
"""Advance timeout on delay seconds.
155155
156156
The delay can be negative.
157+
158+
Raise RuntimeError if shift is called when deadline is not scheduled
157159
"""
158-
now = self._loop.time()
159-
self.shift_to(now + delay)
160+
deadline = self._deadline
161+
if deadline is None:
162+
raise RuntimeError("cannot shift timeout if deadline is not scheduled")
163+
self.update(deadline + delay)
164+
165+
def update(self, deadline: float) -> None:
166+
"""Set deadline to absolute value.
167+
168+
deadline argument points on the time in the same clock system
169+
as loop.time().
160170
161-
def shift_to(self, deadline: float) -> None:
162-
"""Advance timeout on the abdelay seconds.
171+
If new deadline is in the past the timeout is raised immediatelly.
163172
164-
If new deadline is in the past
165-
the timeout is raised immediatelly.
173+
Please note: it is not POSIX time but a time with
174+
undefined starting base, e.g. the time of the system power on.
166175
"""
167176
if self._state == _State.EXIT:
168177
raise RuntimeError("cannot reschedule after exit from context manager")

tests/test_timeout.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -286,34 +286,32 @@ async def test_async_no_timeout() -> None:
286286

287287

288288
@pytest.mark.asyncio
289-
async def test_shift_to() -> None:
289+
async def test_shift() -> None:
290290
loop = asyncio.get_event_loop()
291291
t0 = loop.time()
292292
async with timeout(1) as cm:
293293
t1 = loop.time()
294294
assert cm.deadline is not None
295295
assert t0 + 1 <= cm.deadline <= t1 + 1
296-
cm.shift_to(t1 + 1)
297-
assert t1 + 1 <= cm.deadline <= t1 + 1.1
296+
cm.shift(1)
297+
assert t0 + 2 <= cm.deadline <= t0 + 2.1
298298

299299

300300
@pytest.mark.asyncio
301-
async def test_shift_by() -> None:
302-
loop = asyncio.get_event_loop()
303-
t0 = loop.time()
304-
async with timeout(1) as cm:
305-
t1 = loop.time()
306-
assert cm.deadline is not None
307-
assert t0 + 1 <= cm.deadline <= t1 + 1
308-
cm.shift_by(1)
309-
assert t1 + 0.999 <= cm.deadline <= t1 + 1.1
301+
async def test_shift_nonscheduled() -> None:
302+
async with timeout(None) as cm:
303+
with pytest.raises(
304+
RuntimeError,
305+
match="cannot shift timeout if deadline is not scheduled",
306+
):
307+
cm.shift(1)
310308

311309

312310
@pytest.mark.asyncio
313311
async def test_shift_by_negative_expired() -> None:
314312
async with timeout(1) as cm:
315313
with pytest.raises(asyncio.CancelledError):
316-
cm.shift_by(-1)
314+
cm.shift(-1)
317315

318316

319317
@pytest.mark.asyncio
@@ -322,7 +320,7 @@ async def test_shift_by_expired() -> None:
322320
with pytest.raises(asyncio.CancelledError):
323321
await asyncio.sleep(10)
324322
with pytest.raises(RuntimeError, match="cannot reschedule expired timeout"):
325-
cm.shift_by(10)
323+
cm.shift(10)
326324

327325

328326
@pytest.mark.asyncio
@@ -333,7 +331,7 @@ async def test_shift_to_expired() -> None:
333331
with pytest.raises(asyncio.CancelledError):
334332
await asyncio.sleep(10)
335333
with pytest.raises(RuntimeError, match="cannot reschedule expired timeout"):
336-
cm.shift_to(t0 + 10)
334+
cm.update(t0 + 10)
337335

338336

339337
@pytest.mark.asyncio
@@ -343,7 +341,7 @@ async def test_shift_by_after_cm_exit() -> None:
343341
with pytest.raises(
344342
RuntimeError, match="cannot reschedule after exit from context manager"
345343
):
346-
cm.shift_by(1)
344+
cm.shift(1)
347345

348346

349347
@pytest.mark.asyncio

0 commit comments

Comments
 (0)