Syllabus Lesson 123 of 141 · When boring wins
When boring wins

A retry wrapper

Why it matters Retry logic is a magnet for features: jitter, policies, budgets, circuit breakers. Each is real somewhere. Most call sites need none of them.

A nightly export calls a partner API that sometimes drops the connection. The job runs once a night, on one worker, with nobody else calling the API at that hour. The team wants: retry on ConnectionError or TimeoutError (including their subclasses), waiting 0.5 s, then 1 s, then 2 s (doubling), at most four attempts in all; any other error is a bug and must surface at once.

The wrapper takes the sleep function as an argument, so tests can record waits instead of waiting.

retry(lambda: api.fetch_page(3), time.sleep)               # up to 4 attempts
retry(fetch, time.sleep, attempts=3, base=1)                 # waits 1, 2
Your turn

Write retry(fn, sleep, attempts=4, base=0.5). Call fn() and return its result. If it raises ConnectionError or TimeoutError (or a subclass), call sleep(base * 2 ** k) before retry number k + 1 (so 0.5, 1.0, 2.0 by default) and try again, up to attempts calls in all. If the last attempt fails, raise that error. Any other exception propagates at once, with no retry. Rule: at most 120 syntax nodes (code_size).

Spotted a problem in this lesson? Report it · Ask about this lesson

This exercise opens soon

Finish the lesson before it and this one unlocks. The whole course is free, no account needed, and you can read this lesson right now.