retrying review
retrying 1.4.2 is a synchronous Python decorator and callable wrapper for repeating work after selected exceptions or rejected return values. Its keyword arguments cap attempts or elapsed milliseconds and calculate fixed, random, exponential, or jittered sleep. A bare retry decorator has the dangerous default of retrying every exception forever with no delay. Version 1.4.2 updates packaging, notices, and project documentation rather than adding a new execution model. It has no coroutine-aware runner or persistent retry state.
retrying 1.4.2 installed in 0.2 seconds and used 1 MB in our sandbox with no dependencies or audit findings. Keep it for compatible synchronous call paths; new async or observable retry work should start with Tenacity or Stamina.
We installed it
| Install | ✓ · 0.2s | 1 package on disk · 1 MB |
| Import | ✓ | import retrying in 0.11s · pure Python · requires Python >=3.6 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does retrying install cleanly?
Yes. In a fresh container with an empty cache, pip install retrying finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does retrying need to run?
Python >=3.6, and nothing compiled: it is pure Python. In our run import retrying succeeded in 0.11s.
retrying or tenacity: which should you use?
tenacity: Use it for new sync or async code needing composable policies, callbacks, and statistics. retrying 1.4.2 installed in 0.2 seconds and used 1 MB in our sandbox with no dependencies or audit findings.
When should you not use retrying?
You are writing new application code. Tenacity or Stamina supplies async support, callbacks, statistics, and safer composition.
Use it if
- Legacy synchronous code already depends on retrying's exact exception and timing behavior.
- A small script needs bounded retries without another installed dependency.
- Polling should repeat on a sentinel result such as None as well as on exceptions.
- A third-party callable needs runtime-selected policy through Retrying.call.
- You are writing new application code. Tenacity or Stamina supplies async support, callbacks, statistics, and safer composition.
- The function is async. The decorator sees a returned coroutine, and its sleeps use time.sleep.
- The proposed call is bare retry. The documented default loops immediately and without a stopping limit.
- Typed configuration is required. Durations are flat integer millisecond arguments and the package has no py.typed marker.
- The operation can duplicate a payment, publish, or write. A retry library cannot add idempotency to the side effect.
Setup reality
We installed retrying 1.4.2 in a clean unprivileged Python 3.12 Bookworm container in 0.2 seconds. One package occupied 1 MB. It has zero direct dependencies, contains pure Python, and declares Python 3.6 or newer. import retrying succeeded in 0.11 seconds. pip-audit found zero known vulnerabilities. Our inspection found no py.typed marker and no license value in the installed metadata.
There are no credentials, native tools, services, or files to configure. Every duration is expressed in milliseconds: wait_fixed=2 sleeps for 2 ms, while stop_max_delay=10000 represents 10 seconds. Always set an attempts, elapsed-time, or custom stop rule because the defaults have no end.
By default every exception repeats, including TypeError and other programming mistakes. Use retry_on_exception to select transient failures. retry_on_result has reversed-looking semantics: true means reject this value and try again. Exhausted result retries raise RetryError; exhausted exception retries normally raise the original exception unless wrap_exception is true.
Several base wait policies resolve to the longest computed delay, after which jitter is added. Exponential wait starts above the multiplier on the first retry, so test the actual schedule against a latency budget. Version 1.4.2 does not understand async cancellation, HTTP Retry-After, or transport idempotency. Those policies belong in a newer retry layer or surrounding application code.
Patterns
Retry at most three calls cap-attempts
@retry(stop_max_attempt_number=3)
def fetch():
response = session.get(url, timeout=5)
response.raise_for_status()
return response.textA stopping option makes the loop finite; bare retry has unlimited immediate attempts.
Use a 10-second elapsed budget cap-elapsed-time
@retry(stop_max_delay=10_000)
def connect():
return open_connection()The setting is milliseconds, and the last slow call may finish after the configured elapsed threshold.
Pause 2 seconds between calls fixed-wait
@retry(stop_max_attempt_number=5, wait_fixed=2_000)
def poll():
return request_status()Add jitter when many clients may reach the same recovering service together.
Back off with a 10-second ceiling exponential-wait
@retry(stop_max_attempt_number=6, wait_exponential_multiplier=1_000, wait_exponential_max=10_000)
def call_remote():
return remote_call()The first computed exponential delay exceeds the multiplier; assert the schedule in a test.
Repeat only connection failures filter-exceptions
def transient(error):
return isinstance(error, (TimeoutError, ConnectionError))
@retry(retry_on_exception=transient, stop_max_attempt_number=4, wait_fixed=500)
def read_remote(): return client.read()When the predicate returns false, that exception is raised immediately.
Poll until a value appears retry-result
@retry(retry_on_result=lambda value: value is None, stop_max_attempt_number=5, wait_fixed=1_000)
def take_job(): return queue.take_nowait()True rejects the returned value; exhaustion raises RetryError instead of returning the last None.
Apply policy without a decorator wrap-callable
policy = Retrying(stop_max_attempt_number=3, wait_fixed=200)
result = policy.call(session.get, url, timeout=5)This works for a function you do not own or a policy chosen at runtime.
Leave coroutines to an async retry runner avoid-async
# Do not decorate async def with retrying.retry.
# Use tenacity.AsyncRetrying or stamina instead.Calling async def returns a coroutine before its body runs, so retrying sees success and cannot catch awaited exceptions.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| tenacity | PyPI | Use it for new sync or async code needing composable policies, callbacks, and statistics. |
| backoff | PyPI | Use it when maintained decorator-based exception and predicate retries fit the project. |
| stamina | PyPI | Use it for opinionated sync and async retries on Tenacity with testing helpers. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

