"""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 stateis 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)"""fromtypingimportTYPE_CHECKINGfromtypingimportAnyifTYPE_CHECKING:fromstatemachine.invokeimportInvokeContextclass_Timeout:"""IInvoke handler that waits for a duration and optionally sends an event."""def__init__(self,duration:float,on:"str | None"=None):self.duration=durationself.on=ondefrun(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=notctx.cancelled.wait(timeout=self.duration)ifnotfired:# State was exited before the timeout — nothing to do.returnNoneifself.onisnotNone:ctx.send(self.on)returnNonedef__repr__(self)->str:args=f"{self.duration}"ifself.onisnotNone:args+=f", on={self.on!r}"returnf"timeout({args})"
[docs]deftimeout(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. """ifduration<=0:raiseValueError(f"timeout duration must be positive, got {duration}")return_Timeout(duration=duration,on=on)