Compose Timeouts With A Shared Deadline
A deadline helper that derives per-step timeouts from one shared budget so a slow first step cannot make later steps exceed the total.
Code
import time
from typing import Callable
class DeadlineExceededError(RuntimeError):
"""Raised when the shared budget no longer covers the requested step."""
class Deadline:
"""A single budget shared by every step of an operation."""
def __init__(self, budget_s: float, clock: Callable[[], float] = time.monotonic) -> None:
if budget_s <= 0:
raise ValueError("budget_s must be positive")
self._budget_s = budget_s
self._clock = clock
self._start = clock()
def remaining(self) -> float:
return max(0.0, self._budget_s - (self._clock() - self._start))
def expired(self) -> bool:
return self.remaining() <= 0.0
def require(self, needed_s: float) -> float:
"""Return the time granted for a step, or raise if it cannot fit."""
if needed_s <= 0:
raise ValueError("needed_s must be positive")
left = self.remaining()
if left < needed_s:
raise DeadlineExceededError(
"budget exhausted: %.3fs left, %.3fs needed" % (left, needed_s)
)
return needed_sReport a problem with this exampleOpens a pre-filled issue on GitHub.