Validating Fixed-size and Variadic Tuples
In this codebase, tuple validation is handled primarily through the TupleSchema definition and the tuple_schema factory function located in pydantic-core/python/pydantic_core/core_schema.py. This implementation provides a unified approach to validating both fixed-size positional tuples and variadic tuples (homogeneous or mixed with a repeating element).
Unified Tuple Validation
The tuple_schema function replaces older, specialized schemas (like tuple_positional_schema and tuple_variable_schema) with a single interface. It defines how a tuple should be structured using the items_schema list and an optional variadic_item_index.
from pydantic_core import SchemaValidator, core_schema
# A simple fixed-size tuple: (int, str, float)
schema = core_schema.tuple_schema(
[
core_schema.int_schema(),
core_schema.str_schema(),
core_schema.float_schema()
]
)
v = SchemaValidator(schema)
assert v.validate_python((1, 'hello', 1.5)) == (1, 'hello', 1.5)
Fixed-size Positional Tuples
When variadic_item_index is not provided (or set to None), the schema validates a fixed-size tuple. The input must contain exactly the same number of elements as there are schemas in the items_schema list. Each element at a specific index is validated against the corresponding schema in the list.
If the input length does not match the items_schema length, validation fails. For example, in pydantic-core/tests/validators/test_tuple.py, validation of a fixed-size tuple with missing elements results in an error identifying the specific missing index in the loc field.
Variadic Tuples and PEP 646
The implementation supports variadic tuples by using the variadic_item_index field. This field specifies which schema in the items_schema list should be treated as the repeating element, allowing the tuple to accept an arbitrary number of items of that type at that position.
Homogeneous Variadic Tuples
To create a tuple that accepts any number of items of the same type (equivalent to tuple[int, ...]), you provide a single-item list to items_schema and set variadic_item_index=0.
# Equivalent to tuple[int, ...]
schema = core_schema.tuple_schema(
items_schema=[core_schema.int_schema()],
variadic_item_index=0
)
v = SchemaValidator(schema)
assert v.validate_python((1, 2, 3, 4)) == (1, 2, 3, 4)
Mixed Variadic Tuples
You can also define tuples with a fixed prefix or suffix and a variadic "tail" or "middle" element. The variadic_item_index determines which schema "expands" to consume extra elements.
# Variadic tuple: (int, str, float, float, ...)
# The element at index 2 (float) is variadic.
schema = core_schema.tuple_schema(
[core_schema.int_schema(), core_schema.str_schema(), core_schema.float_schema()],
variadic_item_index=2,
)
v = SchemaValidator(schema)
# The first two elements match int and str; all subsequent elements must match float.
assert v.validate_python((1, 'hello', 1.5, 2.5, 3.5)) == (1, 'hello', 1.5, 2.5, 3.5)
Strictness and Type Coercion
The TupleSchema includes a strict field that controls how the validator handles non-tuple inputs:
- Lax Mode (
strict=False, default): The validator accepts other iterable types such aslist,deque, or generators, and converts them into atupleupon successful validation. - Strict Mode (
strict=True): The validator only accepts actual Pythontupleobjects. Any other type, even if it contains valid elements, will result in a validation error.
Length Constraints and Performance
Beyond positional validation, TupleSchema supports explicit length constraints:
min_length: Ensures the resulting tuple has at least this many items.max_length: Ensures the resulting tuple has at most this many items.fail_fast: When set toTrue, the validator stops and returns an error immediately upon encountering the first invalid element, rather than validating the entire tuple and collecting all errors.
These constraints are particularly useful for variadic tuples where the number of repeating elements needs to be bounded.
Internal Structure
The TupleSchema is defined as a TypedDict in pydantic_core/core_schema.py:
class TupleSchema(TypedDict, total=False):
type: Required[Literal['tuple']]
items_schema: Required[list[CoreSchema]]
variadic_item_index: int
min_length: int
max_length: int
fail_fast: bool
strict: bool
ref: str
metadata: dict[str, Any]
serialization: IncExSeqOrElseSerSchema
This structure ensures that the underlying Rust validator receives all necessary metadata to perform efficient positional and variadic checks. When errors occur, the validator provides the index of the failing element in the error's loc (location) metadata, allowing for precise debugging of complex tuple structures.