|
3 | 3 | Import of this module is deferred since it contains imports of many standard library modules.
|
4 | 4 | """
|
5 | 5 |
|
6 |
| -# TODO: eventually, we'd like to move all of the types handled here to have pydantic-core validators |
7 |
| -# so that we can avoid this annotation injection and just use the standard pydantic-core schema generation |
| 6 | +# TODO: working on removing these types as the annotation injection pattern we use here is inconsistent |
| 7 | +# with the rest of the library and is not well supported by the pydantic-core schema generation |
8 | 8 |
|
9 | 9 | from __future__ import annotations as _annotations
|
10 | 10 |
|
|
22 | 22 | )
|
23 | 23 | from typing_extensions import get_args, get_origin
|
24 | 24 |
|
25 |
| -from pydantic._internal._serializers import serialize_sequence_via_list |
26 | 25 | from pydantic.errors import PydanticSchemaGenerationError
|
27 | 26 |
|
28 | 27 | from . import _known_annotated_metadata, _typing_extra
|
|
32 | 31 |
|
33 | 32 | FieldInfo = import_cached_field_info()
|
34 | 33 |
|
35 |
| -if typing.TYPE_CHECKING: |
36 |
| - from ._generate_schema import GenerateSchema |
37 |
| - |
38 |
| - StdSchemaFunction = Callable[[GenerateSchema, type[Any]], core_schema.CoreSchema] |
39 |
| - |
40 |
| - |
41 |
| -def deque_validator( |
42 |
| - input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, maxlen: None | int |
43 |
| -) -> collections.deque[Any]: |
44 |
| - if isinstance(input_value, collections.deque): |
45 |
| - maxlens = [v for v in (input_value.maxlen, maxlen) if v is not None] |
46 |
| - if maxlens: |
47 |
| - maxlen = min(maxlens) |
48 |
| - return collections.deque(handler(input_value), maxlen=maxlen) |
49 |
| - else: |
50 |
| - return collections.deque(handler(input_value), maxlen=maxlen) |
51 |
| - |
52 |
| - |
53 |
| -@dataclasses.dataclass(**slots_true) |
54 |
| -class DequeValidator: |
55 |
| - item_source_type: type[Any] |
56 |
| - metadata: dict[str, Any] |
57 |
| - |
58 |
| - def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: |
59 |
| - if _typing_extra.is_any(self.item_source_type): |
60 |
| - items_schema = None |
61 |
| - else: |
62 |
| - items_schema = handler.generate_schema(self.item_source_type) |
63 |
| - |
64 |
| - # if we have a MaxLen annotation might as well set that as the default maxlen on the deque |
65 |
| - # this lets us reuse existing metadata annotations to let users set the maxlen on a dequeue |
66 |
| - # that e.g. comes from JSON |
67 |
| - coerce_instance_wrap = partial( |
68 |
| - core_schema.no_info_wrap_validator_function, |
69 |
| - partial(deque_validator, maxlen=self.metadata.get('max_length', None)), |
70 |
| - ) |
71 |
| - |
72 |
| - # we have to use a lax list schema here, because we need to validate the deque's |
73 |
| - # items via a list schema, but it's ok if the deque itself is not a list |
74 |
| - metadata_with_strict_override = {**self.metadata, 'strict': False} |
75 |
| - constrained_schema = core_schema.list_schema(items_schema, **metadata_with_strict_override) |
76 |
| - |
77 |
| - check_instance = core_schema.json_or_python_schema( |
78 |
| - json_schema=core_schema.list_schema(), |
79 |
| - python_schema=core_schema.is_instance_schema(collections.deque), |
80 |
| - ) |
81 |
| - |
82 |
| - serialization = core_schema.wrap_serializer_function_ser_schema( |
83 |
| - serialize_sequence_via_list, schema=items_schema or core_schema.any_schema(), info_arg=True |
84 |
| - ) |
85 |
| - |
86 |
| - strict = core_schema.chain_schema([check_instance, coerce_instance_wrap(constrained_schema)]) |
87 |
| - |
88 |
| - if self.metadata.get('strict', False): |
89 |
| - schema = strict |
90 |
| - else: |
91 |
| - lax = coerce_instance_wrap(constrained_schema) |
92 |
| - schema = core_schema.lax_or_strict_schema(lax_schema=lax, strict_schema=strict) |
93 |
| - schema['serialization'] = serialization |
94 |
| - |
95 |
| - return schema |
96 |
| - |
97 |
| - |
98 |
| -def deque_schema_prepare_pydantic_annotations( |
99 |
| - source_type: Any, annotations: Iterable[Any] |
100 |
| -) -> tuple[Any, list[Any]] | None: |
101 |
| - args = get_args(source_type) |
102 |
| - |
103 |
| - if not args: |
104 |
| - args = typing.cast(Tuple[Any], (Any,)) |
105 |
| - elif len(args) != 1: |
106 |
| - raise ValueError('Expected deque to have exactly 1 generic parameter') |
107 |
| - |
108 |
| - item_source_type = args[0] |
109 |
| - |
110 |
| - metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) |
111 |
| - _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.SEQUENCE_CONSTRAINTS, source_type) |
112 |
| - |
113 |
| - return (source_type, [DequeValidator(item_source_type, metadata), *remaining_annotations]) |
114 |
| - |
115 | 34 |
|
116 | 35 | MAPPING_ORIGIN_MAP: dict[Any, Any] = {
|
117 | 36 | typing.DefaultDict: collections.defaultdict,
|
|
0 commit comments