mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-18 19:41:34 +00:00
## Summary closes https://github.com/astral-sh/ty/issues/1284 supersedes https://github.com/astral-sh/ruff/pull/20950 by @ibraheemdev ## Test Plan New regression test
1.9 KiB
1.9 KiB
Literal promotion
[environment]
python-version = "3.12"
There are certain places where we promote literals to their common supertype:
reveal_type([1, 2, 3]) # revealed: list[Unknown | int]
reveal_type({"a", "b", "c"}) # revealed: set[Unknown | str]
This promotion should not take place if the literal type appears in contravariant position:
from typing import Callable, Literal
def in_negated_position(non_zero_number: int):
if non_zero_number == 0:
raise ValueError()
reveal_type(non_zero_number) # revealed: int & ~Literal[0]
reveal_type([non_zero_number]) # revealed: list[Unknown | (int & ~Literal[0])]
def in_parameter_position(callback: Callable[[Literal[1]], None]):
reveal_type(callback) # revealed: (Literal[1], /) -> None
reveal_type([callback]) # revealed: list[Unknown | ((Literal[1], /) -> None)]
def double_negation(callback: Callable[[Callable[[Literal[1]], None]], None]):
reveal_type(callback) # revealed: ((Literal[1], /) -> None, /) -> None
reveal_type([callback]) # revealed: list[Unknown | (((int, /) -> None, /) -> None)]
Literal promotion should also not apply recursively to type arguments in contravariant/invariant position:
class Bivariant[T]:
pass
class Covariant[T]:
def pop(self) -> T:
raise NotImplementedError
class Contravariant[T]:
def push(self, value: T) -> None:
pass
class Invariant[T]:
x: T
def _(
bivariant: Bivariant[Literal[1]],
covariant: Covariant[Literal[1]],
contravariant: Contravariant[Literal[1]],
invariant: Invariant[Literal[1]],
):
reveal_type([bivariant]) # revealed: list[Unknown | Bivariant[int]]
reveal_type([covariant]) # revealed: list[Unknown | Covariant[int]]
reveal_type([contravariant]) # revealed: list[Unknown | Contravariant[Literal[1]]]
reveal_type([invariant]) # revealed: list[Unknown | Invariant[Literal[1]]]