For code of this shape:
from functools import partialmethod
def pprint(arg: object, /, *, options: dict) -> str:
return "dummy"
class Foo: ...
class Bar: ...
Bar.__repr__ = partialmethod(pprint, options={})
it is difficult for type checkers to verify that partialmethod is compatible with the target method since it is currently typed with only a return-type parameter:
class partialmethod(Generic[_T]):
...
def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ...
The current stub does not preserve the wrapped callable’s parameters or receiver type, so type checkers cannot model these class and instance access forms even though this ebhaviour is valid at runtime, and partialmethod is a descriptor:
Bar.__repr__ # T: (self: Bar) -> str
Bar().__repr__ # T: () -> str
This forces Pyrefly and Pyright to reject this valid code. Mypy on the other hand, looks at stub’s descriptor fallback (Any) and infers partialmethod[Any] even when the wrapped callable returns str.
We should preserve descriptor binding and as much callable information as possible, while accepting that a fully precise residual signature needs typing-spec support.
Born from Pyrefly issue, facebook/pyrefly#5138
For code of this shape:
it is difficult for type checkers to verify that
partialmethodis compatible with the target method since it is currently typed with only a return-type parameter:The current stub does not preserve the wrapped callable’s parameters or receiver type, so type checkers cannot model these class and instance access forms even though this ebhaviour is valid at runtime, and
partialmethodis a descriptor:This forces Pyrefly and Pyright to reject this valid code. Mypy on the other hand, looks at stub’s descriptor fallback (
Any) and inferspartialmethod[Any]even when the wrapped callable returnsstr.We should preserve descriptor binding and as much callable information as possible, while accepting that a fully precise residual signature needs typing-spec support.
Born from Pyrefly issue, facebook/pyrefly#5138