If Python classes do not define __bool__ or __len__, bool(<class object>) defaults to True.
However, mypy (tested with: v1.15.0) doesn't seem to consider this.
class A:
def __init__(self) -> None:
self._value = 42
def foobar(self) -> int:
return self._value
def foobar(a: A | None) -> int | None:
# case: `a is None` ⇒ returns `None`
# case: `a is not None` ⇒ returns `42`
# however: mypy complains...
# `Incompatible return value type (got A | int | None), expected "int | None"`
return a and a.foobar()
To my understanding, the expression a and a.foobar() can only evaluate to type A if a is not None and bool(a) == False, which cannot happen. My conclusion is that mypy doesn't consider that a is not None ⇒ bool(a) == True.
Am I right that mypy simply fails on this? Is there any convenient workaround to write optional_obj and optional_obj.attribute in a mypy-compatible way?
You fail to consider subclasses:
(playground)
class B(A):
def __bool__(self) -> bool:
return False
reveal_type(foobar(B())) # `int | None` at type checking time,
# but `B` at runtime.
To avoid this pitfall, mark A as @final:
(playground)
from typing import final
@final
class A: ...
As for a better way to write optional_obj and optional_obj.attribute, see
How do I get Pylance to ignore the possibility of None?.
Is there any convenient workaround to write
optional_obj and optional_obj.attributein a mypy-compatible way?
Possibly you are trying to be too clever. This is not 100% equivalent to your original expression, but it might be what you actually meant:
optional_obj.attribute if optional_obj is not None else None
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With