Inconsistency about subclass of typing.Any
#8346
-
For a class that inherits Example -- from typing import Any
class MyAny(Any): ...
def foo(x: int = MyAny):
if x is MyAny:
do_something_0() # Marked as unreachable.
y: int = int()
if y is MyAny:
do_something_1() # Marked as unreachable
z = int()
if z is MyAny:
do_something_2() It looks like the rule is inconsistent here. If In my project, I have a base class of sentinels, which is declared and used like -- from typing import Any, assert_type, final
class Sentinel(Any):
# Some common logic for all subclasses that mostly ensures `cls() is cls` and `isinstance(cls, cls)`.
...
@final
class MISSING(Sentinel):
pass
def f0(x: int | None = MISSING) -> None:
if x is MISSING:
return # Unreachable now.
_ = assert_type(x, "int | None")
def f1(x: int | None | MISSING = MISSING):
if isinstance(x, MISSING):
return # Still works.
_ = assert_type(x, "int | None") I know this is hackish. However, it lets me have different sentinels easily. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Yeah, you're relying on some behaviors here that I wouldn't expect would work. I think pyright's (new) behavior is justified in this case. Based on the behavior of the My recommendation is that you avoid using from typing import assert_type, final
class Sentinel: ...
@final
class MISSING(Sentinel): pass
def func(x: int | None | type[MISSING] = MISSING) -> None:
if x is MISSING:
return
_ = assert_type(x, "int | None") |
Beta Was this translation helpful? Give feedback.
Yeah, you're relying on some behaviors here that I wouldn't expect would work.
I think pyright's (new) behavior is justified in this case. Based on the behavior of the
is
operator, the code here is provably unreachable. Ifx
is constrained to be an instance ofint
, thenx is MyAny
will always evaluate to False. There is no possible materialization for theAny
base class that would make this evaluate to True, so it's reasonable for a type checker to mark this block as unreachable.My recommendation is that you avoid using
Any
as a base class in your sentinels and then properly annotate the parameter to indicate that it is allowed to accept either anint
or a sentinel.