import time
from ash_flare import RestartPolicy, SupervisorHandle, SupervisorSpec
def test_invalid_durations_are_rejected():
spec = SupervisorSpec("durations")
for call in (
lambda: spec.with_shutdown_timeout(-1.0),
lambda: spec.with_shutdown_timeout(float("nan")),
lambda: spec.with_restart_delay(-0.5),
lambda: spec.with_restart_backoff(0.1, float("inf")),
):
try:
call()
except ValueError:
continue
raise AssertionError("an out-of-range duration must raise ValueError")
spec.with_shutdown_timeout(0.5)
spec.with_restart_delay(0.01)
spec.with_restart_backoff(0.01, 0.05)
def _needs_an_argument(a):
return a
def test_a_typeerror_from_the_worker_body_is_not_retried():
runs = []
def worker(should_stop):
runs.append(1)
_needs_an_argument()
spec = SupervisorSpec("body-typeerror")
spec.with_restart_delay(5.0) spec.add_worker("boom", RestartPolicy.temporary(), worker)
handle = SupervisorHandle.start(spec)
time.sleep(0.6)
handle.shutdown()
assert len(runs) == 1, f"worker body ran {len(runs)} times, expected once"
def test_each_supported_signature_is_called():
seen = {}
def zero_arg():
seen["zero"] = True
def one_arg(should_stop):
seen["one"] = should_stop.is_set() is False
def star_args(*args):
seen["star"] = len(args)
def keyword_only(should_stop, *, unused=1):
seen["kwonly"] = True
spec = SupervisorSpec("signatures")
spec.with_restart_delay(5.0)
for name, fn in (
("zero", zero_arg),
("one", one_arg),
("star", star_args),
("kwonly", keyword_only),
):
spec.add_worker(name, RestartPolicy.temporary(), fn)
handle = SupervisorHandle.start(spec)
time.sleep(0.6)
handle.shutdown()
assert seen.get("zero") is True
assert seen.get("one") is True
assert seen.get("star") == 1, "a *args worker receives the should_stop handle"
assert seen.get("kwonly") is True, "keyword-only parameters do not count as positional"
def test_stopping_waits_for_the_python_body():
state = {"finished": False}
def cooperative(should_stop):
while not should_stop():
time.sleep(0.02)
time.sleep(0.15) state["finished"] = True
spec = SupervisorSpec("cooperative")
spec.with_shutdown_timeout(3.0)
spec.add_worker("coop", RestartPolicy.permanent(), cooperative)
handle = SupervisorHandle.start(spec)
time.sleep(0.3)
handle.terminate_child("coop")
assert state["finished"] is True, "terminate_child returned before the body finished"
handle.shutdown()
def test_an_uncooperative_worker_is_bounded_by_the_shutdown_timeout():
def stubborn(should_stop):
time.sleep(5)
spec = SupervisorSpec("stubborn")
spec.with_shutdown_timeout(0.3)
spec.add_worker("stubborn", RestartPolicy.permanent(), stubborn)
handle = SupervisorHandle.start(spec)
time.sleep(0.3)
began = time.monotonic()
handle.terminate_child("stubborn")
elapsed = time.monotonic() - began
handle.shutdown()
assert elapsed < 2.0, f"abandoning the worker took {elapsed:.2f}s, timeout was 0.3s"
if __name__ == "__main__":
tests = [value for name, value in sorted(globals().items()) if name.startswith("test_")]
for test in tests:
test()
print(f"✓ {test.__name__}")
print(f"✓ {len(tests)} Python binding tests passed")