Skip to content

Commit

Permalink
Rework to use a single tuple schema that can express positional, vari…
Browse files Browse the repository at this point in the history
…able, and variadic schemas
  • Loading branch information
dmontagu authored and davidhewitt committed Nov 14, 2023
1 parent baed943 commit 2a65bb2
Show file tree
Hide file tree
Showing 19 changed files with 599 additions and 518 deletions.
2 changes: 1 addition & 1 deletion generate_self_schema.py
Expand Up @@ -142,7 +142,7 @@ def type_dict_schema( # noqa: C901
'type': 'union',
'choices': [
schema_ref_validator,
{'type': 'tuple-positional', 'items_schema': [schema_ref_validator, {'type': 'str'}]},
{'type': 'tuple', 'items_schema': [schema_ref_validator, {'type': 'str'}]},
],
},
}
Expand Down
105 changes: 74 additions & 31 deletions python/pydantic_core/core_schema.py
Expand Up @@ -1384,16 +1384,7 @@ def list_schema(
)


class TuplePositionalSchema(TypedDict, total=False):
type: Required[Literal['tuple-positional']]
items_schema: Required[List[CoreSchema]]
extras_schema: CoreSchema
strict: bool
ref: str
metadata: Any
serialization: IncExSeqOrElseSerSchema


# @deprecated('tuple_positional_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.')
def tuple_positional_schema(
items_schema: list[CoreSchema],
*,
Expand All @@ -1402,7 +1393,7 @@ def tuple_positional_schema(
ref: str | None = None,
metadata: Any = None,
serialization: IncExSeqOrElseSerSchema | None = None,
) -> TuplePositionalSchema:
) -> TupleSchema:
"""
Returns a schema that matches a tuple of schemas, e.g.:
Expand All @@ -1427,20 +1418,70 @@ def tuple_positional_schema(
metadata: Any other information you want to include with the schema, not used by pydantic-core
serialization: Custom serialization schema
"""
return _dict_not_none(
type='tuple-positional',
if extras_schema is not None:
variadic_item_index = len(items_schema)
items_schema = items_schema + [extras_schema]
else:
variadic_item_index = None
return tuple_schema(
items_schema=items_schema,
extras_schema=extras_schema,
variadic_item_index=variadic_item_index,
strict=strict,
ref=ref,
metadata=metadata,
serialization=serialization,
)


class TupleVariableSchema(TypedDict, total=False):
type: Required[Literal['tuple-variable']]
items_schema: CoreSchema
# @deprecated('tuple_variable_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.')
def tuple_variable_schema(
items_schema: CoreSchema | None = None,
*,
min_length: int | None = None,
max_length: int | None = None,
strict: bool | None = None,
ref: str | None = None,
metadata: Any = None,
serialization: IncExSeqOrElseSerSchema | None = None,
) -> TupleSchema:
"""
Returns a schema that matches a tuple of a given schema, e.g.:
```py
from pydantic_core import SchemaValidator, core_schema
schema = core_schema.tuple_variable_schema(
items_schema=core_schema.int_schema(), min_length=0, max_length=10
)
v = SchemaValidator(schema)
assert v.validate_python(('1', 2, 3)) == (1, 2, 3)
```
Args:
items_schema: The value must be a tuple with items that match this schema
min_length: The value must be a tuple with at least this many items
max_length: The value must be a tuple with at most this many items
strict: The value must be a tuple with exactly this many items
ref: Optional unique identifier of the schema, used to reference the schema in other places
metadata: Any other information you want to include with the schema, not used by pydantic-core
serialization: Custom serialization schema
"""
return tuple_schema(
items_schema=[items_schema or any_schema()],
variadic_item_index=0,
min_length=min_length,
max_length=max_length,
strict=strict,
ref=ref,
metadata=metadata,
serialization=serialization,
)


class TupleSchema(TypedDict, total=False):
type: Required[Literal['tuple']]
items_schema: Required[List[CoreSchema]]
variadic_item_index: int
min_length: int
max_length: int
strict: bool
Expand All @@ -1449,41 +1490,45 @@ class TupleVariableSchema(TypedDict, total=False):
serialization: IncExSeqOrElseSerSchema


def tuple_variable_schema(
items_schema: CoreSchema | None = None,
def tuple_schema(
items_schema: list[CoreSchema],
*,
variadic_item_index: int | None = None,
min_length: int | None = None,
max_length: int | None = None,
strict: bool | None = None,
ref: str | None = None,
metadata: Any = None,
serialization: IncExSeqOrElseSerSchema | None = None,
) -> TupleVariableSchema:
) -> TupleSchema:
"""
Returns a schema that matches a tuple of a given schema, e.g.:
Returns a schema that matches a tuple of schemas, with an optional variadic item, e.g.:
```py
from pydantic_core import SchemaValidator, core_schema
schema = core_schema.tuple_variable_schema(
items_schema=core_schema.int_schema(), min_length=0, max_length=10
schema = core_schema.tuple_schema(
[core_schema.int_schema(), core_schema.str_schema(), core_schema.float_schema()],
variadic_item_index=1,
)
v = SchemaValidator(schema)
assert v.validate_python(('1', 2, 3)) == (1, 2, 3)
assert v.validate_python((1, 'hello', 'world', 1.5)) == (1, 'hello', 'world', 1.5)
```
Args:
items_schema: The value must be a tuple with items that match this schema
items_schema: The value must be a tuple with items that match these schemas
variadic_item_index: The index of the schema in `items_schema` to be treated as variadic (following PEP 646)
min_length: The value must be a tuple with at least this many items
max_length: The value must be a tuple with at most this many items
strict: The value must be a tuple with exactly this many items
ref: optional unique identifier of the schema, used to reference the schema in other places
ref: Optional unique identifier of the schema, used to reference the schema in other places
metadata: Any other information you want to include with the schema, not used by pydantic-core
serialization: Custom serialization schema
"""
return _dict_not_none(
type='tuple-variable',
type='tuple',
items_schema=items_schema,
variadic_item_index=variadic_item_index,
min_length=min_length,
max_length=max_length,
strict=strict,
Expand Down Expand Up @@ -3631,8 +3676,7 @@ def definition_reference_schema(
IsSubclassSchema,
CallableSchema,
ListSchema,
TuplePositionalSchema,
TupleVariableSchema,
TupleSchema,
SetSchema,
FrozenSetSchema,
GeneratorSchema,
Expand Down Expand Up @@ -3686,8 +3730,7 @@ def definition_reference_schema(
'is-subclass',
'callable',
'list',
'tuple-positional',
'tuple-variable',
'tuple',
'set',
'frozenset',
'generator',
Expand Down
6 changes: 2 additions & 4 deletions src/serializers/shared.rs
Expand Up @@ -140,8 +140,7 @@ combined_serializer! {
Union: super::type_serializers::union::UnionSerializer;
Literal: super::type_serializers::literal::LiteralSerializer;
Recursive: super::type_serializers::definitions::DefinitionRefSerializer;
TuplePositional: super::type_serializers::tuple::TuplePositionalSerializer;
TupleVariable: super::type_serializers::tuple::TupleVariableSerializer;
Tuple: super::type_serializers::tuple::TupleSerializer;
}
}

Expand Down Expand Up @@ -248,8 +247,7 @@ impl PyGcTraverse for CombinedSerializer {
CombinedSerializer::Union(inner) => inner.py_gc_traverse(visit),
CombinedSerializer::Literal(inner) => inner.py_gc_traverse(visit),
CombinedSerializer::Recursive(inner) => inner.py_gc_traverse(visit),
CombinedSerializer::TuplePositional(inner) => inner.py_gc_traverse(visit),
CombinedSerializer::TupleVariable(inner) => inner.py_gc_traverse(visit),
CombinedSerializer::Tuple(inner) => inner.py_gc_traverse(visit),
CombinedSerializer::Uuid(inner) => inner.py_gc_traverse(visit),
}
}
Expand Down

0 comments on commit 2a65bb2

Please sign in to comment.