Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 662
Rick supports Monica Avatar asked Apr 30 '19 17:04

Rick supports Monica


1 Answers

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: ...
like image 52
Alex Waygood Avatar answered Nov 10 '22 22:11

Alex Waygood