NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1909 most downloaded on PyPI
Production-grade retries made easy.
Last release 5 months ago
13 Apr 2026
Ships fairly regularly
a new release about every 4 months
Nearly every release is documented
notes for 11 of 11 stable releases
Nothing withdrawn
no release was ever pulled
4 years old
11 releases · first in 2022
One column per quarter.
stamina now allows more than 1024 retries by handling overflows and warns on a footgun.
stamina now allows more than 1024 retries by handling overflows and warns on a footgun.
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Kraken Tech (@kraken-tech), Klaviyo (@klaviyo), Privacy Solutions GmbH (@privacy-solutions), FilePreviews (@filepreviews), Ecosystems (@ecosyste-ms), TestMu AI Open Source Office (Formerly LambdaTest) (@LambdaTest-Inc), Doist (@Doist), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Buttondown (@buttondown), Christopher Dignam (@chdsbd), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Rivo Laks (@rivol), Polar (@polarsource), Mike Fiedler (@miketheman), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Pieter Swinkels (@swinkels), Nick Libertini (@libertininick), Brian M. Dennis (@crossjam), Celebrity News AG (@celebritynewsag), The Westervelt Company (@westerveltco), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Thomas Klinger (@thmsklngr), Andreas Poehlmann (@ap--), August Trapper Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
timeout=0 now raises a warning since it counterintuitively disables all retries. If you explicitly want to disable the timeout, use timeout=None._compute_backoff() no longer raises OverflowError when the attempt number exceeds 1024 with a float wait_exp_base. The exponential result is now clamped to wait_max on overflow. #137This release contains contributions from @hynek, and @vigneshakaviki.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek stamina-26.1.0.tar.gzand
$ gh attestation verify --owner hynek stamina-26.1.0-py3-none-any.whlThe headliners for this release are that on hooks now may return a manual back-off value (think: Retry-After headers!) and the support for generator f
The headliners for this release are that on hooks now may return a manual back-off value (think: Retry-After headers!) and the support for generator functions (sync and async).
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Privacy Solutions GmbH (@privacy-solutions), FilePreviews (@filepreviews), LambdaTest (@LambdaTest-Inc), Doist (@Doist), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Buttondown (@buttondown), Christopher Dignam (@chdsbd), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Polar (@polarsource), Mike Fiedler (@miketheman), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Pieter Swinkels (@swinkels), Nick Libertini (@libertininick), Brian M. Dennis (@crossjam), Celebrity News AG (@celebritynewsag), The Westervelt Company (@westerveltco), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Thomas Klinger (@thmsklngr), Andreas Poehlmann (@ap--), August Trapper Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
The type hints for our public API are now also verified using Pyrefly and ty. #124
stamina.retry() now retries wrapped generator functions and async generator functions.
Warning: Being able to asend and athrow into wrapped async generators introduced nontrivial complexity in the implementation and is therefore provisional. If supporting these features causes problems, they may be removed again in a future version. #123
An on hook can now return a float or a datetime.timedelta to specify a custom backoff that overrides the default backoff. #103 #125
Prevent unbounded stop condition when both attempts and timeout are non-None falsy values. #109
Default wait_exp_base parameter is now an integer to prevent an OverflowError after the 1023th retry. #104
Attempt.next_wait now returns the correct value. #115
This release contains contributions from @DoctorJohn, @gesslerpd, and @hynek.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek stamina-25.2.0.tar.gzand
$ gh attestation verify --owner hynek stamina-25.2.0-py3-none-any.whlThe context manager edition! You can now use stamina.set_testing() as a context manager and also return one from an instrumentation retry hook which i
The context manager edition! You can now use stamina.set_testing() as a context manager and also return one from an instrumentation retry hook which is entered and exited before and after sleep which allows you to emit spans for back off times!
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Ecosystems (@ecosyste-ms), Klaviyo (@klaviyo), Privacy Solutions GmbH (@privacy-solutions), FilePreviews (@filepreviews), nate nowack (@zzstoatzz), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Buttondown (@buttondown), Christopher Dignam (@chdsbd), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Polar (@polarsource), Mike Fiedler (@miketheman), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Pieter Swinkels (@swinkels), Nick Libertini (@libertininick), Brian M. Dennis (@crossjam), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), The Westervelt Company (@westerveltco), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Thomas Klinger (@thmsklngr), Andreas Poehlmann (@ap--), August Trapper Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
Not to forget 14 more amazing humans who chose to be generous but anonymous!
cap argument to stamina.set_testing(). By default, the value passed as attempts is used strictly. When cap=True, it is used as an upper cap; that means that if the original attempts number is lower, it's not changed. #80
stamina.set_testing() can now be used as a context manager. #94
Instrumentation hooks can now can return context managers. If they do, they are entered when a retry is scheduled and exited right before the retry is attempted. #95
This release contains contributions from @hynek and @sparkiegeek.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek stamina-25.1.0.tar.gzand
$ gh attestation verify --owner hynek stamina-25.1.0-py3-none-any.whlThree changes, three highlights! stamina now allows you to test your retry logic, by limit the number of retries, you can specify a callable that deci
Three changes, three highlights! stamina now allows you to test your retry logic, by limit the number of retries, you can specify a callable that decides whether you, indeed want to retry on a certain exception (think: different HTTP errors), and finally there's more info on the Attempt object.
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Klaviyo (@klaviyo), Daniel Fortunov (@asqui), Kevin P. Fleming (@kpfleming), and Wim Jeantine-Glenn (@wimglenn).
Adam Hill (@adamghill), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Mike Fiedler (@miketheman), Thomas Klinger (@thmsklngr), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Andreas Poehlmann (@ap--), Pieter Swinkels (@swinkels), August Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
Not to forget 8 more amazing humans who chose to be generous but anonymous!
The on argument in all retry functions now can be a callable that takes an exception and returns a bool which decides whether or not a retry should be scheduled. #70
stamina.Attempt now has a next_wait attribute that contains the time the next backoff will wait, if the current attempt fails (sans jitter). #72
It is now possible to switch stamina into a testing mode using stamina.set_testing(). It disables backoffs and caps the number of retries. #73
This release contains contributions from @gsakkis, @hynek, and @wlach.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek stamina-24.3.0.tar.gzand
$ gh attestation verify --owner hynek stamina-24.3.0-py3-none-any.whlThis release adds two handy classes for the cases when you just want to retry a single function/method call:
This release adds two handy classes for the cases when you just want to retry a single function/method call:
def do_something_with_url(url, some_kw):
resp = httpx.get(url)
resp.raise_for_status()
...
rc = stamina.RetryingCaller(attempts=5)
rc(httpx.HTTPError, do_something_with_url, f"https://httpbin.org/status/404", some_kw=42)
# You can also create a caller with a pre-bound exception type:
bound_rc = rc.on(httpx.HTTPError)
bound_rc(do_something_with_url, f"https://httpbin.org/status/404", some_kw=42)Both rc and bound_rc run:
do_something_with_url(f"https://httpbin.org/status/404", some_kw=42)Unfortunately, it's not possible to implement this behavior transparently for async, so you have to use AsyncRetryingCaller for async callables.
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), Kevin P. Fleming (@kpfleming), and Sören Weber (@SoerenWeber).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Mike Fiedler (@miketheman), and Michel Vittória (@michelvittoria).
Not to forget 5 more amazing humans who chose to be generous but anonymous!
stamina.RetryingCaller and stamina.AsyncRetryingCaller that allow even easier retries of single callables: stamina.RetryingCaller(attempts=5).on(ValueError)(do_something, "foo", bar=42) and stamina.RetryingCaller(attempts=5)(ValueError, do_something, "foo", bar=42) will call do_something("foo", bar=42) and retry on ValueError up to 5 times.
stamina.RetryingCaller and stamina.AsyncRetryingCaller take the same arguments as stamina.retry(), except for on that can be bound separately.
This release only fixes one fun bug caused by one missing return statement. If you ever wondered why you successful blocks got retried when you deacti
This release only fixes one fun bug caused by one missing return statement. If you ever wondered why you successful blocks got retried when you deactivated stamina – wonder no longer.
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), and Mike Fiedler (@miketheman).
Not to forget 6 more amazing humans who chose to be generous but anonymous!
Support for the Trio async framework!
Support for the Trio async framework!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), and Filip Mularczyk (@mukiblejlok).
Not to forget 5 more amazing humans who chose to be generous but anonymous!
The highlight of this release are without question configurable retry hooks ! You can now freely configure functions that are run whenever a retry is
The highlight of this release are without question configurable retry hooks! You can now freely configure functions that are run whenever a retry is scheduled.
Can't wait to see what kind of third-party integrations y'all come up with – please show and tell us!
As a bonus, stamina now also comes with standard library's logging instrumentation out of the box.
If you have praise, comments, or concerns (that aren't outright bugs), head over to the release discussion!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), HiredScore (@HiredScore), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), and Sławomir Ehlert (@slafs).
Not to forget 5 more amazing humans who chose to be generous but anonymous!
Instrumentation is now pluggable! You can define your own hooks that are run with retry details whenever a retry is scheduled. The documentation now has a whole chapter on instrumentation. #37
If structlog is not installed, the scheduled retry is now logged using the standard library logging module by default. #35
Tenacity's internal AttemptManager object is no longer exposed to the user. This was an oversight and never documented. stamina.retry_context() now yields instances of stamina.Attempt. #22
Initialization of instrumentation is now delayed. This means that if there's no retries, there's no startup overhead from importing structlog and prometheus-client. #34
Some key names in structlog log messages have been renamed to better reflect their meaning (slept → waited_so_far, attempt → retry_num, and error → caused_by). You can rename them back using structlog's structlog.processors.EventRenamer. #35
With this release, stamina graduates to becoming a "real" project. It now has proper documentation and a backwards-compatibility policy .
With this release, stamina graduates to becoming a "real" project. It now has proper documentation and a backwards-compatibility policy.
The other highlights are certainly transparent support for async and retrying of arbitrary code blocks!
Full changelog can be found below.
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Sentry (@getsentry), HiredScore (@HiredScore), FilePreviews (@filepreviews), and Daniel Fortunov (@asqui).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), Stein Magnus Jodal (@jodal), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Tom Ballinger (@thomasballinger), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), and zorazen (@ZoraZen).
Not to forget 3 more amazing humans who chose to be generous but anonymous!
for loops and context managers. #12datetime.timedelta.Retries are now instrumented. If *prometheus-client* is installed, retries are counted using the *Prometheus* counter stamina_retries_total. If *struc
Instrumentation!
stamina_retries_total.
If structlog is installed, they are logged using a structlog logger at warning level.
These two instrumentations are independent from each other.### Added - Initial release.
Your coding agent can read these notes before it upgrades. Set up the MCP server →