Skip to content

Unique check #820

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions python/pydantic_core/core_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,7 @@ class ListSchema(TypedDict, total=False):
items_schema: CoreSchema
min_length: int
max_length: int
unique: bool
strict: bool
ref: str
metadata: Any
Expand All @@ -1251,6 +1252,7 @@ def list_schema(
*,
min_length: int | None = None,
max_length: int | None = None,
unique: bool | None = None,
strict: bool | None = None,
ref: str | None = None,
metadata: Any = None,
Expand All @@ -1271,6 +1273,7 @@ def list_schema(
items_schema: The value must be a list of items that match this schema
min_length: The value must be a list with at least this many items
max_length: The value must be a list with at most this many items
unique: The value must be a list without any repeated items
strict: The value must be a list 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
Expand All @@ -1281,6 +1284,7 @@ def list_schema(
items_schema=items_schema,
min_length=min_length,
max_length=max_length,
unique=unique,
strict=strict,
ref=ref,
metadata=metadata,
Expand All @@ -1292,6 +1296,7 @@ class TuplePositionalSchema(TypedDict, total=False):
type: Required[Literal['tuple-positional']]
items_schema: Required[List[CoreSchema]]
extra_schema: CoreSchema
unique: bool
strict: bool
ref: str
metadata: Any
Expand All @@ -1301,6 +1306,7 @@ class TuplePositionalSchema(TypedDict, total=False):
def tuple_positional_schema(
items_schema: list[CoreSchema],
*,
unique: bool | None = None,
extra_schema: CoreSchema | None = None,
strict: bool | None = None,
ref: str | None = None,
Expand All @@ -1322,6 +1328,7 @@ def tuple_positional_schema(

Args:
items_schema: The value must be a tuple with items that match these schemas
unique: The value must be a tuple without any repeated items
extra_schema: The value must be a tuple with items that match this schema
This was inspired by JSON schema's `prefixItems` and `items` fields.
In python's `typing.Tuple`, you can't specify a type for "extra" items -- they must all be the same type
Expand All @@ -1334,6 +1341,7 @@ def tuple_positional_schema(
return _dict_not_none(
type='tuple-positional',
items_schema=items_schema,
unique=unique,
extra_schema=extra_schema,
strict=strict,
ref=ref,
Expand All @@ -1347,6 +1355,7 @@ class TupleVariableSchema(TypedDict, total=False):
items_schema: CoreSchema
min_length: int
max_length: int
unique: bool
strict: bool
ref: str
metadata: Any
Expand All @@ -1358,6 +1367,7 @@ def tuple_variable_schema(
*,
min_length: int | None = None,
max_length: int | None = None,
unique: bool | None = None,
strict: bool | None = None,
ref: str | None = None,
metadata: Any = None,
Expand All @@ -1380,6 +1390,7 @@ def tuple_variable_schema(
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
unique: The value must be a tuple without any repeated 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
Expand All @@ -1390,6 +1401,7 @@ def tuple_variable_schema(
items_schema=items_schema,
min_length=min_length,
max_length=max_length,
unique=unique,
strict=strict,
ref=ref,
metadata=metadata,
Expand All @@ -1402,6 +1414,7 @@ class SetSchema(TypedDict, total=False):
items_schema: CoreSchema
min_length: int
max_length: int
unique: bool
strict: bool
ref: str
metadata: Any
Expand All @@ -1413,6 +1426,7 @@ def set_schema(
*,
min_length: int | None = None,
max_length: int | None = None,
unique: bool | None = None,
strict: bool | None = None,
ref: str | None = None,
metadata: Any = None,
Expand All @@ -1435,6 +1449,7 @@ def set_schema(
items_schema: The value must be a set with items that match this schema
min_length: The value must be a set with at least this many items
max_length: The value must be a set with at most this many items
unique: The value must be a set without any repeated items
strict: The value must be a set 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
Expand All @@ -1445,6 +1460,7 @@ def set_schema(
items_schema=items_schema,
min_length=min_length,
max_length=max_length,
unique=unique,
strict=strict,
ref=ref,
metadata=metadata,
Expand All @@ -1457,6 +1473,7 @@ class FrozenSetSchema(TypedDict, total=False):
items_schema: CoreSchema
min_length: int
max_length: int
unique: bool
strict: bool
ref: str
metadata: Any
Expand All @@ -1468,6 +1485,7 @@ def frozenset_schema(
*,
min_length: int | None = None,
max_length: int | None = None,
unique: bool | None = None,
strict: bool | None = None,
ref: str | None = None,
metadata: Any = None,
Expand All @@ -1490,6 +1508,7 @@ def frozenset_schema(
items_schema: The value must be a frozenset with items that match this schema
min_length: The value must be a frozenset with at least this many items
max_length: The value must be a frozenset with at most this many items
unique: The value must be a frozenset with no repeated items
strict: The value must be a frozenset 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
Expand All @@ -1500,6 +1519,7 @@ def frozenset_schema(
items_schema=items_schema,
min_length=min_length,
max_length=max_length,
unique=unique,
strict=strict,
ref=ref,
metadata=metadata,
Expand Down Expand Up @@ -3840,6 +3860,7 @@ def definition_reference_schema(
'finite_number',
'too_short',
'too_long',
'non_unique',
'iterable_type',
'iteration_error',
'string_type',
Expand Down
9 changes: 9 additions & 0 deletions src/errors/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ pub enum ErrorType {
actual_length: usize,
},
// ---------------------
// generic collection uniqueness error
NonUnique {
field_type: String,
},
// ---------------------
// generic collection and iteration errors
IterableType,
IterationError {
Expand Down Expand Up @@ -425,6 +430,7 @@ impl ErrorType {
max_length: usize,
actual_length: usize
),
Self::NonUnique { .. } => extract_context!(NonUnique, ctx, field_type: String),
Self::IterationError { .. } => extract_context!(IterationError, ctx, error: String),
Self::StringTooShort { .. } => extract_context!(StringTooShort, ctx, min_length: usize),
Self::StringTooLong { .. } => extract_context!(StringTooLong, ctx, max_length: usize),
Expand Down Expand Up @@ -502,6 +508,7 @@ impl ErrorType {
Self::FiniteNumber => "Input should be a finite number",
Self::TooShort {..} => "{field_type} should have at least {min_length} item{expected_plural} after validation, not {actual_length}",
Self::TooLong {..} => "{field_type} should have at most {max_length} item{expected_plural} after validation, not {actual_length}",
Self::NonUnique {..} => "{field_type} should be unique, but an item appeared more than once",
Self::IterableType => "Input should be iterable",
Self::IterationError {..} => "Error iterating over object, error: {error}",
Self::StringType => "Input should be a valid string",
Expand Down Expand Up @@ -647,6 +654,7 @@ impl ErrorType {
let expected_plural = plural_s(*max_length);
to_string_render!(tmpl, field_type, max_length, actual_length, expected_plural)
}
Self::NonUnique { field_type } => render!(tmpl, field_type),
Self::IterationError { error } => render!(tmpl, error),
Self::StringTooShort { min_length } => to_string_render!(tmpl, min_length),
Self::StringTooLong { max_length } => to_string_render!(tmpl, max_length),
Expand Down Expand Up @@ -719,6 +727,7 @@ impl ErrorType {
max_length,
actual_length,
} => py_dict!(py, field_type, max_length, actual_length),
Self::NonUnique { field_type } => py_dict!(py, field_type),
Self::IterationError { error } => py_dict!(py, error),
Self::StringTooShort { min_length } => py_dict!(py, min_length),
Self::StringTooLong { max_length } => py_dict!(py, max_length),
Expand Down
6 changes: 3 additions & 3 deletions src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ pub(crate) use datetime::{
pub(crate) use input_abstract::{Input, InputType};
pub(crate) use parse_json::{JsonInput, JsonObject};
pub(crate) use return_enums::{
py_string_str, AttributesGenericIterator, DictGenericIterator, EitherBytes, EitherFloat, EitherInt, EitherString,
GenericArguments, GenericIterable, GenericIterator, GenericMapping, Int, JsonArgs, JsonObjectGenericIterator,
MappingGenericIterator, PyArgs,
py_string_str, unique_check, AttributesGenericIterator, DictGenericIterator, EitherBytes, EitherFloat, EitherInt,
EitherString, GenericArguments, GenericIterable, GenericIterator, GenericMapping, Int, JsonArgs,
JsonObjectGenericIterator, MappingGenericIterator, PyArgs,
};

// Defined here as it's not exported by pyo3
Expand Down
Loading