Skip to content

Type inference on owner argument to __set_name__ #7039

Closed

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 >= 10

In 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

Explicitly providing a hint for the InstanceT type removes the errors:

class C:

    x = IntAttr["C"](lambda self, value: self.validate(value)) # Errors

    def validate(self, value: int) -> bool:
        return value >= 10

Performing 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:

Sister issue for Mypy: python/mypy#16796

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    as designedNot a bug, working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions