Coverage for pydantic/_internal/_std_types_schema.py: 95.26%
305 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
1"""Logic for generating pydantic-core schemas for standard library types.
3Import of this module is deferred since it contains imports of many standard library modules.
4"""
6from __future__ import annotations as _annotations 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
8import collections 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
9import collections.abc 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
10import dataclasses 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
11import decimal 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
12import inspect 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
13import os 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
14import typing 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
15from enum import Enum 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
16from functools import partial 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
17from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
18from operator import attrgetter 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
19from typing import Any, Callable, Iterable, Literal, Tuple, TypeVar 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
21import typing_extensions 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
22from pydantic_core import ( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
23 CoreSchema,
24 MultiHostUrl,
25 PydanticCustomError,
26 PydanticOmit,
27 Url,
28 core_schema,
29)
30from typing_extensions import get_args, get_origin 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
32from pydantic.errors import PydanticSchemaGenerationError 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
33from pydantic.fields import FieldInfo 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
34from pydantic.types import Strict 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
36from ..config import ConfigDict 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
37from ..json_schema import JsonSchemaValue 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
38from . import _known_annotated_metadata, _typing_extra, _validators 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
39from ._core_utils import get_type_ref 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
40from ._internal_dataclass import slots_true 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
41from ._schema_generation_shared import GetCoreSchemaHandler, GetJsonSchemaHandler 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
43if typing.TYPE_CHECKING: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
44 from ._generate_schema import GenerateSchema
46 StdSchemaFunction = Callable[[GenerateSchema, type[Any]], core_schema.CoreSchema]
49@dataclasses.dataclass(**slots_true) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
50class SchemaTransformer: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
51 get_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
52 get_json_schema: Callable[[CoreSchema, GetJsonSchemaHandler], JsonSchemaValue] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
54 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
55 return self.get_core_schema(source_type, handler) 1ABabcdopqrefCDghijstuvEFklmnwxyz
57 def __get_pydantic_json_schema__(self, schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
58 return self.get_json_schema(schema, handler) 1ABabcdopqrefCDghijstuvEFklmnwxyz
61def get_enum_core_schema(enum_type: type[Enum], config: ConfigDict) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
62 cases: list[Any] = list(enum_type.__members__.values()) 1ABabcdopqrefCDghijstuvEFklmnwxyz
64 enum_ref = get_type_ref(enum_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
65 description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) 1ABabcdopqrefCDghijstuvEFklmnwxyz
66 if description == 'An enumeration.': # This is the default value provided by enum.EnumMeta.__new__; don't use it 1ABabcdopqrefCDghijstuvEFklmnwxyz
67 description = None 1ABabcdefCDghijEFklmn
68 js_updates = {'title': enum_type.__name__, 'description': description} 1ABabcdopqrefCDghijstuvEFklmnwxyz
69 js_updates = {k: v for k, v in js_updates.items() if v is not None} 1ABabcdopqrefCDghijstuvEFklmnwxyz
71 sub_type: Literal['str', 'int', 'float'] | None = None 1ABabcdopqrefCDghijstuvEFklmnwxyz
72 if issubclass(enum_type, int): 1ABabcdopqrefCDghijstuvEFklmnwxyz
73 sub_type = 'int' 1ABabcdopqrefCDghijstuvEFklmnwxyz
74 value_ser_type: core_schema.SerSchema = core_schema.simple_ser_schema('int') 1ABabcdopqrefCDghijstuvEFklmnwxyz
75 elif issubclass(enum_type, str): 1ABabcdopqrefCDghijstuvEFklmnwxyz
76 # this handles `StrEnum` (3.11 only), and also `Foobar(str, Enum)`
77 sub_type = 'str' 1ABabcdopqrefCDghijstuvEFklmnwxyz
78 value_ser_type = core_schema.simple_ser_schema('str') 1ABabcdopqrefCDghijstuvEFklmnwxyz
79 elif issubclass(enum_type, float): 1ABabcdopqrefCDghijstuvEFklmnwxyz
80 sub_type = 'float' 1abcdopqrefghijstuvklmnwxyz
81 value_ser_type = core_schema.simple_ser_schema('float') 1abcdopqrefghijstuvklmnwxyz
82 else:
83 # TODO this is an ugly hack, how do we trigger an Any schema for serialization?
84 value_ser_type = core_schema.plain_serializer_function_ser_schema(lambda x: x) 1ABabcdopqrefCDghijstuvEFklmnwxyz
86 if cases: 1ABabcdopqrefCDghijstuvEFklmnwxyz
88 def get_json_schema(schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1ABabcdopqrefCDghijstuvEFklmnwxyz
89 json_schema = handler(schema) 1ABabcdopqrefCDghijstuvEFklmnwxyz
90 original_schema = handler.resolve_ref_schema(json_schema) 1ABabcdopqrefCDghijstuvEFklmnwxyz
91 original_schema.update(js_updates) 1ABabcdopqrefCDghijstuvEFklmnwxyz
92 return json_schema 1ABabcdopqrefCDghijstuvEFklmnwxyz
94 # we don't want to add the missing to the schema if it's the default one
95 default_missing = getattr(enum_type._missing_, '__func__', None) == Enum._missing_.__func__ # type: ignore 1ABabcdopqrefCDghijstuvEFklmnwxyz
96 enum_schema = core_schema.enum_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
97 enum_type,
98 cases,
99 sub_type=sub_type,
100 missing=None if default_missing else enum_type._missing_,
101 ref=enum_ref,
102 metadata={'pydantic_js_functions': [get_json_schema]},
103 )
105 if config.get('use_enum_values', False): 1ABabcdopqrefCDghijstuvEFklmnwxyz
106 enum_schema = core_schema.no_info_after_validator_function( 1ABabcdopqrefCDghijstuvEFklmnwxyz
107 attrgetter('value'), enum_schema, serialization=value_ser_type
108 )
110 return enum_schema 1ABabcdopqrefCDghijstuvEFklmnwxyz
112 else:
114 def get_json_schema_no_cases(_, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1ABabcdopqrefCDghijstuvEFklmnwxyz
115 json_schema = handler(core_schema.enum_schema(enum_type, cases, sub_type=sub_type, ref=enum_ref)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
116 original_schema = handler.resolve_ref_schema(json_schema) 1ABabcdopqrefCDghijstuvEFklmnwxyz
117 original_schema.update(js_updates) 1ABabcdopqrefCDghijstuvEFklmnwxyz
118 return json_schema 1ABabcdopqrefCDghijstuvEFklmnwxyz
120 # Use an isinstance check for enums with no cases.
121 # The most important use case for this is creating TypeVar bounds for generics that should
122 # be restricted to enums. This is more consistent than it might seem at first, since you can only
123 # subclass enum.Enum (or subclasses of enum.Enum) if all parent classes have no cases.
124 # We use the get_json_schema function when an Enum subclass has been declared with no cases
125 # so that we can still generate a valid json schema.
126 return core_schema.is_instance_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
127 enum_type,
128 metadata={'pydantic_js_functions': [get_json_schema_no_cases]},
129 )
132@dataclasses.dataclass(**slots_true) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
133class InnerSchemaValidator: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
134 """Use a fixed CoreSchema, avoiding interference from outward annotations."""
136 core_schema: CoreSchema 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
137 js_schema: JsonSchemaValue | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
138 js_core_schema: CoreSchema | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
139 js_schema_update: JsonSchemaValue | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
141 def __get_pydantic_json_schema__(self, _schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
142 if self.js_schema is not None: 142 ↛ 143line 142 didn't jump to line 143, because the condition on line 142 was never true1ABabcdopqrefCDghijstuvEFklmnwxyz
143 return self.js_schema
144 js_schema = handler(self.js_core_schema or self.core_schema) 1ABabcdopqrefCDghijstuvEFklmnwxyz
145 if self.js_schema_update is not None: 1ABabcdopqrefCDghijstuvEFklmnwxyz
146 js_schema.update(self.js_schema_update) 1ABabcdopqrefCDghijstuvEFklmnwxyz
147 return js_schema 1ABabcdopqrefCDghijstuvEFklmnwxyz
149 def __get_pydantic_core_schema__(self, _source_type: Any, _handler: GetCoreSchemaHandler) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
150 return self.core_schema 1ABabcdopqrefCDghijstuvEFklmnwxyz
153def decimal_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
154 source: Any, annotations: Iterable[Any], config: ConfigDict
155) -> tuple[Any, list[Any]] | None:
156 if source is not decimal.Decimal: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
157 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
159 metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) 1ABabcdopqrefCDghijstuvEFklmnwxyz
161 config_allow_inf_nan = config.get('allow_inf_nan') 1ABabcdopqrefCDghijstuvEFklmnwxyz
162 if config_allow_inf_nan is not None: 1ABabcdopqrefCDghijstuvEFklmnwxyz
163 metadata.setdefault('allow_inf_nan', config_allow_inf_nan) 1ABabcdopqrefCDghijstuvEFklmnwxyz
165 _known_annotated_metadata.check_metadata( 1ABabcdopqrefCDghijstuvEFklmnwxyz
166 metadata, {*_known_annotated_metadata.FLOAT_CONSTRAINTS, 'max_digits', 'decimal_places'}, decimal.Decimal
167 )
168 return source, [InnerSchemaValidator(core_schema.decimal_schema(**metadata)), *remaining_annotations] 1ABabcdopqrefCDghijstuvEFklmnwxyz
171def datetime_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
172 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
173) -> tuple[Any, list[Any]] | None:
174 import datetime 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
176 metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
177 if source_type is datetime.date: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
178 sv = InnerSchemaValidator(core_schema.date_schema(**metadata)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
179 elif source_type is datetime.datetime: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
180 sv = InnerSchemaValidator(core_schema.datetime_schema(**metadata)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
181 elif source_type is datetime.time: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
182 sv = InnerSchemaValidator(core_schema.time_schema(**metadata)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
183 elif source_type is datetime.timedelta: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
184 sv = InnerSchemaValidator(core_schema.timedelta_schema(**metadata)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
185 else:
186 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
187 # check now that we know the source type is correct
188 _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.DATE_TIME_CONSTRAINTS, source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
189 return (source_type, [sv, *remaining_annotations]) 1ABabcdopqrefCDghijstuvEFklmnwxyz
192def uuid_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
193 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
194) -> tuple[Any, list[Any]] | None:
195 # UUIDs have no constraints - they are fixed length, constructing a UUID instance checks the length
197 from uuid import UUID 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
199 if source_type is not UUID: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
200 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
202 return (source_type, [InnerSchemaValidator(core_schema.uuid_schema()), *annotations]) 1ABabcdopqrefCDghijstuvEFklmnwxyz
205def path_schema_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
206 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
207) -> tuple[Any, list[Any]] | None:
208 import pathlib 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
210 if source_type not in { 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
211 os.PathLike,
212 pathlib.Path,
213 pathlib.PurePath,
214 pathlib.PosixPath,
215 pathlib.PurePosixPath,
216 pathlib.PureWindowsPath,
217 }:
218 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
220 metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) 1ABabcdopqrefCDghijstuvEFklmnwxyz
221 _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.STR_CONSTRAINTS, source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
223 construct_path = pathlib.PurePath if source_type is os.PathLike else source_type 1ABabcdopqrefCDghijstuvEFklmnwxyz
225 def path_validator(input_value: str) -> os.PathLike[Any]: 1ABabcdopqrefCDghijstuvEFklmnwxyz
226 try: 1ABabcdopqrefCDghijstuvEFklmnwxyz
227 return construct_path(input_value) 1ABabcdopqrefCDghijstuvEFklmnwxyz
228 except TypeError as e:
229 raise PydanticCustomError('path_type', 'Input is not a valid path') from e
231 constrained_str_schema = core_schema.str_schema(**metadata) 1ABabcdopqrefCDghijstuvEFklmnwxyz
233 instance_schema = core_schema.json_or_python_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
234 json_schema=core_schema.no_info_after_validator_function(path_validator, constrained_str_schema),
235 python_schema=core_schema.is_instance_schema(source_type),
236 )
238 strict: bool | None = None 1ABabcdopqrefCDghijstuvEFklmnwxyz
239 for annotation in annotations: 1ABabcdopqrefCDghijstuvEFklmnwxyz
240 if isinstance(annotation, Strict): 1ABabcdopqrefCDghijstuvEFklmnwxyz
241 strict = annotation.strict 1ABabcdopqrefCDghijstuvEFklmnwxyz
243 schema = core_schema.lax_or_strict_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
244 lax_schema=core_schema.union_schema(
245 [
246 instance_schema,
247 core_schema.no_info_after_validator_function(path_validator, constrained_str_schema),
248 ],
249 custom_error_type='path_type',
250 custom_error_message='Input is not a valid path',
251 strict=True,
252 ),
253 strict_schema=instance_schema,
254 serialization=core_schema.to_string_ser_schema(),
255 strict=strict,
256 )
258 return ( 1ABabcdopqrefCDghijstuvEFklmnwxyz
259 source_type,
260 [
261 InnerSchemaValidator(schema, js_core_schema=constrained_str_schema, js_schema_update={'format': 'path'}),
262 *remaining_annotations,
263 ],
264 )
267def dequeue_validator( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
268 input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, maxlen: None | int
269) -> collections.deque[Any]:
270 if isinstance(input_value, collections.deque): 1ABabcdopqrefCDghijstuvEFklmnwxyz
271 maxlens = [v for v in (input_value.maxlen, maxlen) if v is not None] 1ABabcdopqrefCDghijstuvEFklmnwxyz
272 if maxlens: 1ABabcdopqrefCDghijstuvEFklmnwxyz
273 maxlen = min(maxlens) 1ABabcdopqrefCDghijstuvEFklmnwxyz
274 return collections.deque(handler(input_value), maxlen=maxlen) 1ABabcdopqrefCDghijstuvEFklmnwxyz
275 else:
276 return collections.deque(handler(input_value), maxlen=maxlen) 1ABabcdopqrefCDghijstuvEFklmnwxyz
279def serialize_sequence_via_list( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
280 v: Any, handler: core_schema.SerializerFunctionWrapHandler, info: core_schema.SerializationInfo
281) -> Any:
282 items: list[Any] = [] 1ABabcdopqrefCDghijstuvEFklmnwxyz
284 mapped_origin = SEQUENCE_ORIGIN_MAP.get(type(v), None) 1ABabcdopqrefCDghijstuvEFklmnwxyz
285 if mapped_origin is None: 1ABabcdopqrefCDghijstuvEFklmnwxyz
286 # we shouldn't hit this branch, should probably add a serialization error or something
287 return v 1ABabcdopqrefCDghijstuvEFklmnwxyz
289 for index, item in enumerate(v): 1ABabcdopqrefCDghijstuvEFklmnwxyz
290 try: 1ABabcdopqrefCDghijstuvEFklmnwxyz
291 v = handler(item, index) 1ABabcdopqrefCDghijstuvEFklmnwxyz
292 except PydanticOmit:
293 pass
294 else:
295 items.append(v) 1ABabcdopqrefCDghijstuvEFklmnwxyz
297 if info.mode_is_json(): 1ABabcdopqrefCDghijstuvEFklmnwxyz
298 return items 1ABabcdopqrefCDghijstuvEFklmnwxyz
299 else:
300 return mapped_origin(items) 1ABabcdopqrefCDghijstuvEFklmnwxyz
303@dataclasses.dataclass(**slots_true) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
304class SequenceValidator: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
305 mapped_origin: type[Any] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
306 item_source_type: type[Any] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
307 min_length: int | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
308 max_length: int | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
309 strict: bool | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
311 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
312 if self.item_source_type is Any: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
313 items_schema = None 1ABabcdopqrefCDghijstuvEFklmnwxyz
314 else:
315 items_schema = handler.generate_schema(self.item_source_type) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
317 metadata = {'min_length': self.min_length, 'max_length': self.max_length, 'strict': self.strict} 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
319 if self.mapped_origin in (list, set, frozenset): 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
320 if self.mapped_origin is list: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
321 constrained_schema = core_schema.list_schema(items_schema, **metadata) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
322 elif self.mapped_origin is set: 1ABabcdopqrefCDghijstuvEFklmnwxyz
323 constrained_schema = core_schema.set_schema(items_schema, **metadata) 1ABabcdopqrefCDghijstuvEFklmnwxyz
324 else:
325 assert self.mapped_origin is frozenset # safety check in case we forget to add a case 1ABabcdopqrefCDghijstuvEFklmnwxyz
326 constrained_schema = core_schema.frozenset_schema(items_schema, **metadata) 1ABabcdopqrefCDghijstuvEFklmnwxyz
328 schema = constrained_schema 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
329 else:
330 # safety check in case we forget to add a case
331 assert self.mapped_origin in (collections.deque, collections.Counter) 1ABabcdopqrefCDghijstuvEFklmnwxyz
333 if self.mapped_origin is collections.deque: 333 ↛ 342line 333 didn't jump to line 342, because the condition on line 333 was always true1ABabcdopqrefCDghijstuvEFklmnwxyz
334 # if we have a MaxLen annotation might as well set that as the default maxlen on the deque
335 # this lets us re-use existing metadata annotations to let users set the maxlen on a dequeue
336 # that e.g. comes from JSON
337 coerce_instance_wrap = partial( 1ABabcdopqrefCDghijstuvEFklmnwxyz
338 core_schema.no_info_wrap_validator_function,
339 partial(dequeue_validator, maxlen=metadata.get('max_length', None)),
340 )
341 else:
342 coerce_instance_wrap = partial(core_schema.no_info_after_validator_function, self.mapped_origin)
344 # we have to use a lax list schema here, because we need to validate the deque's
345 # items via a list schema, but it's ok if the deque itself is not a list (same for Counter)
346 metadata_with_strict_override = {**metadata, 'strict': False} 1ABabcdopqrefCDghijstuvEFklmnwxyz
347 constrained_schema = core_schema.list_schema(items_schema, **metadata_with_strict_override) 1ABabcdopqrefCDghijstuvEFklmnwxyz
349 check_instance = core_schema.json_or_python_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
350 json_schema=core_schema.list_schema(),
351 python_schema=core_schema.is_instance_schema(self.mapped_origin),
352 )
354 serialization = core_schema.wrap_serializer_function_ser_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
355 serialize_sequence_via_list, schema=items_schema or core_schema.any_schema(), info_arg=True
356 )
358 strict = core_schema.chain_schema([check_instance, coerce_instance_wrap(constrained_schema)]) 1ABabcdopqrefCDghijstuvEFklmnwxyz
360 if metadata.get('strict', False): 360 ↛ 361line 360 didn't jump to line 361, because the condition on line 360 was never true1ABabcdopqrefCDghijstuvEFklmnwxyz
361 schema = strict
362 else:
363 lax = coerce_instance_wrap(constrained_schema) 1ABabcdopqrefCDghijstuvEFklmnwxyz
364 schema = core_schema.lax_or_strict_schema(lax_schema=lax, strict_schema=strict) 1ABabcdopqrefCDghijstuvEFklmnwxyz
365 schema['serialization'] = serialization 1ABabcdopqrefCDghijstuvEFklmnwxyz
367 return schema 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
370SEQUENCE_ORIGIN_MAP: dict[Any, Any] = { 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
371 typing.Deque: collections.deque,
372 collections.deque: collections.deque,
373 list: list,
374 typing.List: list,
375 set: set,
376 typing.AbstractSet: set,
377 typing.Set: set,
378 frozenset: frozenset,
379 typing.FrozenSet: frozenset,
380 typing.Sequence: list,
381 typing.MutableSequence: list,
382 typing.MutableSet: set,
383 # this doesn't handle subclasses of these
384 # parametrized typing.Set creates one of these
385 collections.abc.MutableSet: set,
386 collections.abc.Set: frozenset,
387}
390def identity(s: CoreSchema) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
391 return s
394def sequence_like_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
395 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
396) -> tuple[Any, list[Any]] | None:
397 origin: Any = get_origin(source_type) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
399 mapped_origin = SEQUENCE_ORIGIN_MAP.get(origin, None) if origin else SEQUENCE_ORIGIN_MAP.get(source_type, None) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
400 if mapped_origin is None: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
401 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
403 args = get_args(source_type) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
405 if not args: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
406 args = typing.cast(Tuple[Any], (Any,)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
407 elif len(args) != 1: 407 ↛ 408line 407 didn't jump to line 408, because the condition on line 407 was never true1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
408 raise ValueError('Expected sequence to have exactly 1 generic parameter')
410 item_source_type = args[0] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
412 metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
413 _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.SEQUENCE_CONSTRAINTS, source_type) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
415 return (source_type, [SequenceValidator(mapped_origin, item_source_type, **metadata), *remaining_annotations]) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
418MAPPING_ORIGIN_MAP: dict[Any, Any] = { 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
419 typing.DefaultDict: collections.defaultdict,
420 collections.defaultdict: collections.defaultdict,
421 collections.OrderedDict: collections.OrderedDict,
422 typing_extensions.OrderedDict: collections.OrderedDict,
423 dict: dict,
424 typing.Dict: dict,
425 collections.Counter: collections.Counter,
426 typing.Counter: collections.Counter,
427 # this doesn't handle subclasses of these
428 typing.Mapping: dict,
429 typing.MutableMapping: dict,
430 # parametrized typing.{Mutable}Mapping creates one of these
431 collections.abc.MutableMapping: dict,
432 collections.abc.Mapping: dict,
433}
436def defaultdict_validator( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
437 input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, default_default_factory: Callable[[], Any]
438) -> collections.defaultdict[Any, Any]:
439 if isinstance(input_value, collections.defaultdict): 1ABabcdopqrefCDghijstuvEFklmnwxyz
440 default_factory = input_value.default_factory 1ABabcdopqrefCDghijstuvEFklmnwxyz
441 return collections.defaultdict(default_factory, handler(input_value)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
442 else:
443 return collections.defaultdict(default_default_factory, handler(input_value)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
446def get_defaultdict_default_default_factory(values_source_type: Any) -> Callable[[], Any]: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
447 def infer_default() -> Callable[[], Any]: 1ABabcdopqrefCDghijstuvEFklmnwxyz
448 allowed_default_types: dict[Any, Any] = { 1ABabcdopqrefCDghijstuvEFklmnwxyz
449 typing.Tuple: tuple,
450 tuple: tuple,
451 collections.abc.Sequence: tuple,
452 collections.abc.MutableSequence: list,
453 typing.List: list,
454 list: list,
455 typing.Sequence: list,
456 typing.Set: set,
457 set: set,
458 typing.MutableSet: set,
459 collections.abc.MutableSet: set,
460 collections.abc.Set: frozenset,
461 typing.MutableMapping: dict,
462 typing.Mapping: dict,
463 collections.abc.Mapping: dict,
464 collections.abc.MutableMapping: dict,
465 float: float,
466 int: int,
467 str: str,
468 bool: bool,
469 }
470 values_type_origin = get_origin(values_source_type) or values_source_type 1ABabcdopqrefCDghijstuvEFklmnwxyz
471 instructions = 'set using `DefaultDict[..., Annotated[..., Field(default_factory=...)]]`' 1ABabcdopqrefCDghijstuvEFklmnwxyz
472 if isinstance(values_type_origin, TypeVar): 1ABabcdopqrefCDghijstuvEFklmnwxyz
474 def type_var_default_factory() -> None: 1ABabcdopqrefCDghijstuvEFklmnwxyz
475 raise RuntimeError(
476 'Generic defaultdict cannot be used without a concrete value type or an'
477 ' explicit default factory, ' + instructions
478 )
480 return type_var_default_factory 1ABabcdopqrefCDghijstuvEFklmnwxyz
481 elif values_type_origin not in allowed_default_types: 1ABabcdopqrefCDghijstuvEFklmnwxyz
482 # a somewhat subjective set of types that have reasonable default values
483 allowed_msg = ', '.join([t.__name__ for t in set(allowed_default_types.values())]) 1ABabcdopqrefCDghijstuvEFklmnwxyz
484 raise PydanticSchemaGenerationError( 1ABabcdopqrefCDghijstuvEFklmnwxyz
485 f'Unable to infer a default factory for keys of type {values_source_type}.'
486 f' Only {allowed_msg} are supported, other types require an explicit default factory'
487 ' ' + instructions
488 )
489 return allowed_default_types[values_type_origin] 1ABabcdopqrefCDghijstuvEFklmnwxyz
491 # Assume Annotated[..., Field(...)]
492 if _typing_extra.is_annotated(values_source_type): 1ABabcdopqrefCDghijstuvEFklmnwxyz
493 field_info = next((v for v in get_args(values_source_type) if isinstance(v, FieldInfo)), None) 493 ↛ exitline 493 didn't finish the generator expression on line 4931ABabcdopqrefCDghijstuvEFklmnwxyz
494 else:
495 field_info = None 1ABabcdopqrefCDghijstuvEFklmnwxyz
496 if field_info and field_info.default_factory: 1ABabcdopqrefCDghijstuvEFklmnwxyz
497 default_default_factory = field_info.default_factory 1ABabcdopqrefCDghijstuvEFklmnwxyz
498 else:
499 default_default_factory = infer_default() 1ABabcdopqrefCDghijstuvEFklmnwxyz
500 return default_default_factory 1ABabcdopqrefCDghijstuvEFklmnwxyz
503@dataclasses.dataclass(**slots_true) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
504class MappingValidator: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
505 mapped_origin: type[Any] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
506 keys_source_type: type[Any] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
507 values_source_type: type[Any] 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
508 min_length: int | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
509 max_length: int | None = None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
510 strict: bool = False 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
512 def serialize_mapping_via_dict(self, v: Any, handler: core_schema.SerializerFunctionWrapHandler) -> Any: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
513 return handler(v) 1ABabcdopqrefCDghijstuvEFklmnwxyz
515 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
516 if self.keys_source_type is Any: 1ABabcdopqrefCDghijstuvEFklmnwxyz
517 keys_schema = None 1ABabcdopqrefCDghijstuvEFklmnwxyz
518 else:
519 keys_schema = handler.generate_schema(self.keys_source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
520 if self.values_source_type is Any: 1ABabcdopqrefCDghijstuvEFklmnwxyz
521 values_schema = None 1ABabcdopqrefCDghijstuvEFklmnwxyz
522 else:
523 values_schema = handler.generate_schema(self.values_source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
525 metadata = {'min_length': self.min_length, 'max_length': self.max_length, 'strict': self.strict} 1ABabcdopqrefCDghijstuvEFklmnwxyz
527 if self.mapped_origin is dict: 1ABabcdopqrefCDghijstuvEFklmnwxyz
528 schema = core_schema.dict_schema(keys_schema, values_schema, **metadata) 1ABabcdopqrefCDghijstuvEFklmnwxyz
529 else:
530 constrained_schema = core_schema.dict_schema(keys_schema, values_schema, **metadata) 1ABabcdopqrefCDghijstuvEFklmnwxyz
531 check_instance = core_schema.json_or_python_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
532 json_schema=core_schema.dict_schema(),
533 python_schema=core_schema.is_instance_schema(self.mapped_origin),
534 )
536 if self.mapped_origin is collections.defaultdict: 1ABabcdopqrefCDghijstuvEFklmnwxyz
537 default_default_factory = get_defaultdict_default_default_factory(self.values_source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
538 coerce_instance_wrap = partial( 1ABabcdopqrefCDghijstuvEFklmnwxyz
539 core_schema.no_info_wrap_validator_function,
540 partial(defaultdict_validator, default_default_factory=default_default_factory),
541 )
542 else:
543 coerce_instance_wrap = partial(core_schema.no_info_after_validator_function, self.mapped_origin) 1ABabcdopqrefCDghijstuvEFklmnwxyz
545 serialization = core_schema.wrap_serializer_function_ser_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
546 self.serialize_mapping_via_dict,
547 schema=core_schema.dict_schema(
548 keys_schema or core_schema.any_schema(), values_schema or core_schema.any_schema()
549 ),
550 info_arg=False,
551 )
553 strict = core_schema.chain_schema([check_instance, coerce_instance_wrap(constrained_schema)]) 1ABabcdopqrefCDghijstuvEFklmnwxyz
555 if metadata.get('strict', False): 555 ↛ 556line 555 didn't jump to line 556, because the condition on line 555 was never true1ABabcdopqrefCDghijstuvEFklmnwxyz
556 schema = strict
557 else:
558 lax = coerce_instance_wrap(constrained_schema) 1ABabcdopqrefCDghijstuvEFklmnwxyz
559 schema = core_schema.lax_or_strict_schema(lax_schema=lax, strict_schema=strict) 1ABabcdopqrefCDghijstuvEFklmnwxyz
560 schema['serialization'] = serialization 1ABabcdopqrefCDghijstuvEFklmnwxyz
562 return schema 1ABabcdopqrefCDghijstuvEFklmnwxyz
565def mapping_like_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
566 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
567) -> tuple[Any, list[Any]] | None:
568 origin: Any = get_origin(source_type) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
570 mapped_origin = MAPPING_ORIGIN_MAP.get(origin, None) if origin else MAPPING_ORIGIN_MAP.get(source_type, None) 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
571 if mapped_origin is None: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
572 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
574 args = get_args(source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
576 if not args: 1ABabcdopqrefCDghijstuvEFklmnwxyz
577 args = typing.cast(Tuple[Any, Any], (Any, Any)) 1ABabcdopqrefCDghijstuvEFklmnwxyz
578 elif mapped_origin is collections.Counter: 1ABabcdopqrefCDghijstuvEFklmnwxyz
579 # a single generic
580 if len(args) != 1: 580 ↛ 581line 580 didn't jump to line 581, because the condition on line 580 was never true1ABabcdopqrefCDghijstuvEFklmnwxyz
581 raise ValueError('Expected Counter to have exactly 1 generic parameter')
582 args = (args[0], int) # keys are always an int 1ABabcdopqrefCDghijstuvEFklmnwxyz
583 elif len(args) != 2: 583 ↛ 584line 583 didn't jump to line 584, because the condition on line 583 was never true1ABabcdopqrefCDghijstuvEFklmnwxyz
584 raise ValueError('Expected mapping to have exactly 2 generic parameters')
586 keys_source_type, values_source_type = args 1ABabcdopqrefCDghijstuvEFklmnwxyz
588 metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) 1ABabcdopqrefCDghijstuvEFklmnwxyz
589 _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.SEQUENCE_CONSTRAINTS, source_type) 1ABabcdopqrefCDghijstuvEFklmnwxyz
591 return ( 1ABabcdopqrefCDghijstuvEFklmnwxyz
592 source_type,
593 [
594 MappingValidator(mapped_origin, keys_source_type, values_source_type, **metadata),
595 *remaining_annotations,
596 ],
597 )
600def ip_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
601 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
602) -> tuple[Any, list[Any]] | None:
603 def make_strict_ip_schema(tp: type[Any]) -> CoreSchema: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
604 return core_schema.json_or_python_schema( 1ABabcdopqrefCDghijstuvEFklmnwxyz
605 json_schema=core_schema.no_info_after_validator_function(tp, core_schema.str_schema()),
606 python_schema=core_schema.is_instance_schema(tp),
607 )
609 if source_type is IPv4Address: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
610 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
611 SchemaTransformer(
612 lambda _1, _2: core_schema.lax_or_strict_schema(
613 lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v4_address_validator),
614 strict_schema=make_strict_ip_schema(IPv4Address),
615 serialization=core_schema.to_string_ser_schema(),
616 ),
617 lambda _1, _2: {'type': 'string', 'format': 'ipv4'},
618 ),
619 *annotations,
620 ]
621 if source_type is IPv4Network: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
622 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
623 SchemaTransformer(
624 lambda _1, _2: core_schema.lax_or_strict_schema(
625 lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v4_network_validator),
626 strict_schema=make_strict_ip_schema(IPv4Network),
627 serialization=core_schema.to_string_ser_schema(),
628 ),
629 lambda _1, _2: {'type': 'string', 'format': 'ipv4network'},
630 ),
631 *annotations,
632 ]
633 if source_type is IPv4Interface: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
634 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
635 SchemaTransformer(
636 lambda _1, _2: core_schema.lax_or_strict_schema(
637 lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v4_interface_validator),
638 strict_schema=make_strict_ip_schema(IPv4Interface),
639 serialization=core_schema.to_string_ser_schema(),
640 ),
641 lambda _1, _2: {'type': 'string', 'format': 'ipv4interface'},
642 ),
643 *annotations,
644 ]
646 if source_type is IPv6Address: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
647 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
648 SchemaTransformer(
649 lambda _1, _2: core_schema.lax_or_strict_schema(
650 lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v6_address_validator),
651 strict_schema=make_strict_ip_schema(IPv6Address),
652 serialization=core_schema.to_string_ser_schema(),
653 ),
654 lambda _1, _2: {'type': 'string', 'format': 'ipv6'},
655 ),
656 *annotations,
657 ]
658 if source_type is IPv6Network: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
659 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
660 SchemaTransformer(
661 lambda _1, _2: core_schema.lax_or_strict_schema(
662 lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v6_network_validator),
663 strict_schema=make_strict_ip_schema(IPv6Network),
664 serialization=core_schema.to_string_ser_schema(),
665 ),
666 lambda _1, _2: {'type': 'string', 'format': 'ipv6network'},
667 ),
668 *annotations,
669 ]
670 if source_type is IPv6Interface: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
671 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
672 SchemaTransformer(
673 lambda _1, _2: core_schema.lax_or_strict_schema(
674 lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v6_interface_validator),
675 strict_schema=make_strict_ip_schema(IPv6Interface),
676 serialization=core_schema.to_string_ser_schema(),
677 ),
678 lambda _1, _2: {'type': 'string', 'format': 'ipv6interface'},
679 ),
680 *annotations,
681 ]
683 return None 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
686def url_prepare_pydantic_annotations( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
687 source_type: Any, annotations: Iterable[Any], _config: ConfigDict
688) -> tuple[Any, list[Any]] | None:
689 if source_type is Url: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
690 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
691 SchemaTransformer(
692 lambda _1, _2: core_schema.url_schema(),
693 lambda cs, handler: handler(cs),
694 ),
695 *annotations,
696 ]
697 if source_type is MultiHostUrl: 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
698 return source_type, [ 1ABabcdopqrefCDghijstuvEFklmnwxyz
699 SchemaTransformer(
700 lambda _1, _2: core_schema.multi_host_url_schema(),
701 lambda cs, handler: handler(cs),
702 ),
703 *annotations,
704 ]
707PREPARE_METHODS: tuple[Callable[[Any, Iterable[Any], ConfigDict], tuple[Any, list[Any]] | None], ...] = ( 1ABabcdopqrefCDghijstuvGHIJKLMNOEFklmnwxyz
708 decimal_prepare_pydantic_annotations,
709 sequence_like_prepare_pydantic_annotations,
710 datetime_prepare_pydantic_annotations,
711 uuid_prepare_pydantic_annotations,
712 path_schema_prepare_pydantic_annotations,
713 mapping_like_prepare_pydantic_annotations,
714 ip_prepare_pydantic_annotations,
715 url_prepare_pydantic_annotations,
716)