That use of __bool__ was ordinary in the past, but now it’s wrong because it ignores modern type checking. You must at least narrow the return type for __bool__ to be Literal[False]:
def __bool__(self) -> Literal[False]:
return False
To be clear, whenever I impulsively say “abusing customization of bool” I specifically mean the following:
- New types using their own truthiness as a sorting category while also being mixed with other unknown types. There is a reason that
if x is not None:is common, thus the exampleMissingTypeis typically checked withx is MISSINGorisinstance(x, MissingType)rather thanif x:which is long known to cause insidious issues with generic unions of types. If you break this rule then it should be for a well-documented reason rather than laziness or inertia. The dangerous allure ofif x:is why I’ve previously suggested banning truthiness from sentinels. - Types with constant truthiness which are not detected by a type checker, return type hints must be
Literalfor these otherwise one has made a runtime-only pattern. - Types which disallow truth tests which are not caught by a type checker. A “not allowed to call this function return type” is still being discussed and I expect Numpy to use them once they’re added. Until then these issues are annoyingly silent until runtime.
Breaking these rules outside of a library might be okay if the only reviewer is oneself but as a library author I always default to the context of writing a library and having high standards for the API. When I see MissingType I think about how an empty string referring to the working directory would have the same truthiness as it as well as other worst case scenarios, but you’re likely to be using MissingType in a less speculative environment.