python / python/mypy

Wrapped methods are not properly resolved until after the class definition

Open
#16,397 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Context & setup

Consider the following simple protocol:

class Proto(Protocol):
    @property
    def f(self) -> int:
        ...

One can implement this protocol wrongly as follows:

T = TypeVar("T")
P = ParamSpec("P")


def wrapper(f: Callable[P, T]) -> Callable[P, str]:
    def wrapped(*args: P.args, **kwargs: P.kwargs) -> str:
        return 0

    return wrapped

class Impl:
    @wrapper  # converts return type from `bool` to `str`, while the protocol expects `int`
    def f(self) -> bool:
        return False

# error: Incompatible return value type (got "Impl", expected "Proto")  [return-value]
# note: Following member(s) of "Impl" have conflicts:
# note:     Expected:
# note:         def f(self) -> int
# note:     Got:
# note:         def f(self) -> str
def b() -> Proto:
    return Impl()

The bug (false negative)

When this same function b is placed before class Impl, mypy does not report an error. Adding reveal_type(Impl.f) before and after the class definition hints at what is going on:

main.py:21: error: Expression has type "Any"  [misc]
main.py:21: error: Cannot determine type of "f"  [has-type]
main.py:21: note: Revealed type is "Any"
main.py:21: error: Name "Impl" is used before definition  [used-before-def]

main.py:28: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"

If f is not decorated but explicitly returns str, one gets

main.py:21: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
main.py:21: error: Name "Impl" is used before definition  [used-before-def]

main.py:27: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"

as expected. This indicates that the wrapped decorator is only properly resolved for code after the class definition.

False positive variant

If f is decorated with @property, this can also produce false positives. This is how I encountered the issue in the first place. Playground.

Additional info

Python: 3.11
Mypy: 371219347a6d17e16924bbabf3e693c6874e7138
Flags: --strict --disallow-any-*

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the false negative and false positive in main.py using the linked mypy-play examples, comparing function placement, @wrapper, and @property. Trace how the decorator and wrapped method type are resolved before and after class Impl; done means both variants report the expected protocol compatibility diagnostics consistently.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.