mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-08 20:58:05 +00:00
6.6 KiB
6.6 KiB
Length (len()
)
Literal and constructed iterables
Strings and bytes literals
reveal_type(len("no\rmal")) # revealed: Literal[6]
reveal_type(len(r"aw stri\ng")) # revealed: Literal[10]
reveal_type(len(r"conca\t" "ena\tion")) # revealed: Literal[14]
reveal_type(len(b"ytes lite" rb"al")) # revealed: Literal[11]
reveal_type(len("𝒰𝕹🄸©🕲𝕕ℇ")) # revealed: Literal[7]
# fmt: off
reveal_type(len( # revealed: Literal[7]
"""foo
bar"""
))
reveal_type(len( # revealed: Literal[9]
r"""foo\r
bar"""
))
reveal_type(len( # revealed: Literal[7]
b"""foo
bar"""
))
reveal_type(len( # revealed: Literal[9]
rb"""foo\r
bar"""
))
# fmt: on
Tuples
reveal_type(len(())) # revealed: Literal[0]
reveal_type(len((1,))) # revealed: Literal[1]
reveal_type(len((1, 2))) # revealed: Literal[2]
reveal_type(len(tuple())) # revealed: Literal[0]
# TODO: Handle star unpacks; Should be: Literal[0]
reveal_type(len((*[],))) # revealed: Literal[1]
# fmt: off
# TODO: Handle star unpacks; Should be: Literal[1]
reveal_type(len( # revealed: Literal[2]
(
*[],
1,
)
))
# fmt: on
# TODO: Handle star unpacks; Should be: Literal[2]
reveal_type(len((*[], 1, 2))) # revealed: Literal[3]
# TODO: Handle star unpacks; Should be: Literal[0]
reveal_type(len((*[], *{}))) # revealed: Literal[2]
Tuple subclasses:
class EmptyTupleSubclass(tuple[()]): ...
class Length1TupleSubclass(tuple[int]): ...
class Length2TupleSubclass(tuple[int, str]): ...
class UnknownLengthTupleSubclass(tuple[int, ...]): ...
reveal_type(len(EmptyTupleSubclass())) # revealed: Literal[0]
reveal_type(len(Length1TupleSubclass((1,)))) # revealed: Literal[1]
reveal_type(len(Length2TupleSubclass((1, "foo")))) # revealed: Literal[2]
reveal_type(len(UnknownLengthTupleSubclass((1, 2, 3)))) # revealed: int
reveal_type(tuple[int, int].__len__) # revealed: (self: tuple[int, int], /) -> Literal[2]
reveal_type(tuple[int, ...].__len__) # revealed: (self: tuple[int, ...], /) -> int
def f(x: tuple[int, int], y: tuple[int, ...]):
reveal_type(x.__len__) # revealed: () -> Literal[2]
reveal_type(y.__len__) # revealed: () -> int
reveal_type(EmptyTupleSubclass.__len__) # revealed: (self: tuple[()], /) -> Literal[0]
reveal_type(EmptyTupleSubclass().__len__) # revealed: () -> Literal[0]
reveal_type(UnknownLengthTupleSubclass.__len__) # revealed: (self: tuple[int, ...], /) -> int
reveal_type(UnknownLengthTupleSubclass().__len__) # revealed: () -> int
If __len__
is overridden, we use the overridden return type:
from typing import Literal
class UnknownLengthSubclassWithDunderLenOverridden(tuple[int, ...]):
def __len__(self) -> Literal[42]:
return 42
reveal_type(len(UnknownLengthSubclassWithDunderLenOverridden())) # revealed: Literal[42]
class FixedLengthSubclassWithDunderLenOverridden(tuple[int]):
# TODO: we should complain about this as a Liskov violation (incompatible override)
def __len__(self) -> Literal[42]:
return 42
reveal_type(len(FixedLengthSubclassWithDunderLenOverridden((1,)))) # revealed: Literal[42]
Lists, sets and dictionaries
reveal_type(len([])) # revealed: int
reveal_type(len([1])) # revealed: int
reveal_type(len([1, 2])) # revealed: int
reveal_type(len([*{}, *dict()])) # revealed: int
reveal_type(len({})) # revealed: int
reveal_type(len({**{}})) # revealed: int
reveal_type(len({**{}, **{}})) # revealed: int
reveal_type(len({1})) # revealed: int
reveal_type(len({1, 2})) # revealed: int
reveal_type(len({*[], 2})) # revealed: int
reveal_type(len(list())) # revealed: int
reveal_type(len(set())) # revealed: int
reveal_type(len(dict())) # revealed: int
reveal_type(len(frozenset())) # revealed: int
__len__
The returned value of __len__
is implicitly and recursively converted to int
.
Literal integers
from typing import Literal
class Zero:
def __len__(self) -> Literal[0]:
return 0
class ZeroOrOne:
def __len__(self) -> Literal[0, 1]:
return 0
class ZeroOrTrue:
def __len__(self) -> Literal[0, True]:
return 0
class OneOrFalse:
def __len__(self) -> Literal[1] | Literal[False]:
return 1
class OneOrFoo:
def __len__(self) -> Literal[1, "foo"]:
return 1
class ZeroOrStr:
def __len__(self) -> Literal[0] | str:
return 0
reveal_type(len(Zero())) # revealed: Literal[0]
reveal_type(len(ZeroOrOne())) # revealed: Literal[0, 1]
reveal_type(len(ZeroOrTrue())) # revealed: Literal[0, 1]
reveal_type(len(OneOrFalse())) # revealed: Literal[1, 0]
# TODO: Emit a diagnostic
reveal_type(len(OneOrFoo())) # revealed: int
# TODO: Emit a diagnostic
reveal_type(len(ZeroOrStr())) # revealed: int
Literal booleans
from typing import Literal
class LiteralTrue:
def __len__(self) -> Literal[True]:
return True
class LiteralFalse:
def __len__(self) -> Literal[False]:
return False
reveal_type(len(LiteralTrue())) # revealed: Literal[1]
reveal_type(len(LiteralFalse())) # revealed: Literal[0]
Enums
from enum import Enum, auto
from typing import Literal
class SomeEnum(Enum):
AUTO = auto()
INT = 2
STR = "4"
TUPLE = (8, "16")
INT_2 = 3_2
class Auto:
def __len__(self) -> Literal[SomeEnum.AUTO]:
return SomeEnum.AUTO
class Int:
def __len__(self) -> Literal[SomeEnum.INT]:
return SomeEnum.INT
class Str:
def __len__(self) -> Literal[SomeEnum.STR]:
return SomeEnum.STR
class Tuple:
def __len__(self) -> Literal[SomeEnum.TUPLE]:
return SomeEnum.TUPLE
class IntUnion:
def __len__(self) -> Literal[SomeEnum.INT, SomeEnum.INT_2]:
return SomeEnum.INT
reveal_type(len(Auto())) # revealed: int
reveal_type(len(Int())) # revealed: int
reveal_type(len(Str())) # revealed: int
reveal_type(len(Tuple())) # revealed: int
reveal_type(len(IntUnion())) # revealed: int
Negative integers
from typing import Literal
class Negative:
def __len__(self) -> Literal[-1]:
return -1
# TODO: Emit a diagnostic
reveal_type(len(Negative())) # revealed: int
Wrong signature
from typing import Literal
class SecondOptionalArgument:
def __len__(self, v: int = 0) -> Literal[0]:
return 0
class SecondRequiredArgument:
def __len__(self, v: int) -> Literal[1]:
return 1
# TODO: Emit a diagnostic
reveal_type(len(SecondOptionalArgument())) # revealed: Literal[0]
# TODO: Emit a diagnostic
reveal_type(len(SecondRequiredArgument())) # revealed: Literal[1]
No __len__
class NoDunderLen: ...
# error: [invalid-argument-type]
reveal_type(len(NoDunderLen())) # revealed: int