crates/ty_python_semantic/resources/mdtest/unpacking.md
If there are not enough or too many values when unpacking, an error will occur and the types of all
variables (if nested tuple unpacking fails, only the variables within the failed tuples) is inferred
to be Unknown.
a, b, c = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
[a, b, c] = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
[a, (b, c), d] = (1, (2, 3), 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
reveal_type(d) # revealed: Literal[4]
a, b = c = 1, 2
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: tuple[Literal[1], Literal[2]]
a, (b, c), d = (1, (2, 3), 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
reveal_type(d) # revealed: Literal[4]
a, b, c = (1, (2, 3), 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: tuple[Literal[2], Literal[3]]
reveal_type(c) # revealed: Literal[4]
# error: [invalid-assignment] "Not enough values to unpack: Expected 3"
a, b, c = (1, 2)
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
a, b = (1, 2, 3)
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
a, (b, c), d = (1, (2,), 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Literal[3]
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
a, (b, c), d = (1, (2, 3, 4), 5)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Literal[5]
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 3"
[a, *b, c, d] = (1, 2)
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
[a, *b, c] = (1, 2)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Literal[2]
[a, *b, c] = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: list[int]
reveal_type(c) # revealed: Literal[3]
[a, *b, c, d] = (1, 2, 3, 4, 5, 6)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: list[int]
reveal_type(c) # revealed: Literal[5]
reveal_type(d) # revealed: Literal[6]
[a, b, *c] = (1, 2, 3, 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: list[int]
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 5"
a, b, c, *d, e, f = (1,)
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: list[Unknown]
reveal_type(e) # revealed: Unknown
reveal_type(f) # revealed: Unknown
For performance, ty widens inferred integer literal types to int in tuples with more than 64
elements. Unpacking preserves that widening: this unannotated assignment infers int for first
and list[int] for rest, including when the elements come from a list literal expansion. Widening
also applies inside nested tuple elements. Unpacking the small tuple (0, (1,), 2) instead infers
Literal[0] and Literal[1] for the fixed targets.
# fmt: off
first, (second,), *rest = (*[
0, (1,), 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64,
],)
# fmt: on
reveal_type(first) # revealed: int
reveal_type(second) # revealed: int
reveal_type(rest) # revealed: list[int]
# error: "Object of type `Literal[1]` is not iterable"
a, b = 1
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
# error: [not-iterable] "Object of type `Literal[1]` is not iterable"
# error: [invalid-assignment] "Cannot assign to a subscript on an object of type `Literal[1]`"
(1[0],) = 1
class Iterator:
def __next__(self) -> int:
return 42
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
a, b = Iterable()
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
class Iterator:
def __next__(self) -> int:
return 42
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
a, (b, c), d = (1, Iterable(), 2)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: int
reveal_type(c) # revealed: int
reveal_type(d) # revealed: Literal[2]
a, b = [1, 2]
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
A list literal without starred elements has a known length. If it cannot fill all targets, we report
the mismatch and infer Unknown for those targets, as with a tuple literal.
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
first, last = [1]
reveal_type(first) # revealed: Unknown
reveal_type(last) # revealed: Unknown
A starred target does not reduce the number of elements required by the fixed targets. On a length mismatch, its element type is also unknown.
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 2"
first, *rest, last = [1]
reveal_type(first) # revealed: Unknown
reveal_type(rest) # revealed: list[Unknown]
reveal_type(last) # revealed: Unknown
Without a starred target, every element needs a corresponding target. Extra elements cause an error and leave all targets with unknown types.
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
first, last = [1, 2, 3]
reveal_type(first) # revealed: Unknown
reveal_type(last) # revealed: Unknown
def _(value: list[int]):
a, b = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
def _(value: list[list[int]]):
a, (b, c) = value
reveal_type(a) # revealed: list[int]
reveal_type(b) # revealed: int
reveal_type(c) # revealed: int
def _(value: list[int]):
# error: [not-iterable] "Object of type `int` is not iterable"
a, (b, c) = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
def _(value: list[int]):
a, *b, c = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[int]
reveal_type(c) # revealed: int
Unpacking a list literal assigns each element to its corresponding target.
first: int
first, *rest = [1, "wrong"]
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
The starred target can also precede the fixed targets:
*rest, last = ["one", "two", 3]
reveal_type(rest) # revealed: list[str]
reveal_type(last) # revealed: Literal[3]
A starred target between fixed targets excludes both the prefix and the suffix from its element type:
[first, *rest, last] = [1, "two", "three", 4]
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(last) # revealed: Literal[4]
When the fixed targets consume every element, the starred target receives an empty list. As with an empty list literal, the element type is unknown, allowing values to be added later.
first, *rest, last = [1, 2]
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[Unknown]
reveal_type(last) # revealed: Literal[2]
rest.append(3)
(*empty,) = []
reveal_type(empty) # revealed: list[Unknown]
Element positions are preserved when list literals are nested inside other list or tuple literals.
(first, *rest), *outer_rest, (last,) = [[1, "two"], False, [3]]
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(outer_rest) # revealed: list[bool]
reveal_type(last) # revealed: Literal[3]
The same nested lists retain their element positions when the outer literal is a tuple.
(first, *rest), *outer_rest, (last,) = ([1, "two"], False, [3])
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(outer_rest) # revealed: list[bool]
reveal_type(last) # revealed: Literal[3]
Unpacking another iterable alongside a list literal does not affect the literal's element types.
def nested(values: list[int]):
(first, *rest), (other,) = ([1, "two"], values)
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(other) # revealed: int
If a nested list has too few elements, only the targets unpacked from that list get unknown types. The sibling target retains its corresponding element's type.
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 2"
(first, *rest, last), other = [[1], 2]
reveal_type(first) # revealed: Unknown
reveal_type(rest) # revealed: list[Unknown]
reveal_type(last) # revealed: Unknown
reveal_type(other) # revealed: Literal[2]
A starred outer target does not hide a length mismatch inside either kind of literal.
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
(first, last), *rest = ([1],)
reveal_type(first) # revealed: Unknown
reveal_type(last) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
(first, last), *rest = [[1]]
reveal_type(first) # revealed: Unknown
reveal_type(last) # revealed: Unknown
An incompatible element still causes an error for its corresponding target.
first: int
# error: [invalid-assignment] "Object of type `Literal["wrong"]` is not assignable to `int`"
first, *rest = ["wrong", 1]
reveal_type(rest) # revealed: list[int]
The starred target is checked against the list of collected elements.
numbers: list[int]
# error: [invalid-assignment] "Object of type `list[str]` is not assignable to `list[int]`"
first, *numbers = [1, "wrong"]
A starred target receives a new list. Inferred literal element types are promoted, as in a list literal, so additional values of the same type can be appended.
first, *rest = [1, "two"]
rest.append("three")
reveal_type(rest) # revealed: list[str]
first, *rest = (1, "two")
rest.append("three")
reveal_type(rest) # revealed: list[str]
first, *rest = (1,)
rest.append("three")
reveal_type(rest) # revealed: list[Unknown]
The collected elements are also compatible with an explicitly annotated list.
strings: list[str]
first, *strings = [1, "two"]
first, *strings = [1]
Singleton values follow the same inference rules as in a list literal.
optional: list[int | None]
first, *optional = [1, None]
first, *optional = (1, None)
Explicit literal annotations are preserved when constructing the collected list.
from typing import Literal
def explicit_literal(value: Literal["one", "two"]):
first, *rest = [1, value]
reveal_type(rest) # revealed: list[Literal["one", "two"]]
first, *rest = (1, value)
reveal_type(rest) # revealed: list[Literal["one", "two"]]
Homogeneous tuple literals of different lengths are promoted to a variable-length tuple element type, as in an ordinary list literal.
rest: list[tuple[int, ...]]
first, *rest = [(1,), (2,), (3, 4)]
reveal_type(first) # revealed: tuple[Literal[1]]
reveal_type(rest) # revealed: list[tuple[int, ...]]
first, *rest = ((1,), (2,), (3, 4))
reveal_type(first) # revealed: tuple[Literal[1]]
reveal_type(rest) # revealed: list[tuple[int, ...]]
A tuple from a variable retains its annotated shape and prevents tuple-size promotion for the collected elements.
def annotated_tuple(value: tuple[int, int]):
first, *rest = [0, (1,), value]
reveal_type(rest) # revealed: list[tuple[int] | tuple[int, int]]
first, *rest = (0, (1,), value)
reveal_type(rest) # revealed: list[tuple[int] | tuple[int, int]]
Tuple literals collected between multiple expansions remain eligible for tuple-size promotion.
def expanded_tuples(values: list[str]):
first, *rest, last = (0, *values, (1,), *values, (2, 3), False)
reveal_type(first) # revealed: Literal[0]
reveal_type(rest) # revealed: list[str | tuple[int, ...]]
reveal_type(last) # revealed: Literal[False]
first, *rest, last = [0, *values, (1,), *values, (2, 3), False]
reveal_type(first) # revealed: Literal[0]
reveal_type(rest) # revealed: list[str | tuple[int, ...]]
reveal_type(last) # revealed: Literal[False]
A starred element can contribute an unknown number of values. The literal's AST length does not determine whether it can fill the targets.
def unpack(values: list[int]):
first, *rest, last = [*values]
reveal_type(first) # revealed: int
reveal_type(rest) # revealed: list[int]
reveal_type(last) # revealed: int
Known elements before and after a starred expression keep their positions, for both tuple and list
literals. Only the starred target collects the elements supplied by values.
def fixed_ends(values: list[str]):
first: int
first, *rest, last = (1, *values, 2)
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(last) # revealed: Literal[2]
first, *rest, last = [1, *values, 2]
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(last) # revealed: Literal[2]
When an expansion may be empty, a fixed target can receive either one of its elements or a value from the other side of the expansion. We combine those possibilities without losing the types of the unambiguous targets.
def ambiguous(values: list[str]):
first, second, *rest = (0, *values, 1)
reveal_type(first) # revealed: Literal[0]
reveal_type(second) # revealed: str | Literal[1]
reveal_type(rest) # revealed: list[str | int]
first, second, *rest = [0, *values, 1]
reveal_type(first) # revealed: Literal[0]
reveal_type(second) # revealed: str | Literal[1]
reveal_type(rest) # revealed: list[str | int]
Expanding a literal preserves its elements and length. These assignments fail even though their right-hand sides contain starred expressions.
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 2"
first, *rest, last = (*(1,),)
reveal_type(first) # revealed: Unknown
reveal_type(rest) # revealed: list[Unknown]
reveal_type(last) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 2"
first, *rest, last = [*[1]]
reveal_type(first) # revealed: Unknown
reveal_type(rest) # revealed: list[Unknown]
reveal_type(last) # revealed: Unknown
A dictionary literal with a single key supplies one element.
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 2"
first, *rest, last = (*{"key": 1},)
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 2"
first, *rest, last = [*{"key": 1}]
A named expression preserves the structure of its value when unpacked immediately. The bound list itself still has an ordinary list type.
first: int
first, *rest = (items := (1, "two"))
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
first, *rest = (items := [1, "two"])
reveal_type(first) # revealed: Literal[1]
reveal_type(rest) # revealed: list[str]
reveal_type(items) # revealed: list[int | str]
Collected lists retain aliases in their element types.
[environment]
python-version = "3.12"
type Element = int | str
def aliases(value: Element):
first, *rest = (value, value)
reveal_type(first) # revealed: int | str
reveal_type(rest) # revealed: list[Element]
first, *rest = [value, value]
reveal_type(first) # revealed: int | str
reveal_type(rest) # revealed: list[Element]
def _(value: tuple[int, ...]):
a, b = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
def _(value: tuple[tuple[int, ...], ...]):
a, (b, c) = value
reveal_type(a) # revealed: tuple[int, ...]
reveal_type(b) # revealed: int
reveal_type(c) # revealed: int
def _(value: tuple[int, ...]):
# error: [not-iterable] "Object of type `int` is not iterable"
a, (b, c) = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
def _(value: tuple[int, ...]):
a, *b, c = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[int]
reveal_type(c) # revealed: int
[environment]
python-version = "3.11"
def _(value: tuple[int, *tuple[str, ...]]):
a, b = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: str
def _(value: tuple[int, int, *tuple[str, ...]]):
a, b = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
def _(value: tuple[int, *tuple[str, ...], int]):
a, b, c = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: str
reveal_type(c) # revealed: int
def _(value: tuple[int, int, int, *tuple[str, ...]]):
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
a, b = value
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
def _(value: tuple[str, *tuple[tuple[int, ...], ...]]):
a, (b, c) = value
reveal_type(a) # revealed: str
reveal_type(b) # revealed: int
reveal_type(c) # revealed: int
def _(value: tuple[str, *tuple[int, ...]]):
# error: [not-iterable] "Object of type `int` is not iterable"
a, (b, c) = value
reveal_type(a) # revealed: str
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
def _(value: tuple[int, *tuple[str, ...]]):
a, *b, c = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[str]
reveal_type(c) # revealed: str
def _(value: tuple[int, *tuple[str, ...], int]):
a, *b, c = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[str]
reveal_type(c) # revealed: int
def _(value: tuple[int, *tuple[str, ...], int]):
a, *b, c, d = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[str]
reveal_type(c) # revealed: str
reveal_type(d) # revealed: int
def _(value: tuple[int, int, *tuple[str, ...], int]):
a, *b, c = value
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[int | str]
reveal_type(c) # revealed: int
A tuple subclass inherits its heterogeneous unpacking behaviour from its tuple superclass.
[environment]
python-version = "3.11"
class I0: ...
class I1: ...
class I2: ...
class HeterogeneousTupleSubclass(tuple[I0, I1, I2]): ...
def f(x: HeterogeneousTupleSubclass):
a, b, c = x
reveal_type(a) # revealed: I0
reveal_type(b) # revealed: I1
reveal_type(c) # revealed: I2
d, e = x # error: [invalid-assignment] "Too many values to unpack: Expected 2"
reveal_type(d) # revealed: Unknown
reveal_type(e) # revealed: Unknown
f, g, h, i = x # error: [invalid-assignment] "Not enough values to unpack: Expected 4"
reveal_type(f) # revealed: Unknown
reveal_type(g) # revealed: Unknown
reveal_type(h) # revealed: Unknown
reveal_type(i) # revealed: Unknown
[j, *k] = x
reveal_type(j) # revealed: I0
reveal_type(k) # revealed: list[I1 | I2]
[l, m, *n] = x
reveal_type(l) # revealed: I0
reveal_type(m) # revealed: I1
reveal_type(n) # revealed: list[I2]
[o, p, q, *r] = x
reveal_type(o) # revealed: I0
reveal_type(p) # revealed: I1
reveal_type(q) # revealed: I2
reveal_type(r) # revealed: list[Unknown]
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 4"
[s, t, u, v, *w] = x
reveal_type(s) # revealed: Unknown
reveal_type(t) # revealed: Unknown
reveal_type(u) # revealed: Unknown
reveal_type(v) # revealed: Unknown
reveal_type(w) # revealed: list[Unknown]
class MixedTupleSubclass(tuple[I0, *tuple[I1, ...], I2]): ...
def f(x: MixedTupleSubclass):
(a,) = x # error: [invalid-assignment] "Too many values to unpack: Expected 1"
reveal_type(a) # revealed: Unknown
c, d = x
reveal_type(c) # revealed: I0
reveal_type(d) # revealed: I2
e, f, g = x
reveal_type(e) # revealed: I0
reveal_type(f) # revealed: I1
reveal_type(g) # revealed: I2
h, i, j, k = x
reveal_type(h) # revealed: I0
reveal_type(i) # revealed: I1
reveal_type(j) # revealed: I1
reveal_type(k) # revealed: I2
[l, *m] = x
reveal_type(l) # revealed: I0
reveal_type(m) # revealed: list[I1 | I2]
[n, o, *p] = x
reveal_type(n) # revealed: I0
reveal_type(o) # revealed: I1 | I2
reveal_type(p) # revealed: list[I1 | I2]
[o, p, q, *r] = x
reveal_type(o) # revealed: I0
reveal_type(p) # revealed: I1 | I2
reveal_type(q) # revealed: I1 | I2
reveal_type(r) # revealed: list[I1 | I2]
s, *t, u = x
reveal_type(s) # revealed: I0
reveal_type(t) # revealed: list[I1]
reveal_type(u) # revealed: I2
aa, bb, *cc, dd = x
reveal_type(aa) # revealed: I0
reveal_type(bb) # revealed: I1
reveal_type(cc) # revealed: list[I1]
reveal_type(dd) # revealed: I2
a, b = "ab"
reveal_type(a) # revealed: Literal["a"]
reveal_type(b) # revealed: Literal["b"]
# error: [invalid-assignment] "Not enough values to unpack: Expected 3"
a, b, c = "ab"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
a, b = "abc"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 3"
a, *b, c, d = "ab"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 3"
a, b, *c, d = "a"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: list[Unknown]
reveal_type(d) # revealed: Unknown
a, *b, c = "ab"
reveal_type(a) # revealed: Literal["a"]
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Literal["b"]
a, *b, c = "abc"
reveal_type(a) # revealed: Literal["a"]
reveal_type(b) # revealed: list[str]
reveal_type(c) # revealed: Literal["c"]
a, *b, c, d = "abcdef"
reveal_type(a) # revealed: Literal["a"]
reveal_type(b) # revealed: list[str]
reveal_type(c) # revealed: Literal["e"]
reveal_type(d) # revealed: Literal["f"]
a, b, *c = "abcd"
reveal_type(a) # revealed: Literal["a"]
reveal_type(b) # revealed: Literal["b"]
reveal_type(c) # revealed: list[str]
from typing_extensions import LiteralString
def _(s: LiteralString):
a, b, *c = s
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: LiteralString
reveal_type(c) # revealed: list[LiteralString]
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
a, b = "é"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
a, b = "\u9e6c"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
a, b = "\U0010ffff"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
a, b = "\ud800\udfff"
reveal_type(a) # revealed: Literal["�"]
reveal_type(b) # revealed: Literal["�"]
string = "very long stringgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
a, *b = string
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: list[LiteralString]
a, b = b"ab"
reveal_type(a) # revealed: Literal[97]
reveal_type(b) # revealed: Literal[98]
# error: [invalid-assignment] "Not enough values to unpack: Expected 3"
a, b, c = b"ab"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
a, b = b"abc"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 3"
a, *b, c, d = b"ab"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected at least 3"
a, b, *c, d = b"a"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: list[Unknown]
reveal_type(d) # revealed: Unknown
a, *b, c = b"ab"
reveal_type(a) # revealed: Literal[97]
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Literal[98]
a, *b, c = b"abc"
reveal_type(a) # revealed: Literal[97]
reveal_type(b) # revealed: list[int]
reveal_type(c) # revealed: Literal[99]
a, *b, c, d = b"abcdef"
reveal_type(a) # revealed: Literal[97]
reveal_type(b) # revealed: list[int]
reveal_type(c) # revealed: Literal[101]
reveal_type(d) # revealed: Literal[102]
a, b, *c = b"abcd"
reveal_type(a) # revealed: Literal[97]
reveal_type(b) # revealed: Literal[98]
reveal_type(c) # revealed: list[int]
too_long = b"very long bytes stringggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
a, *b = too_long
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[int]
Union of two tuples of equal length and each element is of the same type.
def _(arg: tuple[int, int] | tuple[int, int]):
a, b = arg
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
Union of two tuples of equal length and one element differs in its type.
def _(arg: tuple[int, int] | tuple[int, str]):
a, b = arg
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int | str
Union of two tuples of equal length and both the element types are different.
def _(arg: tuple[int, str] | tuple[str, int]):
a, b = arg
reveal_type(a) # revealed: int | str
reveal_type(b) # revealed: str | int
Union of three tuples of equal length and various combination of element types:
def _(arg: tuple[int, int, int] | tuple[int, str, bytes] | tuple[int, int, str]):
a, b, c = arg
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int | str
reveal_type(c) # revealed: int | bytes | str
from typing import Literal
def _(arg: tuple[int, tuple[str, bytes]] | tuple[tuple[int, bytes], Literal["ab"]]):
a, (b, c) = arg
reveal_type(a) # revealed: int | tuple[int, bytes]
reveal_type(b) # revealed: str
reveal_type(c) # revealed: bytes | Literal["b"]
def _(arg: tuple[int, bytes, int] | tuple[int, int, str, int, bytes]):
a, *b, c = arg
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[bytes] | list[int | str]
reveal_type(c) # revealed: int | bytes
def _(arg: tuple[int, bytes, int] | tuple[int, int, str, int, bytes]):
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
# error: [invalid-assignment] "Too many values to unpack: Expected 2"
a, b = arg
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
def _(arg: tuple[int, bytes] | tuple[int, str]):
# error: [invalid-assignment] "Not enough values to unpack: Expected 3"
# error: [invalid-assignment] "Not enough values to unpack: Expected 3"
a, b, c = arg
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
def _(flag: bool):
if flag:
value = (1, 2)
else:
value = (3, 4)
a, b = value
reveal_type(a) # revealed: Literal[1, 3]
reveal_type(b) # revealed: Literal[2, 4]
def _(flag: bool):
if flag:
value = (1, 2)
else:
value = ("a", "b")
a, b = value
reveal_type(a) # revealed: Literal[1, "a"]
reveal_type(b) # revealed: Literal[2, "b"]
from typing import Literal
def _(arg: tuple[int, int] | Literal["ab"]):
a, b = arg
reveal_type(a) # revealed: int | Literal["a"]
reveal_type(b) # revealed: int | Literal["b"]
class Iterator:
def __next__(self) -> tuple[int, int] | tuple[int, str]:
return (1, 2)
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
(a, b), c = Iterable()
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int | str
reveal_type(c) # revealed: tuple[int, int] | tuple[int, str]
class Iterator:
def __next__(self) -> bytes:
return b""
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
def _(arg: tuple[int, str] | Iterable):
a, b = arg
reveal_type(a) # revealed: int | bytes
reveal_type(b) # revealed: str | bytes
Unpacking in a for statement.
def _(arg: tuple[tuple[int, int], tuple[int, int]]):
for a, b in arg:
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
def _(arg: tuple[tuple[int, int], tuple[int, str]]):
for a, b in arg:
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int | str
def _(arg: tuple[tuple[int, str], tuple[str, int]]):
for a, b in arg:
reveal_type(a) # revealed: int | str
reveal_type(b) # revealed: str | int
def _(arg: tuple[tuple[int, int, int], tuple[int, str, bytes], tuple[int, int, str]]):
for a, b, c in arg:
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int | str
reveal_type(c) # revealed: int | bytes | str
for a, b in ((1, 2), (3, 4)):
reveal_type(a) # revealed: Literal[1, 3]
reveal_type(b) # revealed: Literal[2, 4]
for a, b in ((1, 2), ("a", "b")):
reveal_type(a) # revealed: Literal[1, "a"]
reveal_type(b) # revealed: Literal[2, "b"]
# error: "Object of type `Literal[1]` is not iterable"
# error: "Object of type `Literal[2]` is not iterable"
# error: "Object of type `Literal[4]` is not iterable"
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
for a, b in (1, 2, (3, "a"), 4, (5, "b"), "c"):
reveal_type(a) # revealed: Unknown | Literal[3, 5]
reveal_type(b) # revealed: Unknown | Literal["a", "b"]
class Iterator:
def __next__(self) -> tuple[int, int]:
return (1, 2)
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
for a, b in Iterable():
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
class Iterator:
def __next__(self) -> bytes:
return b""
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
def _(arg: tuple[tuple[int, str], Iterable]):
for a, b in arg:
reveal_type(a) # revealed: int | bytes
reveal_type(b) # revealed: str | bytes
Unpacking in a with statement.
class ContextManager:
def __enter__(self) -> tuple[int, int]:
return (1, 2)
def __exit__(self, exc_type, exc_value, traceback) -> None:
pass
with ContextManager() as (a, b):
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
class ContextManager:
def __enter__(self) -> tuple[int, str]:
return (1, "a")
def __exit__(self, exc_type, exc_value, traceback) -> None:
pass
with ContextManager() as (a, b):
reveal_type(a) # revealed: int
reveal_type(b) # revealed: str
class ContextManager:
def __enter__(self) -> tuple[int, tuple[str, bytes]]:
return (1, ("a", b"bytes"))
def __exit__(self, exc_type, exc_value, traceback) -> None:
pass
with ContextManager() as (a, (b, c)):
reveal_type(a) # revealed: int
reveal_type(b) # revealed: str
reveal_type(c) # revealed: bytes
class ContextManager:
def __enter__(self) -> tuple[int, int, int]:
return (1, 2, 3)
def __exit__(self, exc_type, exc_value, traceback) -> None:
pass
with ContextManager() as (a, *b):
reveal_type(a) # revealed: int
reveal_type(b) # revealed: list[int]
# error: [unresolved-reference] "Name `nonexistant` used when not defined"
with nonexistant as (x, y):
reveal_type(x) # revealed: Unknown
reveal_type(y) # revealed: Unknown
class ContextManager:
def __enter__(self) -> tuple[int, str]:
return (1, "a")
def __exit__(self, *args) -> None:
pass
# error: [invalid-assignment] "Not enough values to unpack: Expected 3"
with ContextManager() as (a, b, c):
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
Unpacking in a comprehension.
def _(arg: tuple[tuple[int, int], tuple[int, int]]):
# revealed: tuple[int, int]
[reveal_type((a, b)) for a, b in arg]
def _(arg: tuple[tuple[int, int], tuple[int, str]]):
# revealed: tuple[int, int | str]
[reveal_type((a, b)) for a, b in arg]
def _(arg: tuple[tuple[int, str], tuple[str, int]]):
# revealed: tuple[int | str, str | int]
[reveal_type((a, b)) for a, b in arg]
def _(arg: tuple[tuple[int, int, int], tuple[int, str, bytes], tuple[int, int, str]]):
# revealed: tuple[int, int | str, int | bytes | str]
[reveal_type((a, b, c)) for a, b, c in arg]
# revealed: tuple[Literal[1, 3], Literal[2, 4]]
[reveal_type((a, b)) for a, b in ((1, 2), (3, 4))]
# revealed: tuple[Literal[1, "a"], Literal[2, "b"]]
[reveal_type((a, b)) for a, b in ((1, 2), ("a", "b"))]
# error: "Object of type `Literal[1]` is not iterable"
# error: "Object of type `Literal[2]` is not iterable"
# error: "Object of type `Literal[4]` is not iterable"
# error: [invalid-assignment] "Not enough values to unpack: Expected 2"
# revealed: tuple[Unknown | Literal[3, 5], Unknown | Literal["a", "b"]]
[reveal_type((a, b)) for a, b in (1, 2, (3, "a"), 4, (5, "b"), "c")]
class Iterator:
def __next__(self) -> tuple[int, int]:
return (1, 2)
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
# revealed: tuple[int, int]
[reveal_type((a, b)) for a, b in Iterable()]
class Iterator:
def __next__(self) -> bytes:
return b""
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
def _(arg: tuple[tuple[int, str], Iterable]):
# revealed: tuple[int | bytes, str | bytes]
[reveal_type((a, b)) for a, b in arg]
Unpacking an empty tuple or list shouldn't raise any diagnostics.
[] = []
() = ()
[] = ()
() = []