-
-
Notifications
You must be signed in to change notification settings - Fork 103
Expand file tree
/
Copy pathtimeout.py
More file actions
68 lines (51 loc) · 2.2 KB
/
timeout.py
File metadata and controls
68 lines (51 loc) · 2.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
"""Timeout helper for state invocations.
Provides a ``timeout()`` function that returns an :class:`~statemachine.invoke.IInvoke`
handler. When a state is entered, the handler waits for the given duration; if the state
is not exited before the timer expires, an event is sent to the machine.
Example::
from statemachine.contrib.timeout import timeout
class MyMachine(StateChart):
waiting = State(initial=True, invoke=timeout(5, on="expired"))
timed_out = State(final=True)
expired = waiting.to(timed_out)
"""
from typing import TYPE_CHECKING
from typing import Any
if TYPE_CHECKING:
from statemachine.invoke import InvokeContext
class _Timeout:
"""IInvoke handler that waits for a duration and optionally sends an event."""
def __init__(self, duration: float, on: "str | None" = None):
self.duration = duration
self.on = on
def run(self, ctx: "InvokeContext") -> Any:
"""Wait for the timeout duration, then optionally send an event.
If the owning state is exited before the timer expires (``ctx.cancelled``
is set), the handler returns immediately without sending anything.
"""
fired = not ctx.cancelled.wait(timeout=self.duration)
if not fired:
# State was exited before the timeout — nothing to do.
return None
if self.on is not None:
ctx.send(self.on)
return None
def __repr__(self) -> str:
args = f"{self.duration}"
if self.on is not None:
args += f", on={self.on!r}"
return f"timeout({args})"
def timeout(duration: float, *, on: "str | None" = None) -> _Timeout:
"""Create a timeout invoke handler.
Args:
duration: Time in seconds to wait before firing.
on: Event name to send when the timeout expires. If ``None``, the
standard ``done.invoke.<state>`` event fires via invoke completion.
Returns:
An :class:`~statemachine.invoke.IInvoke`-compatible handler.
Raises:
ValueError: If *duration* is not positive.
"""
if duration <= 0:
raise ValueError(f"timeout duration must be positive, got {duration}")
return _Timeout(duration=duration, on=on)