- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 3k
Description
Because of the circumstances under which __set_name__ is invoked, it should be possible to perform type inference on its owner argument, in a way which could be used to statically typecheck other components of a descriptor.
As a brief motivating example, below is a sketch of descriptor for a validated mutable attribute of integer type:
from __future__ import annotations
from typing import Any, Generic, Protocol, Self, Type, TypeVar, cast, overload
InstanceT = TypeVar("InstanceT")
InstanceT_contra = TypeVar("InstanceT_contra", contravariant=True)
class Validator(Protocol[InstanceT_contra]):
    def __call__(self, instance: InstanceT_contra, value: int, /) -> bool:
        ...
class IntAttr(Generic[InstanceT]):
    __validator: Validator[InstanceT]
    __name: str
    def __init__(self, validator: Validator[InstanceT]) -> None:
        self.__validator = validator
    def __set_name__(self, owner: Type[InstanceT], name: str) -> None:
        self.__name = name
    @overload
    def __get__(self, instance: None, _: Type[Any]) -> Self:
        ...
    @overload
    def __get__(self, instance: InstanceT, _: Type[Any]) -> int:
        ...
    def __get__(self, instance: InstanceT|None, _: Type[Any]) -> int | Self:
        if instance is None:
            return self
        return cast(int, getattr(instance, f"__{self.__name}"))
    def __set__(self, instance: InstanceT, value: int) -> None:
        if not self.__validator(instance, value):
            raise ValueError()
        setattr(instance, f"__{self.__name}", value)
class C:
    x = IntAttr(lambda self, value: self.validate(value)) # Pylance error
    def validate(self, value: int) -> bool:
        return value >= 10In Pylance with strict typechecking rules, the definition of the x descriptor raises the following errors:
reportUnknownMemberType
  Type of "validate" is unknown 
reportUnknownLambdaType
  Return type of lambda is unknown
reportGeneralTypeIssues
  Cannot access member "validate" for type "object*"
    Member "validate" is unknown
Currently, Mypy doesn't raise any errors about the lack of lambda typing (not even for incorrect implementations, such as self.validatez(value) or self.validate(str(value))). Mypy also doesn't raise errors about incorrect lambda implementations once an explicit type hint is provided (see below), while Pylance does.
I opened a separate issue about this: #16797
Explicitly providing a hint for the InstanceT type removes the errors for Pylance:
class C:
    x = IntAttr["C"](lambda self, value: self.validate(value)) # Errors
    def validate(self, value: int) -> bool:
        return value >= 10Performing inference on the owner argument to IntAttr.__set_name__ in the context of class C would ideally result in C being inferred as a value for InstanceT, allowing for static typecheking of the validator lambda function without the need for an explicit type hint.
Related issues for Mypy:
- Infer descriptor type #10246
- __set_name__ isn't checked #8057
- issues about typing of lambdas
Sister issue for Pylance/Pyright: microsoft/pyright#7039