Skip to content

fix(typing): keep classmethod binding for @retry decorated methods - #759

Open
devtechedge wants to merge 1 commit into
jd:mainfrom
devtechedge:fix/756-classmethod-retry-typing
Open

devtechedge wants to merge 1 commit into
jd:mainfrom
devtechedge:fix/756-classmethod-retry-typing

Conversation

@devtechedge

Copy link
Copy Markdown

Fixes #756

Thanks for keeping the typing on retry so tight, it made this one easy to pin down.

Since the _RetryDecorated protocol change, stacking @classmethod on top of @retry breaks under pyright and mypy, because neither checker applies classmethod binding to a protocol object, so cls is never dropped from the call signature.

This adds an extra retry overload for callables that take a first positional parameter, returning a new _RetryDecoratedMethod[S, P, R] protocol.

Its __get__ has a self-typed overload that drops the first parameter when it is type[T], so Foo.method(1) type-checks under @classmethod.

Instance methods still bind self the usual way.

The .retry, .statistics and .retry_with attributes stay typed on the new protocol.

There is no runtime change, only annotations.

One trade-off: a plain function whose first parameter is annotated type[X] and is stored as a class attribute will type-check as if it were bound.

That felt like a rare pattern compared to @classmethod + @retry, but happy to tighten it if you see a better shape.

I added assert_type regression tests for sync and async classmethods and instance methods in tests/test_tenacity.py and tests/test_asyncio.py.

They fail on main under both mypy and pyright and pass with this change.

The full test suite, poe mypy (strict config), poe lint and poe docs all pass locally.

There is a reno note under releasenotes/notes/.

This is really a typing workaround, so it can be dropped if pyright and mypy start applying classmethod binding to protocol objects upstream.

Open to any changes you would prefer here.

@mergify

mergify Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

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.

pyright regression: combining @classmethod and @retry throws error.

1 participant