Skip to content

require-injectable-retry-sleep

Pass an injectable sleep to a tenacity retry policy that waits between attempts.

Why

A tenacity policy built with a backoff wait and no sleep argument sleeps on the wall clock. Every test that exercises a transient failure pays the backoff, and exhausting an exponential policy costs the sum of every interval. Replacing tenacity's sleep from a test is a monkeypatch that SARJ445 forbids.

Fix

Build the policy in a factory or constructor that accepts a sleep callable and pass it as sleep=, or accept the wait strategy itself as a dependency. Tests then inject a no-op or recording sleeper, or wait_none().

Examples

Before — flagged A module-level retry decorator backs off on the wall clock
app/retry.py
from tenacity import retry, stop_after_attempt, wait_exponential
client_retry = retry(stop=stop_after_attempt(4), wait=wait_exponential(multiplier=2))
After — preferred A retry factory passes an injected sleep
app/retry.py
from collections.abc import Awaitable, Callable
from tenacity import AsyncRetrying, stop_after_attempt, wait_exponential
def client_retrying(sleep: Callable[[float], Awaitable[None]]) -> AsyncRetrying:
return AsyncRetrying(
sleep=sleep, stop=stop_after_attempt(4), wait=wait_exponential(multiplier=2)
)