Search code examples
pythonmypycallablepython-descriptors

mypy error: Callable has no attribute "__get__"


I have something like the following:

from typing import TypeVar, Callable, Generic, Type, Union, Optional

T = TypeVar("T")
V = TypeVar("V")

class DescClass(Generic[T, V]):
    """A descriptor."""
    def __init__(self, func: Callable[[T], V]) -> None:
        self.func = func

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

class C:
    @DescClass
    def f(self): ...

...for which Mypy will return this error:

test.py:12: error: "Callable[[T], Any]" has no attribute "__get__"

What is the canonical way to specify the type for func, so that Mypy understands it is a descriptor (and thus always has a __get__)?

Update: it's a bit humorous that "descriptor" has no hits when searching the Mypy help.


Solution

  • This appears to work fine on modern Python and modern mypy:

    from typing import (
        TypeVar,
        Callable,
        Generic, 
        Type, 
        Optional,
        cast,
        Protocol
    )
    
    T_contra = TypeVar("T_contra", contravariant=True)
    V = TypeVar("V")
    P_co = TypeVar("P_co", covariant=True)
    
    
    class DescriptorProto(Protocol[P_co, T_contra]):
        def __get__(
            self, 
            instance: Optional[T_contra], 
            owner: Type[T_contra]
        ) -> P_co:
            ...
    
    
    FuncType = DescriptorProto[Callable[[], V], T_contra]
    
    
    class DescClass(Generic[T_contra, V]):
        """A descriptor."""
    
        def __init__(self, func: Callable[[T_contra], V]) -> None:
            self.func = cast(FuncType[V, T_contra], func)
    
        def __get__(
            self, 
            instance: Optional[T_contra], 
            owner: Type[T_contra]
        ) -> Callable[[], V]:
            return self.func.__get__(instance, owner)
    
    
    class C:
        @DescClass
        def f(self) -> None: ...