ruff/crates/red_knot_python_semantic/resources/mdtest/unary/not.md
InSync 15fe540251
Improve mdtests style (#14884)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-10 13:05:51 +00:00

4.9 KiB

Unary not

None

reveal_type(not None)  # revealed: Literal[True]
reveal_type(not not None)  # revealed: Literal[False]

Function

def f():
    return 1

reveal_type(not f)  # revealed: Literal[False]
# TODO Unknown should not be part of the type of typing.reveal_type
# reveal_type(not reveal_type)  revealed: Literal[False]

Module

import b
import warnings

reveal_type(not b)  # revealed: Literal[False]
reveal_type(not warnings)  # revealed: Literal[False]
y = 1

Union

def _(flag: bool):
    if flag:
        p = 1
        q = 3.3
        r = "hello"
        s = "world"
        t = 0
    else:
        p = "hello"
        q = 4
        r = ""
        s = 0
        t = ""

    reveal_type(not p)  # revealed: Literal[False]
    reveal_type(not q)  # revealed: bool
    reveal_type(not r)  # revealed: bool
    reveal_type(not s)  # revealed: bool
    reveal_type(not t)  # revealed: Literal[True]

Integer literal

reveal_type(not 1)  # revealed: Literal[False]
reveal_type(not 1234567890987654321)  # revealed: Literal[False]
reveal_type(not 0)  # revealed: Literal[True]
reveal_type(not -1)  # revealed: Literal[False]
reveal_type(not -1234567890987654321)  # revealed: Literal[False]
reveal_type(not --987)  # revealed: Literal[False]

Boolean literal

w = True
reveal_type(w)  # revealed: Literal[True]

x = False
reveal_type(x)  # revealed: Literal[False]

reveal_type(not w)  # revealed: Literal[False]

reveal_type(not x)  # revealed: Literal[True]

String literal

reveal_type(not "hello")  # revealed: Literal[False]
reveal_type(not "")  # revealed: Literal[True]
reveal_type(not "0")  # revealed: Literal[False]
reveal_type(not "hello" + "world")  # revealed: Literal[False]

Bytes literal

reveal_type(not b"hello")  # revealed: Literal[False]
reveal_type(not b"")  # revealed: Literal[True]
reveal_type(not b"0")  # revealed: Literal[False]
reveal_type(not b"hello" + b"world")  # revealed: Literal[False]

Tuple

reveal_type(not (1,))  # revealed: Literal[False]
reveal_type(not (1, 2))  # revealed: Literal[False]
reveal_type(not (1, 2, 3))  # revealed: Literal[False]
reveal_type(not ())  # revealed: Literal[True]
reveal_type(not ("hello",))  # revealed: Literal[False]
reveal_type(not (1, "hello"))  # revealed: Literal[False]

Instance

Not operator is inferred based on https://docs.python.org/3/library/stdtypes.html#truth-value-testing. An instance is True or False if the __bool__ method says so.

At runtime, the __len__ method is a fallback for __bool__, but we can't make use of that. If we have a class that defines __len__ but not __bool__, it is possible that any subclass could add a __bool__ method that would invalidate whatever conclusion we drew from __len__. So instances of classes without a __bool__ method, with or without __len__, must be inferred as unknown truthiness.

class AlwaysTrue:
    def __bool__(self) -> Literal[True]:
        return True

# revealed: Literal[False]
reveal_type(not AlwaysTrue())

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

# revealed: Literal[True]
reveal_type(not AlwaysFalse())

# We don't get into a cycle if someone sets their `__bool__` method to the `bool` builtin:
class BoolIsBool:
    __bool__ = bool

# revealed: bool
reveal_type(not BoolIsBool())

# At runtime, no `__bool__` and no `__len__` means truthy, but we can't rely on that, because
# a subclass could add a `__bool__` method.
class NoBoolMethod: ...

# revealed: bool
reveal_type(not NoBoolMethod())

# And we can't rely on `__len__` for the same reason: a subclass could add `__bool__`.
class LenZero:
    def __len__(self) -> Literal[0]:
        return 0

# revealed: bool
reveal_type(not LenZero())

class LenNonZero:
    def __len__(self) -> Literal[1]:
        return 1

# revealed: bool
reveal_type(not LenNonZero())

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

    def __len__(self) -> Literal[2]:
        return 2

# revealed: Literal[True]
reveal_type(not WithBothLenAndBool1())

class WithBothLenAndBool2:
    def __bool__(self) -> Literal[True]:
        return True

    def __len__(self) -> Literal[0]:
        return 0

# revealed: Literal[False]
reveal_type(not WithBothLenAndBool2())

# TODO: raise diagnostic when __bool__ method is not valid: [unsupported-operator] "Method __bool__ for type `MethodBoolInvalid` should return `bool`, returned type `int`"
# https://docs.python.org/3/reference/datamodel.html#object.__bool__
class MethodBoolInvalid:
    def __bool__(self) -> int:
        return 0

# revealed: bool
reveal_type(not MethodBoolInvalid())

# Don't trust a possibly-unbound `__bool__` method:
def get_flag() -> bool:
    return True

class PossiblyUnboundBool:
    if get_flag():
        def __bool__(self) -> Literal[False]:
            return False

# revealed: bool
reveal_type(not PossiblyUnboundBool())