Search code examples
pythonmypypython-descriptors

Type hinting with descriptors


In this pull request it looks like type hinting support for descriptors was added.

However it looks like no finalized "correct" usage example was ever posted, nor does it looks like any documentation was ever added to the typing module or to Mypy.

It looks like the correct usage is something like this:

from typing import TypeVar

T = TypeVar('T')
V = TypeVar('V')


class classproperty():
    def __init__(self, getter: Callable[[Type[T]], V]) -> None:
        self.getter = getter

    def __get__(self, instance: Optional[T], owner: Type[T]) -> V:
        return self.getter(owner)


def forty_two(cls: Type) -> int:
    return 42


class C:
    forty_two: int = classproperty(forty_two)

This seems logical, but I have no idea if that's actually the right way to do things.

Is there any documentation on this? Or complete examples that actually works on the version that was merged?


Solution

  • After some time of struggling with this issue, this page being the top result when you search for "type hinting descriptors", I would like to share a solution that fully satisfies the mypy and pyright static type checkers, is python 3.6 compatible and does not inherit from property

    from typing import Callable, Generic, Type, TypeVar, overload, Union
    
    
    Instance = TypeVar('Instance')
    Value = TypeVar('Value')
    Attribute = TypeVar('Attribute')
    
    
    class Descriptor(Generic[Instance, Attribute, Value]):
        def __init__(self, method: Callable[[Instance, Attribute], Value]):
            """ Called on initialisation of descriptor """
    
        @overload
        def __get__(self, instance: None, owner: Type[Instance]) -> 'Descriptor':
            """ Called when an attribute is accessed via class not an instance """
    
        @overload
        def __get__(self, instance: Instance, owner: Type[Instance]) -> Value:
            """ Called when an attribute is accessed on an instance variable """
    
        def __get__(self, instance: Union[Instance, None], owner: Type[Instance]) -> Union[Value, 'Descriptor']:
            """ Full implementation is declared here """
            ...
    
        def __set__(self, instance: Instance, value: Value):
            """ Called when setting a value."""