ruff/crates/red_knot_python_semantic/resources/mdtest/subscript/tuple.md
David Peter 000948ad3b
[red-knot] Statically known branches (#15019)
## Summary

This changeset adds support for precise type-inference and
boundness-handling of definitions inside control-flow branches with
statically-known conditions, i.e. test-expressions whose truthiness we
can unambiguously infer as *always false* or *always true*.

This branch also includes:
- `sys.platform` support
- statically-known branches handling for Boolean expressions and while
  loops
- new `target-version` requirements in some Markdown tests which were
  now required due to the understanding of `sys.version_info` branches.

closes #12700 
closes #15034 

## Performance

### `tomllib`, -7%, needs to resolve one additional module (sys)

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./red_knot_main --project /home/shark/tomllib` | 22.2 ± 1.3 | 19.1 |
25.6 | 1.00 |
| `./red_knot_feature --project /home/shark/tomllib` | 23.8 ± 1.6 | 20.8
| 28.6 | 1.07 ± 0.09 |

### `black`, -6%

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./red_knot_main --project /home/shark/black` | 129.3 ± 5.1 | 119.0 |
137.8 | 1.00 |
| `./red_knot_feature --project /home/shark/black` | 136.5 ± 6.8 | 123.8
| 147.5 | 1.06 ± 0.07 |

## Test Plan

- New Markdown tests for the main feature in
  `statically-known-branches.md`
- New Markdown tests for `sys.platform`
- Adapted tests for `EllipsisType`, `Never`, etc
2024-12-21 11:33:10 +01:00

3.9 KiB

Tuple subscripts

Indexing

t = (1, "a", "b")

reveal_type(t[0])  # revealed: Literal[1]
reveal_type(t[1])  # revealed: Literal["a"]
reveal_type(t[-1])  # revealed: Literal["b"]
reveal_type(t[-2])  # revealed: Literal["a"]

reveal_type(t[False])  # revealed: Literal[1]
reveal_type(t[True])  # revealed: Literal["a"]

a = t[4]  # error: [index-out-of-bounds]
reveal_type(a)  # revealed: Unknown

b = t[-4]  # error: [index-out-of-bounds]
reveal_type(b)  # revealed: Unknown

Slices

t = (1, "a", None, b"b")

reveal_type(t[0:0])  # revealed: tuple[()]
reveal_type(t[0:1])  # revealed: tuple[Literal[1]]
reveal_type(t[0:2])  # revealed: tuple[Literal[1], Literal["a"]]
reveal_type(t[0:4])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]
reveal_type(t[0:5])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]
reveal_type(t[1:3])  # revealed: tuple[Literal["a"], None]

reveal_type(t[-2:4])  # revealed: tuple[None, Literal[b"b"]]
reveal_type(t[-3:-1])  # revealed: tuple[Literal["a"], None]
reveal_type(t[-10:10])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]

reveal_type(t[0:])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]
reveal_type(t[2:])  # revealed: tuple[None, Literal[b"b"]]
reveal_type(t[4:])  # revealed: tuple[()]
reveal_type(t[:0])  # revealed: tuple[()]
reveal_type(t[:2])  # revealed: tuple[Literal[1], Literal["a"]]
reveal_type(t[:10])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]
reveal_type(t[:])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]

reveal_type(t[::-1])  # revealed: tuple[Literal[b"b"], None, Literal["a"], Literal[1]]
reveal_type(t[::2])  # revealed: tuple[Literal[1], None]
reveal_type(t[-2:-5:-1])  # revealed: tuple[None, Literal["a"], Literal[1]]
reveal_type(t[::-2])  # revealed: tuple[Literal[b"b"], Literal["a"]]
reveal_type(t[-1::-3])  # revealed: tuple[Literal[b"b"], Literal[1]]

reveal_type(t[None:2:None])  # revealed: tuple[Literal[1], Literal["a"]]
reveal_type(t[1:None:1])  # revealed: tuple[Literal["a"], None, Literal[b"b"]]
reveal_type(t[None:None:None])  # revealed: tuple[Literal[1], Literal["a"], None, Literal[b"b"]]

start = 1
stop = None
step = 2
reveal_type(t[start:stop:step])  # revealed: tuple[Literal["a"], Literal[b"b"]]

reveal_type(t[False:True])  # revealed: tuple[Literal[1]]
reveal_type(t[True:3])  # revealed: tuple[Literal["a"], None]

t[0:4:0]  # error: [zero-stepsize-in-slice]
t[:4:0]  # error: [zero-stepsize-in-slice]
t[0::0]  # error: [zero-stepsize-in-slice]
t[::0]  # error: [zero-stepsize-in-slice]

def _(m: int, n: int):
    tuple_slice = t[m:n]
    # TODO: Support overloads... Should be `tuple[Literal[1, 'a', b"b"] | None, ...]`
    reveal_type(tuple_slice)  # revealed: @Todo(return type)

Inheritance

[environment]
python-version = "3.9"
# TODO: `tuple[int, str]` is a valid base (generics)
# error: [invalid-base] "Invalid class base with type `GenericAlias` (all bases must be a class, `Any`, `Unknown` or `Todo`)"
class A(tuple[int, str]): ...

# Runtime value: `(A, tuple, object)`
# TODO: Generics
reveal_type(A.__mro__)  # revealed: tuple[Literal[A], Unknown, Literal[object]]

typing.Tuple

Correspondence with tuple

typing.Tuple can be used interchangeably with tuple:

from typing import Tuple

class A: ...

def _(c: Tuple, d: Tuple[int, A], e: Tuple[Any, ...]):
    reveal_type(c)  # revealed: tuple
    reveal_type(d)  # revealed: tuple[int, A]
    reveal_type(e)  # revealed: @Todo(full tuple[...] support)

Inheritance

Inheriting from Tuple results in a MRO with builtins.tuple and typing.Generic. Tuple itself is not a class.

from typing import Tuple

class C(Tuple): ...

# Runtime value: `(C, tuple, typing.Generic, object)`
# TODO: Add `Generic` to the MRO
reveal_type(C.__mro__)  # revealed: tuple[Literal[C], Literal[tuple], Unknown, Literal[object]]