Open Jackenmen opened 3 years ago
Another worthy test case checking for proper inference of types when self
is annotated as Type[T]
:
from typing import Any, Optional, Union, Type, TypeVar, TYPE_CHECKING, overload
T = TypeVar("T")
class _IntDescriptorMeta(type):
@overload
def __get__(self: Type[T], instance: None, owner: Any) -> Type[T]: ...
@overload
def __get__(self: Type[T], instance: object, owner: Any) -> T: ...
def __get__(self: Type[T], instance: Optional[object], owner: Any) -> Union[Type[T], T]:
if instance is None:
return self
return self()
class IntDescriptorClass(metaclass=_IntDescriptorMeta):
...
class X:
number_cls = IntDescriptorClass
print(X.number_cls)
print(X().number_cls)
if TYPE_CHECKING:
reveal_type(X.number_cls)
reveal_type(X().number_cls)
Taken from a pyright bug that was reported after this functionality was initially implemented: https://github.com/microsoft/pyright/issues/2177
This provides a very useful workaround to the lack of support for arbitrary class decorators; a metaclass and __get__()
could accomplish almost anything a decorator could.
Bug Report Descriptor protocol does not work on metaclasses - defining
__get__
method in a metaclass does not make classes using that metaclass work as descriptors.To Reproduce
class _IntDescriptorMeta(type): def get(self, instance: Any, owner: Any) -> int: return 123
class IntDescriptorClass(metaclass=_IntDescriptorMeta): ...
class IntDescriptor: def get(self, instance: Any, owner: Any) -> int: return 123
class X: number_cls = IntDescriptorClass number = IntDescriptor()
print(X.number_cls) print(X().number_cls) print(X.number) print(X().number)
if TYPE_CHECKING: reveal_type(X.number_cls) reveal_type(X().number_cls) reveal_type(X.number) reveal_type(X().number)
123 123 123 123
main.py:24: note: Revealed type is "builtins.int" main.py:25: note: Revealed type is "builtins.int" main.py:26: note: Revealed type is "builtins.int" main.py:27: note: Revealed type is "builtins.int"
main.py:24: note: Revealed type is "def () -> main.IntDescriptorClass" main.py:25: note: Revealed type is "def () -> main.IntDescriptorClass" main.py:26: note: Revealed type is "builtins.int" main.py:27: note: Revealed type is "builtins.int"