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.
Advertisement
Answer
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: ...