-
After reading this related issue I'm struggling to follow the advice. I'm working in an existing codebase that I want to annotate accurately to aid in future refactoring. from typing import overload, Any, Literal
class _CalcDescriptor:
@overload
def __get__(self, instance: "ClsWithType[Literal['foo']]", owner: Any) -> Literal[1]:
# error: "__get__" is marked as overload, but no implementation is provided (reportNoOverloadImplementation)
...
@overload
def __get__(self, instance: "ClsWithType[Literal['bar']]", owner: Any) -> Literal["bar"]:
...
class ClsWithType[T: Literal["foo", "bar"]]:
calc: _CalcDescriptor
# error: Declaration "calc" is obscured by a declaration of the same name (reportRedeclaration)
def __init__(self, typ: T):
self.type = typ
@property
def calc(self):
if self.type == "foo":
return 1
else:
return "bar"
reveal_type(ClsWithType("foo"))
# Type of "ClsWithType("foo")" is "ClsWithType[Literal['foo']]"
reveal_type(ClsWithType("foo").calc)
# Type of "ClsWithType("foo").calc" is "Literal[1, 'bar']"
# Going for: Literal[1]
reveal_type(ClsWithType("bar"))
# Type of "ClsWithType("bar")" is "ClsWithType[Literal['bar']]"
reveal_type(ClsWithType("bar").calc)
# Type of "ClsWithType("bar").calc" is "Literal[1, 'bar']"
# Going for: Literal["bar"] The errors seem straightforward, but this is based on a super-naive and literal interpretation of the linked comment. I am not familiar with using descriptors in this way. I've tried a few variations, and the only one I've been able to get to work is the classic descriptor implementation with the behavior actually included, and actually instantiated: from typing import overload, Any, Literal
class _CalcDescriptor:
@overload
def __get__(self, instance: "ClsWithType[Literal['foo']]", owner: Any) -> Literal[1]:
...
@overload
def __get__(self, instance: "ClsWithType[Literal['bar']]", owner: Any) -> Literal["bar"]:
...
def __get__(self, instance: "ClsWithType", owner: Any):
if instance.type == "foo":
return 1
else:
return "bar"
class ClsWithType[T: Literal["foo", "bar"]]:
calc = _CalcDescriptor()
def __init__(self, typ: T):
self.type = typ
reveal_type(ClsWithType("foo"))
# Type of "ClsWithType("foo")" is "ClsWithType[Literal['foo']]"
reveal_type(ClsWithType("foo").calc)
# Type of "ClsWithType("foo").calc" is "Literal[1]"
reveal_type(ClsWithType("bar"))
# Type of "ClsWithType("bar")" is "ClsWithType[Literal['bar']]"
reveal_type(ClsWithType("bar").calc)
# Type of "ClsWithType("bar").calc" is "Literal['bar']" In my real-world codebase, I'm aiming to avoid altering the method code. I feel like that's possible based on my interpretation of the linked comment, but I must be missing something in my implementation. Or I'm simply interpreting the comment wrong :) Thanks in advance! ❤️ Matt |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
I wouldn't recommend designing a class this way. If this code is going to be maintained over time, you may want to invest in a redesign. If you don't want to modify the code, you'll need to use some unsavory hacks to make it type check. The tool I'd reach for in this case is if TYPE_CHECKING:
calc: _CalcDescriptor
else:
@property
def calc(self): ... Code sample in pyright playground |
Beta Was this translation helpful? Give feedback.
I wouldn't recommend designing a class this way. If this code is going to be maintained over time, you may want to invest in a redesign.
If you don't want to modify the code, you'll need to use some unsavory hacks to make it type check. The tool I'd reach for in this case is
TYPE_CHECKING
. This makes for very fragile code, but given the constraints, you don't have many options.Code sample in pyright playground