Retrying Value Assertions (expect.poll)
assert_eventually can only poll the framework’s fixed dict-spec dispatch table
(text / image / pixel / window / clipboard / process / file / http). It cannot retry an
arbitrary value — an OCR’d total equalling "$42.00", a row count stabilising, a
custom predicate. expect_poll takes any zero-argument getter and any
matcher predicate and polls until it passes or the timeout elapses, with injectable
clock / sleep so it is deterministic in tests (the existing helper calls real
time.sleep). It mirrors Playwright’s expect.poll / web-first retrying assertions.
Pure-stdlib, imports no PySide6.
Headless API
from je_auto_control import (expect_poll, assert_poll, to_equal, to_contain,
to_be_greater_than, to_match_regex, to_be_stable)
# Poll an arbitrary getter until it matches.
result = expect_poll(lambda: read_cart_total(), to_equal("$42.00"),
timeout_s=8.0, interval_s=0.5)
if result.ok:
print("settled after", result.attempts, "tries")
# Raise on failure (assertion style).
assert_poll(lambda: row_count(), to_be_greater_than(0))
# Wait for a value to stop changing.
expect_poll(lambda: ocr_value(), to_be_stable(3))
expect_poll returns a PollResult (ok, value, attempts,
waited_s, description); assert_poll raises AutoControlActionException
when it never matches. The matcher factories are to_equal, to_contain,
to_be_greater_than, to_match_regex, to_be_truthy and to_be_stable(n)
(matches once the value repeats n times).
Executor command
AC_expect_poll re-runs a nested action (e.g. ["AC_get_clipboard"]) until a
key of its result matches op (truthy / equals / contains / gt
/ regex) versus expected, or timeout_s elapses — returning
{ok, value, attempts, waited_s}. It is exposed as the MCP tool ac_expect_poll
and as a Script Builder command under Flow.