Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Descriptor protocol is executed for properties returning descriptors #13608

Open
denballakh opened this issue Sep 4, 2022 · 2 comments
Open
Labels
bug mypy got something wrong topic-descriptors Properties, class vs. instance attributes

Comments

@denballakh
Copy link
Contributor

Consider this example:

from __future__ import annotations

class X: ...

class Y:
    @property
    def some_property(self) -> Descriptor: # return Descriptor instance
        return Descriptor()

class Descriptor:
    def __get__(self, obj: X, cls: type[X]) -> int: # can work only with X instances
        print(f'Descriptor.__get__({self}, {obj}, {cls}) called')
        return 0

y = Y()
print(y.some_property)
# arg-type: Argument 1 to "__get__" of "Descriptor" has incompatible type "Y"; expected "X"
# arg-type: Argument 2 to "__get__" of "Descriptor" has incompatible type "Type[Y]"; expected "Type[X]"

# stdout: <__main__.Descriptor object at 0x0000020BF8A5BE20>
# note that Descriptor.__get__ isnt called

I am defining descriptor that can work only with X instances, and i am returning this descriptor from property of Y instances.
At runtime Descriptor.__get__ isnt called, but mypy thinks that Y.some_property is Descriptor (not a property returning Descriptor instance), tries to execute descriptor protocol and fails.

At runtime i am getting no errors, descriptor isnt called and this code is safe.

To reproduce:

  • just run mypy with this code

Expected Behavior

  • no errors

Actual Behavior

  • error

Your Environment

  • Mypy version used: mypy 0.971 (compiled: yes)
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.10.6
  • Operating system and version: Win10
@denballakh denballakh added the bug mypy got something wrong label Sep 4, 2022
@AlexWaygood AlexWaygood added the topic-descriptors Properties, class vs. instance attributes label Sep 4, 2022
@denballakh
Copy link
Contributor Author

from __future__ import annotations

class X:
    ...

class Y:
    # some_descriptor: Descriptor
    def __init__(self) -> None:
        self.some_descriptor = Descriptor()

class Descriptor:
    def __get__(self, obj: X, cls: type[X]) -> int:
        print(f'Descriptor.__get__({self}, {obj}, {cls}) called')
        return 0

y = Y()
print(y.some_descriptor)

This code is fine.
But if i uncomment # some_descriptor: Descriptor line i am getting errors:

at `self.some_descriptor = Descriptor()` line:
Argument 1 to "__get__" of "Descriptor" has incompatible type "Y"; expected "X"                                                                       
Argument 2 to "__get__" of "Descriptor" has incompatible type "Type[Y]"; expected "Type[X]"                                                           
Incompatible types in assignment (expression has type "Descriptor", variable has type "int")                                                          

at `print(y.some_descriptor)` line:
Argument 1 to "__get__" of "Descriptor" has incompatible type "Y"; expected "X"                                                                       
Argument 2 to "__get__" of "Descriptor" has incompatible type "Type[Y]"; expected "Type[X]"  

I think this is related

@eltoder
Copy link

eltoder commented Aug 23, 2023

A similar example with the recent mypy version: https://mypy-play.net/?mypy=latest&python=3.11&gist=069b99fd10df82bef0769c52209ce00e

from typing import assert_type

class Desc:
    def __get__(self, obj, owner=None) -> int:
        return 1

class C:
    a = Desc()
    @property
    def b(self) -> Desc:
        return Desc()

obj = C()
assert_type(obj.a, int)
assert_type(obj.b, Desc)

generates

main.py:15: error: Expression is of type "int", not "Desc"  [assert-type]

but works fine at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-descriptors Properties, class vs. instance attributes
Projects
None yet
Development

No branches or pull requests

3 participants