python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.42k stars 2.82k forks source link

@property returning descriptor instance triggers false positive #9416

Open dargueta opened 4 years ago

dargueta commented 4 years ago

🐛 Bug Report

Accessing a field implementing the descriptor protocol via a property method causes MyPy to complain that there is no matching overload for __get__().

To Reproduce

Run MyPy with all default settings on the following code:

from typing import Optional
from typing import overload
from typing import Type

class A:
    pass

class Field:
    def __init__(self, attr: Optional["Field"] = None):
        self.attr = attr

    @property
    def crashing_property(self) -> Optional["Field"]:
        return self.attr

    @overload
    def __get__(self, instance: None, owner: Type["A"]) -> "Field":
        ...

    @overload
    def __get__(self, instance: "A", owner: Type["A"]) -> Optional["Field"]:
        ...

    def __get__(self, instance, owner):
        if not instance:
            return self
        return self.attr

f = Field()  # type: Field
f.attr  # Works fine
f.crashing_property  # Triggers "no overload variant"

n.b. adding __set__() makes no difference

Expected Behavior

Accessing crashing_property shouldn't cause an error.

Actual Behavior

The last line in the example throws the following error:

test.py:39: error: No overload variant of "__get__" of "Field" matches argument types "Field", "Type[Field]"
test.py:39: note: Possible overload variants:
test.py:39: note:     def __get__(self, instance: None, owner: Type[A]) -> Field
test.py:39: note:     def __get__(self, instance: A, owner: Type[A]) -> Optional[Field]

If you add this the code validates fine:

@overload
def __get__(self, instance: "Field", owner: Type["Field"]):
    ...

Your Environment

dargueta commented 2 years ago

Update: Still happening as of 0.910.

dargueta commented 3 months ago

Still happening with MyPy 1.10.1 on Python 3.12.