diff --git a/pyproject.toml b/pyproject.toml index a847907..801eab1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "commons" -version = "0.2.0" +version = "0.2.1" description = "small stdlib-only sync helpers: time/timezone deltas, dotted-path dict access, display masking, ip/address tooling, and retry/backoff" requires-python = ">=3.10" dependencies = [] diff --git a/src/commons/__init__.py b/src/commons/__init__.py index 2358c94..22ef211 100644 --- a/src/commons/__init__.py +++ b/src/commons/__init__.py @@ -63,4 +63,4 @@ __all__ = [ "aretry", ] -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/src/commons/retry.py b/src/commons/retry.py index e5d4e92..257669b 100644 --- a/src/commons/retry.py +++ b/src/commons/retry.py @@ -77,8 +77,10 @@ def retry( `retry(fn, ...)` runs immediately; `@retry(...)` wraps a function. retries on the `on` exceptions, stops early if `give_up(exc)` is true, re-raises the last exception once `attempts` are exhausted. `sleep`/`rand` are injectable for tests. + `attempts` is floored at 1 so the callable always runs at least once. """ types = _as_types(on) + attempts = max(1, attempts) def run(target: Callable, args, kwargs): delays = list(_delays(attempts, backoff, factor, max_backoff)) @@ -128,8 +130,10 @@ def aretry( async twin of `retry`. `await aretry(coro_fn, ...)` runs immediately; `@aretry(...)` wraps a coroutine function. same semantics: retry on `on`, stop on `give_up`, re-raise the last exception after `attempts`. `sleep`/`rand` injectable. + `attempts` is floored at 1 so the callable always runs at least once. """ types = _as_types(on) + attempts = max(1, attempts) async def run(target: Callable, args, kwargs): delays = list(_delays(attempts, backoff, factor, max_backoff))