#πŸ”’ mypy make object always falsy

26 messages Β· Page 1 of 1 (latest)

agile gate
#

Is there a way to indicate to mypy that a given object/type will always be falsy? For example:

class M(type):
    @staticmethod
    def __bool__() -> Literal[False]:
        return False

class C(metaclass=M):
    pass

This, for example, fails in the following case:

x: List[str] = [v for v in ("a", "b", C) if v]

because mypy cannot infer that C is falsy, and thus will not be in the list. If None were used in place of C, mypy does not complain:

x: List[str] = [v for v in ("a", "b", None) if v]

This also fails if C is an instance and __bool__ is instance-bound, i.e.:

class C:
    def __bool__(self) -> Literal[False]:
        return False

x: List[str] = [v for v in ("a", "b", C()) if v]
lime harborBOT
#

@agile gate

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

vast cradle
#

Actually, Your issue is that you need to use a TypeGuard

#

That's the only way to test if an object is a certain type.

agile gate
vast cradle
#

typeguard is a function with a single argument that returns a bool.

#

ex: ```py
from typing import TypeGuard

def is_str(arg: Any) -> TypeGuard[str]:
return arg and isinstance(arg, str)

agile gate
#

Ahh, I see

#

@vast cradle So something like the below would never realistically be possible I presume?

class Falsy(...):
    ...
# or bool(Falsy)
reveal_type(not not Falsy) # Literal[False]
vast cradle
#

You can do this. ```py
class FalseyMeta:
def invert(self) -> Literal[False]:
return False

class Falsey(metaclass=FalseyMeta): ...

reveal_type(~Falsey) # Type of "~Falsey" is "Literal[False]"

agile gate
#

Interesting

#

So why would that work while not Falsy (which, iirc, should just be giving the reverse of Falsy.__bool__) doesn't? (Or even bool(Falsy), which should just be calling Falsy.__bool__)
If you don't know that's fine, just curious as I am very new to mypy

vast cradle
#

You just can't reliably override bool(), not, and, or, is

agile gate
#

Ahh, unfortunate

vast cradle
#

bool() always expects a bool, so the type checker never assumes any other type.

#

if you try to return some other type, you get a TypeError. ```py

class X:
... def bool(self): return "hello"
...
bool(X())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bool should return bool, returned str

#

The same thing happens when you use not

#

The only way for not x to be Literal[False] is if x: Literal[True]

agile gate
#

So mypy is generalizing the Literal[False] return type of __bool__ to bool due to pythons return type restrictions?

vast cradle
#

So this will work. ```py
from typing import Literal, reveal_type

class Falsey:
def bool(self) -> Literal[True]:
return True

x = Falsey().bool()

reveal_type(not x) # Type of "not x" is "Literal[False]"

agile gate
#

Interesting

#

Thanks for the help @vast cradle πŸ™‚

vast cradle
#

This works because all the values are literals. ```py
x: list[str] = [y for y in ("hello", False, "world") if y]

agile gate
#

!close

lime harborBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.