mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
[ty] Homogeneous and mixed tuples (#18600)
Some checks are pending
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
We already had support for homogeneous tuples (`tuple[int, ...]`). This PR extends this to also support mixed tuples (`tuple[str, str, *tuple[int, ...], str str]`). A mixed tuple consists of a fixed-length (possibly empty) prefix and suffix, and a variable-length portion in the middle. Every element of the variable-length portion must be of the same type. A homogeneous tuple is then just a mixed tuple with an empty prefix and suffix. The new data representation uses different Rust types for a fixed-length (aka heterogeneous) tuple. Another option would have been to use the `VariableLengthTuple` representation for all tuples, and to wrap the "variable + suffix" portion in an `Option`. I don't think that would simplify the method implementations much, though, since we would still have a 2×2 case analysis for most of them. One wrinkle is that the definition of the `tuple` class in the typeshed has a single typevar, and canonically represents a homogeneous tuple. When getting the class of a tuple instance, that means that we have to summarize our detailed mixed tuple type information into its "homogeneous supertype". (We were already doing this for heterogeneous types.) A similar thing happens when concatenating two mixed tuples: the variable-length portion and suffix of the LHS, and the prefix and variable-length portion of the RHS, all get unioned into the variable-length portion of the result. The LHS prefix and RHS suffix carry through unchanged. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
d9266284df
commit
ea812d0813
32 changed files with 2432 additions and 758 deletions
|
@ -69,8 +69,64 @@ def _(m: int, n: int):
|
|||
t[::0] # error: [zero-stepsize-in-slice]
|
||||
|
||||
tuple_slice = t[m:n]
|
||||
# TODO: Should be `tuple[Literal[1, 'a', b"b"] | None, ...]`
|
||||
reveal_type(tuple_slice) # revealed: tuple[Unknown, ...]
|
||||
reveal_type(tuple_slice) # revealed: tuple[Literal[1, "a", b"b"] | None, ...]
|
||||
```
|
||||
|
||||
## Slices of homogeneous and mixed tuples
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import Literal
|
||||
|
||||
def homogeneous(t: tuple[str, ...]) -> None:
|
||||
reveal_type(t[0]) # revealed: str
|
||||
reveal_type(t[1]) # revealed: str
|
||||
reveal_type(t[2]) # revealed: str
|
||||
reveal_type(t[3]) # revealed: str
|
||||
|
||||
reveal_type(t[-1]) # revealed: str
|
||||
reveal_type(t[-2]) # revealed: str
|
||||
reveal_type(t[-3]) # revealed: str
|
||||
reveal_type(t[-4]) # revealed: str
|
||||
|
||||
def mixed(s: tuple[str, ...]) -> None:
|
||||
t = (1, 2, 3) + s + (8, 9, 10)
|
||||
|
||||
reveal_type(t[0]) # revealed: Literal[1]
|
||||
reveal_type(t[1]) # revealed: Literal[2]
|
||||
reveal_type(t[2]) # revealed: Literal[3]
|
||||
reveal_type(t[3]) # revealed: str | Literal[8]
|
||||
reveal_type(t[4]) # revealed: str | Literal[8, 9]
|
||||
reveal_type(t[5]) # revealed: str | Literal[8, 9, 10]
|
||||
|
||||
reveal_type(t[-1]) # revealed: Literal[10]
|
||||
reveal_type(t[-2]) # revealed: Literal[9]
|
||||
reveal_type(t[-3]) # revealed: Literal[8]
|
||||
reveal_type(t[-4]) # revealed: Literal[3] | str
|
||||
reveal_type(t[-5]) # revealed: Literal[2, 3] | str
|
||||
reveal_type(t[-6]) # revealed: Literal[1, 2, 3] | str
|
||||
```
|
||||
|
||||
## `tuple` as generic alias
|
||||
|
||||
For tuple instances, we can track more detailed information about the length and element types of
|
||||
the tuple. This information carries over to the generic alias that the tuple is an instance of.
|
||||
|
||||
```py
|
||||
def _(a: tuple, b: tuple[int], c: tuple[int, str], d: tuple[int, ...]) -> None:
|
||||
reveal_type(a) # revealed: tuple[Unknown, ...]
|
||||
reveal_type(b) # revealed: tuple[int]
|
||||
reveal_type(c) # revealed: tuple[int, str]
|
||||
reveal_type(d) # revealed: tuple[int, ...]
|
||||
|
||||
reveal_type(tuple) # revealed: <class 'tuple'>
|
||||
reveal_type(tuple[int]) # revealed: <class 'tuple[int]'>
|
||||
reveal_type(tuple[int, str]) # revealed: <class 'tuple[int, str]'>
|
||||
reveal_type(tuple[int, ...]) # revealed: <class 'tuple[int, ...]'>
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
@ -83,8 +139,13 @@ python-version = "3.9"
|
|||
```py
|
||||
class A(tuple[int, str]): ...
|
||||
|
||||
# revealed: tuple[<class 'A'>, <class 'tuple[@Todo(Generic tuple specializations), ...]'>, <class 'Sequence[@Todo(Generic tuple specializations)]'>, <class 'Reversible[@Todo(Generic tuple specializations)]'>, <class 'Collection[@Todo(Generic tuple specializations)]'>, <class 'Iterable[@Todo(Generic tuple specializations)]'>, <class 'Container[@Todo(Generic tuple specializations)]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
||||
# revealed: tuple[<class 'A'>, <class 'tuple[int, str]'>, <class 'Sequence[int | str]'>, <class 'Reversible[int | str]'>, <class 'Collection[int | str]'>, <class 'Iterable[int | str]'>, <class 'Container[int | str]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
||||
reveal_type(A.__mro__)
|
||||
|
||||
class C(tuple): ...
|
||||
|
||||
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
||||
reveal_type(C.__mro__)
|
||||
```
|
||||
|
||||
## `typing.Tuple`
|
||||
|
@ -109,9 +170,19 @@ def _(c: Tuple, d: Tuple[int, A], e: Tuple[Any, ...]):
|
|||
Inheriting from `Tuple` results in a MRO with `builtins.tuple` and `typing.Generic`. `Tuple` itself
|
||||
is not a class.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.9"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import Tuple
|
||||
|
||||
class A(Tuple[int, str]): ...
|
||||
|
||||
# revealed: tuple[<class 'A'>, <class 'tuple[int, str]'>, <class 'Sequence[int | str]'>, <class 'Reversible[int | str]'>, <class 'Collection[int | str]'>, <class 'Iterable[int | str]'>, <class 'Container[int | str]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
||||
reveal_type(A.__mro__)
|
||||
|
||||
class C(Tuple): ...
|
||||
|
||||
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue