Uu - #1
Open
Rwadxvv wants to merge 217 commits into
Open
Conversation
* start_time == outcome_timestamp is possible * logging.Formatter uses explicitly hard-coded `\n` newlines (as opposed to using the platform-specific os.linesep)
...rather than sometimes allowing a default return of `None` Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Slava Skvortsov <kondor1995@mail.ru>
* Fix iscoroutinefunction check (inspect & asyncio) * Add real async wrap & preserve retry attributes * Adjust to pep8 * Add test to ensure retryable_coroutine attributes * Fix `blank line contains whitespace` pep8 error
* chore: add missing noqa statements * docs: fix autoinstanceattribute
chore: add support for Python 3.9
* Copy whole internal state when retry_with (#233) Both `retry_error_cls` and `retry_error_callback` were missing from the copy, resulting in a copy that presents a different behavior than the original function. * Apply review feedback - define `_first_set` only once - get away with unittest - use pytest.raises
This should make sure we don't forget to put a release note before merging a PR. Related to #284
ci(mergify): force release notes to be present
Tweak Mergify config
* Make logger more compatible * Add release note * Fix black formatting * Ignore D402 error in flake8 * Update PR Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
* Add retry_except_exception_type Fixes #256 * Apply suggestions from code review Co-authored-by: Julien Danjou <julien@danjou.info> * rename 'except' to 'if not' * fix test * rename again Co-authored-by: Julien Danjou <julien@danjou.info>
Drop support for deprecated Pythons
- Use `black` for code formatting and validate using `black --check`. Code compatibility: py26-py39. - Enforce maximal line length to 120 symbols
Use black instead of "flake8-black" on CI.
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Since license = "Apache-2.0" is already set as an SPDX expression, the License :: OSI Approved :: Apache Software License classifier is redundant and causes setuptools deprecation warnings. Fixes #523 Co-authored-by: Community Contributor <anonymous@users.noreply.github.com>
* fix: reraise underlying exception when TryAgain wraps a cause When reraise=True and TryAgain is raised from within an except block, RetryError.reraise() now surfaces the underlying exception that caused the retry once attempts are exhausted, rather than the opaque TryAgain sentinel. A bare TryAgain raised without an active exception keeps reraising TryAgain as before. Fixes #544 * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: ATOM00blue <219721791+ATOM00blue@users.noreply.github.com> Co-authored-by: Julien Danjou <julien@danjou.info> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v6.0.2...v6.0.3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…_) (#643) When Retrying(enabled=False) was used with the context-manager iterator pattern (`for attempt in retrying` / `async for attempt in retrying`), the `enabled` flag was silently ignored and the full retry machinery ran (multiple attempts, stop/wait evaluated) instead of executing the body exactly once and propagating any exception directly. The `wraps()`-based path already handled `enabled=False` correctly, so this patch brings the iterator API in line with it: - BaseRetrying.__iter__: if not enabled, yield one AttemptManager and re-raise the stored exception if the attempt failed. - AsyncRetrying.__aiter__ / __anext__: same semantics via a one-shot flag that stops after the first yielded AttemptManager. Four regression tests are added (two sync, two async) covering the failure and success cases for each protocol.
…exError (#646) Calling wait_chain() with no strategies stored an empty tuple in self.strategies. The first call to __call__ then evaluated min(max(1, 1), 0) == 0, so self.strategies[wait_func_no - 1] turned into self.strategies[-1] and raised IndexError, which is opaque for what is really a programmer error at construction time. Validate in __init__ and raise ValueError('wait_chain() requires at least one strategy') so the failure happens at the point where the bad configuration is created, not later when the wait function is first evaluated inside a running retry. Co-authored-by: Zo Bot <github-automation@zo.computer>
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 6.0.3 to 7.0.0 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v6.0.3...v7.0.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…exError (#647) Calling wait_chain() with no strategies stored an empty tuple in self.strategies. The first call to __call__ then evaluated min(max(1, 1), 0) == 0, so self.strategies[wait_func_no - 1] turned into self.strategies[-1] and raised IndexError, which is opaque for what is really a programmer error at construction time. Validate in __init__ and raise ValueError('wait_chain() requires at least one strategy') so the failure happens at the point where the bad configuration is created, not later when the wait function is first evaluated inside a running retry. Co-authored-by: Zo Bot <github-automation@zo.computer>
…653) * Keep statistics visible when @Retry is further wrapped When a @retry-decorated function was wrapped by another decorator that uses functools.wraps (e.g. a timing decorator), accessing ``func.statistics`` returned an empty ``{}``. functools.wraps copies the inner wrapper's ``__dict__`` (including the ``statistics`` reference) into the outer wrapper. On each call the inner wrapper rebound ``wrapped_f.statistics`` to a fresh dict, so the outer wrapper kept pointing at the original, now-stale, empty dict. Reuse the same statistics dict in place on every call (clearing it and sharing it with the per-call copy) instead of rebinding the attribute. The dict identity stays stable, so the stats remain reachable through the outer wrapper's copied ``__dict__``. The same change is applied to the async wrapper. Fixes #519. * Type __wrapped__ on _RetryDecorated so statistics stay type-safe The runtime fix keeps func.statistics visible when a @retry-decorated function is further wrapped by another functools.wraps-based decorator. However mypy (strict) rejected the tests because the public typing did not expose the attributes the tests rely on: * _RetryDecorated had no __wrapped__ member, so accessing my_call.__wrapped__.statistics failed with [attr-defined]. * the sync test's outer decorator was annotated Callable[..., Any] -> Callable[..., Any], erasing the _RetryDecorated type so .statistics/.__wrapped__ were unreachable. Declare __wrapped__ on the _RetryDecorated protocol (it is always set by functools.wraps on the retry wrapper) and make the sync test's outer decorator type-preserving via a TypeVar, mirroring the async test. mypy, ruff, pytest and the docs build are all green.
Once an exponential wait exceeds its configured maximum, calculate the threshold logarithmically and return the cap without constructing an ever larger integer power. Add a regression test that rejects power evaluation past the cap. Fixes #526
retry_if_exception_message validated its message/match arguments with
truthiness checks (if message and match, if not message and not match).
An empty string is a valid message target — it matches exceptions whose
str() is empty, e.g. a bare RuntimeError() — but 'not message' is True
for '', so retry_if_exception_message(message='') wrongly raised
TypeError('missing 1 required argument'). _check had the same problem:
'if self.message:' skipped the empty-message branch.
Replace the truthiness guards with 'is None' checks so an empty message
is accepted and matched. This makes every message matchable, not every
message except the empty one.
retry_if_exception_cause_type walked __cause__ until None. Legal cycles such as ``raise e from e`` (or mutual a<->b causes) never terminate, so the predicate spins at 100% CPU and stop conditions never run. Track seen exception ids while walking, matching stdlib traceback's cycle-safe approach. Regression covers self-cause and two-node cycles. Fixes #658.
An unpinned ruff means every new ruff release can break CI with newly stabilized rules. Pin it to the current version so upgrades are explicit and deliberate. Upgrading to 0.16.1 stabilizes RUF036 (None not at the end of a type union); fix the 14 violations in the same commit via ruff's auto-fix so the pinned version lints clean. The changes are pure annotation reordering with no semantic effect. Change-Id: I252d1eff6bc8704288e2a9b0855d5a0c8851649d Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Same rationale as pinning ruff: new mypy releases can introduce new errors and break CI without any code change. Pin to the current version so upgrades are explicit. mypy passes clean at 2.3.0. Change-Id: I4a514903b8798fcee59236cf97b292ddd4f93f25 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
typeguard existed for exactly one test: TestRetryTyping asserted at runtime that @Retry preserves the decorated function's type. That check was weaker than it looked. check_type() cannot inspect a function object's signature at runtime, so 'check_type(with_raw, Callable[[int], str])' only ever confirmed the object was callable -- it would have passed just as happily on a wrapper that took different arguments or returned something else. The real guarantee comes from the WrappedFn TypeVar in the overloads, which is a static property, so check it statically. mypy already runs strict over tests/, and the annotations here catch strictly more than typeguard did: the positive cases pin the signature structurally, and the negative case would fail if @Retry ever decayed to Any, because warn_unused_ignores turns the then-dead ignore into an error. Also fixes a copy-paste bug: with_constructor_result was assigned from with_raw(1), so the @Retry(...) path's result was never checked at all. Drops the typeguard dependency, which had already cost a round of test breakage on its 3.x release. Change-Id: Iba6bbce53302c81bb7a84c6a9925b6f234d22d37 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Dependabot was configured for github-actions only, so nothing watched the Python dependencies. That was tolerable while ruff and mypy floated, but now that they are pinned it is a trap: the pins would never move and the tooling would silently rot. Add the uv ecosystem, grouped into a single monthly PR to match the github-actions entry. uv.lock is gitignored here; Dependabot updates the pyproject.toml constraints and skips lockfile regeneration when no lockfile is committed. Change-Id: I10bfd34f28d44198689e5871877d886cea635552 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Bumps the github-actions group with 1 update in the / directory: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 7.0.0 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v7.0.0...v7.0.1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Improve inline type annotations Port the accepted annotation-only completeness improvements from Tenacity 8.1.0 while adapting them to current main. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Preserve existing Any annotations Keep the existing public annotations for RetryCallState arguments and results rather than narrowing them to object-based container types. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Remove redundant inline annotations Keep only constructor return annotations and the non-trivial regex field union; remove declarations already evident from literals, conversions, typed parameters, varargs, and singleton constructors. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…668) _RetryDecorated had no __get__, so mypy and pyright treated instance.method the same as the unbound Class.method: an ordinary call like instance.method(value=1) demanded an explicit self argument and the return type resolved to Unknown/Any. Add __get__ overloads so attribute access through an instance returns the bound form with the correct return type. The runtime object is a real function from functools.wraps and already binds correctly, so this is purely a static analysis fix.
…676) Tornado ships a `py.typed` marker, so its types resolve without help. The override was silently downgrading every tornado symbol to `Any` if the package ever went missing, instead of failing loudly. `mypy` still reports no issues with the override removed. Change-Id: I1939167c4dd59eb99ca1d71a18307af6db0f5e2d
`on.pull_request.branches` filters on the *base* branch, not the head. With it pinned to `main`, only a pull request merging directly into `main` ran CI -- every stacked pull request, whose base is the branch below it in the stack, got no test, lint or mypy run at all and showed nothing but the Mergify checks. Drop the filter so CI runs for every pull request regardless of base. Change-Id: I1c211a106a57b9f4986bec4b76c0680c1a0f1b61
) * ci: run on pull requests targeting any base branch `on.pull_request.branches` filters on the *base* branch, not the head. With it pinned to `main`, only a pull request merging directly into `main` ran CI -- every stacked pull request, whose base is the branch below it in the stack, got no test, lint or mypy run at all and showed nothing but the Mergify checks. Drop the filter so CI runs for every pull request regardless of base. Change-Id: I1c211a106a57b9f4986bec4b76c0680c1a0f1b61 * fix: annotate wait_base.__radd__ as taking the int seed from sum() `__radd__` was annotated `other: wait_base`, which is the one type it can never receive: `wait_base.__add__` always succeeds, so Python never falls back to the reflected operator for two wait strategies. The only caller is `sum()`, which seeds its accumulator with the int 0 — hence the `type: ignore[comparison-overlap]` on `other == 0`, and the dead `return self` branch behind it that `--warn-unreachable` flags. Annotate the parameter as `int` and return `NotImplemented` for a non-zero left operand, so `5 + wait_fixed(1)` raises `TypeError` at the addition rather than building a `wait_combine` that explodes later when called. With the signature corrected, mypy infers `sum([...])` over wait strategies on its own, so four `type: ignore[list-item]` comments in `test_wait_arbitrary_sum` are no longer needed. Change-Id: Iad1bcef4412d6afc1a6f1335970cfbfcc4a34fcb
* ci: run on pull requests targeting any base branch `on.pull_request.branches` filters on the *base* branch, not the head. With it pinned to `main`, only a pull request merging directly into `main` ran CI -- every stacked pull request, whose base is the branch below it in the stack, got no test, lint or mypy run at all and showed nothing but the Mergify checks. Drop the filter so CI runs for every pull request regardless of base. Change-Id: I1c211a106a57b9f4986bec4b76c0680c1a0f1b61 * fix: annotate wait_base.__radd__ as taking the int seed from sum() `__radd__` was annotated `other: wait_base`, which is the one type it can never receive: `wait_base.__add__` always succeeds, so Python never falls back to the reflected operator for two wait strategies. The only caller is `sum()`, which seeds its accumulator with the int 0 — hence the `type: ignore[comparison-overlap]` on `other == 0`, and the dead `return self` branch behind it that `--warn-unreachable` flags. Annotate the parameter as `int` and return `NotImplemented` for a non-zero left operand, so `5 + wait_fixed(1)` raises `TypeError` at the addition rather than building a `wait_combine` that explodes later when called. With the signature corrected, mypy infers `sum([...])` over wait strategies on its own, so four `type: ignore[list-item]` comments in `test_wait_arbitrary_sum` are no longer needed. Change-Id: Iad1bcef4412d6afc1a6f1335970cfbfcc4a34fcb * chore(mypy): enable the strictness options `strict` leaves off `strict = true` is a curated subset, not every check mypy has. These five error codes and three flags are all off under `strict` and all report zero errors on the current tree, so turning them on costs nothing today and catches regressions from here on: - `warn_unreachable` -- dead branches, which are usually a wrong annotation rather than dead code (see the `wait_base.__radd__` fix below this commit) - `disallow_any_unimported` -- silent `Any` leaking in from untyped deps - `extra_checks` -- unsafely overlapping operator signatures, among others - `ignore-without-code` -- keeps `type: ignore` comments specific - `redundant-expr`, `truthy-iterable`, `unused-awaitable`, `exhaustive-match` Deliberately left off: `disallow_any_expr` (655 errors -- unusable for a decorator library) and `disallow_any_decorated` (63 errors, all inherent to `@retry` wrapping untyped test helpers). Change-Id: I9eee43a749050d2bcf0aa0abbedf8725301eba18
…679) `truthy-bool` reports objects tested for truthiness that implement neither `__bool__` nor `__len__`, and so can only ever be true. Three sites: `BaseRetrying._run_wait` and `AsyncRetrying._run_wait` both guarded the wait call with `if self.wait:`. `wait` is typed `WaitBaseT` and defaults to a `wait_none()` instance, so it is never falsy and the `sleep = 0.0` branch has been dead since 17aefd9 -- a leftover from when the surrounding code still used `if self.after is not None:` style guards. Call `self.wait` unconditionally. `if tornado:` guarded the optional import in two places. mypy only ever sees the `try` branch, so it resolves the name to the module and reads the test as always-true. Compute `_HAS_TORNADO` once and branch on that instead; this also keeps `tornado.gen` fully typed, which annotating the name as `ModuleType | None` would have thrown away. Change-Id: Icb9981f6797707e070dc2423015f2fbf6c94e4c4
* refactor: drop always-true truthiness checks and enable truthy-bool `truthy-bool` reports objects tested for truthiness that implement neither `__bool__` nor `__len__`, and so can only ever be true. Three sites: `BaseRetrying._run_wait` and `AsyncRetrying._run_wait` both guarded the wait call with `if self.wait:`. `wait` is typed `WaitBaseT` and defaults to a `wait_none()` instance, so it is never falsy and the `sleep = 0.0` branch has been dead since 17aefd9 -- a leftover from when the surrounding code still used `if self.after is not None:` style guards. Call `self.wait` unconditionally. `if tornado:` guarded the optional import in two places. mypy only ever sees the `try` branch, so it resolves the name to the module and reads the test as always-true. Compute `_HAS_TORNADO` once and branch on that instead; this also keeps `tornado.gen` fully typed, which annotating the name as `ModuleType | None` would have thrown away. Change-Id: Icb9981f6797707e070dc2423015f2fbf6c94e4c4 * refactor: declare BaseAction's REPR_FIELDS and NAME as ClassVar `mutable-override` rejects narrowing a mutable attribute in a subclass: `RetryAction.REPR_FIELDS = ("sleep",)` inferred `tuple[str]` against the base's `Sequence[str]`, and `NAME = "retry"` inferred `str` against `str | None`. Both are unsound in general -- code holding a `BaseAction` could assign a longer sequence or `None` through the base type. `BaseAction`'s docstring already calls these class variables, so mark them `ClassVar` and repeat the base annotation on the override. This documents the extension point for subclasses outside tenacity too, which hit the same error when they type check strictly. Change-Id: I012caeaad2f93327c69467776a53e87c573b6403
Two more error codes mypy leaves off under `strict`, both with real hits in the test suite. `possibly-undefined`: `test_retry_state` bound `retry_state` only inside an `except ExtractCallState` block, then used it unconditionally. Had the retry stopped raising, the test would have failed with `NameError` instead of a useful assertion. Use `assertRaises` as a context manager, which both asserts the exception is raised and binds the state unconditionally. `deprecated`: `asyncio.iscoroutinefunction` is deprecated since 3.14 and removed in 3.16, and was emitting a `DeprecationWarning` on every test run. The line right below it already asserts the same property via `inspect.iscoroutinefunction`, which is the documented replacement, so drop the deprecated call rather than pin the suite to an API that is going away. Change-Id: I135cf5364f3e471d954c878f2599be7441104971
* test: fix possibly-undefined and deprecated call sites Two more error codes mypy leaves off under `strict`, both with real hits in the test suite. `possibly-undefined`: `test_retry_state` bound `retry_state` only inside an `except ExtractCallState` block, then used it unconditionally. Had the retry stopped raising, the test would have failed with `NameError` instead of a useful assertion. Use `assertRaises` as a context manager, which both asserts the exception is raised and binds the state unconditionally. `deprecated`: `asyncio.iscoroutinefunction` is deprecated since 3.14 and removed in 3.16, and was emitting a `DeprecationWarning` on every test run. The line right below it already asserts the same property via `inspect.iscoroutinefunction`, which is the documented replacement, so drop the deprecated call rather than pin the suite to an API that is going away. Change-Id: I135cf5364f3e471d954c878f2599be7441104971 * refactor: mark overridden methods with @OverRide `explicit-override` is off under `strict`, and 63 methods across the library and tests were overriding a base method without saying so. That makes a whole class of change silently lossy: rename or drop a method on `retry_base`/`wait_base`/`stop_base`/`BaseRetrying` and every subclass keeps its now-orphaned implementation, still importable, never called again. `typing.override` only exists from Python 3.12, and tenacity has no runtime dependencies, so `_utils` imports it from `typing_extensions` under TYPE_CHECKING (as the module already does for `Self`) and falls back to a small PEP 698 shim at runtime on 3.10/3.11. Type checkers only ever see the `typing_extensions` name, so the check works regardless of the interpreter mypy runs under. That last point exposed a second problem worth fixing here: mypy's `python_version` defaults to whatever interpreter runs it, so CI on 3.14 was never verifying that the code is valid on the 3.10 we claim to support. Pin `python_version = "3.10"` to match `requires-python`. It caught one real case immediately -- `RetryCallState.__getstate__` is only an override from 3.11 on, where `object.__getstate__` was introduced, so it must not carry the decorator. Verified `mypy` reports the same result under both a 3.10 and a 3.14 interpreter, and the runtime shim sets `__override__` correctly on 3.10. Change-Id: Ide71dd204215e82012f6a9b1afc725dd7c5db8ee
) Three runtime regressions from the mypy-strictness stack (#679, #677), all A/B verified against a2af454. None were caught by the test suite, and all three only fire once a retry actually happens, so happy-path callers see nothing until production. 1. `Retrying(wait=None)`, `wait=0` and `wait=sum([])` now raise `TypeError: 'NoneType' object is not callable` from inside `iter()`. #679 removed the `if self.wait:` guard on the grounds that `truthy-bool` proved it always true -- but that only holds for the *declared* type. Untyped callers pass `None`/`0` to mean "no wait", and `sum([])` over an empty strategy list returns the int 0. The `TypeError` is raised outside the attempt's try/except, so it escapes uncaught and destroys the caller's real exception. Restore the guard with a local `truthy-bool` suppression explaining why the "impossible" branch is reachable. `before`, `after`, `before_sleep` and `retry_error_callback` all kept their `is not None` guards; `wait` was the only one dropped. 2. `plain_callable + wait_strategy` now raises `TypeError`. `WaitBaseT` admits plain callables, and a function has no `__add__`, so this went through `wait_base.__radd__` -- which #677 narrowed to `int`. Widen it to `WaitBaseT | int` and build the `wait_combine` again. `5 + strategy` is still rejected, now via `NotImplemented` rather than a combination that fails later. The parameter stays `int` rather than `Literal[0]` because typeshed's `sum()` protocol requires `__radd__(x: int)`; narrowing it would make every `sum()` over strategies need a `type: ignore`. 3. `wait_combine.__call__` passed the state as `retry_state=`, which crashes on any `WaitBaseT` callable whose parameter has another name. Pass it positionally, as `BaseRetrying._run_wait` already does. Adds regression tests for all three plus the async path -- the original change shipped with no test covering any of them. Change-Id: Ia40c32a22cdac09cbfba4280fb0a7f0c2cb7b7e0
…696) #679 replaced `if tornado:` with a `_HAS_TORNADO` bool computed once at import, to stop `truthy-bool` flagging the module object as always true. But only one of the two call sites was converted: the `retry()` decorator still dereferences the live `tornado` global right after testing the snapshot. The two desync as soon as anything reassigns the global. A/B verified against a2af454: `tenacity.tornado = None` followed by any `@tenacity.retry` decoration returned normally before, and now raises `AttributeError: 'NoneType' object has no attribute 'gen'`. Nulling the module global is the standard way a downstream suite exercises the non-tornado path without uninstalling tornado. Replace the constant with a `_has_tornado()` function so both call sites read the live global and cannot drift apart. As a bonus this needs no `redundant-expr` suppression: the check reads as a plain return rather than the left operand of an `and`. Change-Id: I6f0fbb9cad3d4e46599b0cae1b5b60378742fd1f
* fix: respect enabled=False when calling the controller directly `Retrying.__call__` and `AsyncRetrying.__call__` ignored the `enabled` flag: calling a controller created with `enabled=False` still ran the full retry loop, so a failing function was retried and the original exception was wrapped in a `RetryError`. This makes the direct-call protocol consistent with the decorator (`@retry(enabled=False)`) and the iterator protocols (`__iter__`/`__aiter__`, fixed in #643): with `enabled=False` the function is executed exactly once and any exception propagates unchanged. * fix(types): correct type-ignore codes in asyncio early-return path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Y