Expire cached values using an injected clock
An expiry rule should not depend on a real sleep in a test. Accept the current time as an argument and keep the cache representation explicit. Here an entry stores its value and absolute expiry. The boundary matters: an entry is expired at the expiry instant, not only after it. A cached None must be distinguishable from a missing entry.
entry = (None, 10.0)
print(9.0 < entry[1])Think before coding
An explicit found flag preserves cached None values. The injected time makes expiry tests immediate and deterministic. A real duration cache should use a monotonic clock to avoid wall-clock adjustments. Work through the example by hand before changing the starter. Decide what should happen for an empty input, a single value and a repeated value where those cases apply. The checks use several inputs so a hard-coded answer cannot stand in for the rule.
Your task
Write cache_lookup(cache, key, now). Entries are (value, expires_at). Return (True, value) only when present and now < expires_at. Remove expired entries. A missing or expired key returns (False, None).
The explanation opens here once every check passes.