decorating methods with descriptors loses MyPy when checking against a Protocol. It ends up refusing the assignment because a method isn't Callable[...] but Descriptor
I wasn't able this exact described behaviour in previous bug reports. It does seem like mypy correctly assess the type of the described class fields and method in other cases.
mypy should detect that the __get__ method from the descriptor returns a compatible type for Callable (it has a compatible __call__ method) and allow the assignment my_instance: MyProtocol = MyClass()
Actual Behavior
mypy correctly detects that the concrete_instance.my_method type is the one from the return of the __get__ method from the descriptor. It also correctly asses that the type is compatible with Callable[[int], str] like the method of the protocol.
However, it refuses to verify that this method is indeed compatible with the protocol's method when trying to assign the whole instance to a typed variable.
reproduce.py:42: note: Revealed type is "reproduce.Runner[reproduce.MyClass, [i: builtins.int], builtins.str]"
reproduce.py:45: error: Incompatible types in assignment (expression has type "MyClass", variable has type "MyProtocol") [assignment]
reproduce.py:45: note: Following member(s) of "MyClass" have conflicts:
reproduce.py:45: note: my_method: expected "Callable[[int], str]", got "MyDescriptor[MyClass, [int], str]"
reproduce.py:46: note: Revealed type is "def (i: builtins.int) -> builtins.str"
Found 1 error in 1 file (checked 1 source file)
Your Environment
Mypy version used: 1.16.1 (compiled: yes)
Mypy command-line flags: none.
Mypy configuration options from mypy.ini (and other config files): check_untyped_def=True
Activity
sterliakov commented on Jun 24, 2025
This snippet passes on master! It was (most likely, didn't bisect) fixed in #18943.
Floby commented on Jun 27, 2025
great news, thanks!
I assume this'll make it to the next patch release?