Skip to content

fix: reject a number on the left-hand side of + as well - #724

Open
yu2971512385-ui wants to merge 1 commit into
jd:mainfrom
yu2971512385-ui:fix/wait-add-rejects-number
Open

yu2971512385-ui wants to merge 1 commit into
jd:mainfrom
yu2971512385-ui:fix/wait-add-rejects-number

Conversation

@yu2971512385-ui

Copy link
Copy Markdown

wait_base.__radd__ deliberately turns a non-zero number into a TypeError at the +, with the reasoning spelled out in the comment there:

Any other number is not a wait strategy, and saying so here raises TypeError at the + rather than building a combination that fails when called.

__add__ has no such check, so the mirror expression does exactly what that comment is about:

>>> w = tenacity.wait_fixed(1) + 5
>>> w  # a wait_combine holding an int
>>> Retrying(wait=w, stop=tenacity.stop_after_attempt(3))(flaky)
TypeError: 'int' object is not callable

The error arrives on the first retry, inside wait_combine.__call__, pointing at tenacity rather than at the line that built the strategy. 5 + tenacity.wait_fixed(1) — the same mistake written the other way round — already fails immediately and is covered by test_wait_radd_rejects_non_zero_number.

Change

__add__ returns NotImplemented when the operand is not callable, so Python falls through to the operand's __radd__ and raises the usual unsupported operand type(s) for +: 'wait_fixed' and 'int'. The annotation moves from wait_base to WaitBaseT, matching what wait_combine and __radd__ already accept, so wait_fixed(1) + (lambda retry_state: 2.0) keeps working.

Behaviour after the change:

expression before after
w + 5 wait_combine that raises when called TypeError at the +
5 + w TypeError at the + unchanged
0 + w / sum([w1, w2]) identity / wait_combine unchanged
w1 + w2, w + callable wait_combine unchanged

Tests

  • test_wait_add_rejects_number — the mirror of the existing __radd__ test; it fails without the change.
  • test_wait_add_plain_callable — the callable case keeps building a wait_combine.

pytest tests/ is 185 passed, 1 skipped; mypy tenacity/wait.py is clean (the 3 errors mypy tenacity/ reports in asyncio/__init__.py are there on a clean checkout too).

Disclosure: found and prepared with AI assistance — the asymmetry came out of a sweep over the wait strategy operators; I verified the behaviour and ran the suite and mypy locally.

wait_base.__radd__ turns a non-zero number into a TypeError at the '+',
so that '5 + wait_fixed(1)' fails where it is written rather than
building a combination that raises when it is called. __add__ had no
such check, so the mirror expression went the way __radd__ avoids:

    >>> w = tenacity.wait_fixed(1) + 5
    >>> w(retry_state)
    TypeError: 'int' object is not callable

Return NotImplemented for an operand that is not callable, which lets
Python raise the usual unsupported-operand TypeError. A plain callable
stays accepted, as WaitBaseT and wait_combine both allow one.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant