Type infer error for decorator
class with __get__
not properly identified
type with Generic lose Type[]
The following are the comparisons of several scenarios.
from typing import TypeVar, Callable, Generic, Type
GetterReturnType = TypeVar("GetterReturnType")
class ClassPropertyDescriptor(Generic[GetterReturnType]):
def __init__(self, func: Callable[..., GetterReturnType]):
if isinstance(func, (classmethod, staticmethod)):
fget = func
else:
fget = classmethod(func)
self.fget = fget
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def classproperty(func: Callable[..., GetterReturnType]):
return ClassPropertyDescriptor(func)
T = TypeVar('T')
class B(Generic[T]):
pass
class A:
@classproperty
def a(self):
return [123]
@ClassPropertyDescriptor[list[int]]
def a2(self):
return [123]
@ClassPropertyDescriptor[Type[B]]
def b(self):
return B
@ClassPropertyDescriptor[Type[B[int]]]
def b2(self):
return B[int]
@ClassPropertyDescriptor[B[int]]
def b3(self):
return B[int]()
# should be list[int]
x = A.a
x2 = A.a2
y = A.b
# should be Type[B[int]]
y2 = A.b2
y3 = A.b3
Here's a screenshot of PyCharm:
__get__
Fully TypedTo help the type checker, modify the descriptor like this:
from typing import Generic, Callable, Type, TypeVar, Any
GetterReturnType = TypeVar("GetterReturnType")
Owner = TypeVar("Owner")
class ClassPropertyDescriptor(Generic[GetterReturnType]):
def __init__(self, func: Callable[..., GetterReturnType]):
if isinstance(func, (classmethod, staticmethod)):
fget = func
else:
fget = classmethod(func)
self.fget = fget
def __get__(self, obj: Any, klass: type | None = None) -> GetterReturnType:
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)() # This line's return type is now known
This helps some type checkers (e.g., Pyright) reason about what __get__
returns.
Tip: Use @overload
or reveal_type()
(in MyPy) for debugging
If your IDE/type checker still struggles, you can debug with:
from typing import reveal_type
reveal_type(A.a2) # should show list[int]
reveal_type(A.b2) # should show Type[B[int]]