Coverage for pydantic/types.py: 97.48%
653 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-02 11:05 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-02 11:05 +0000
1"""The types module contains custom types used by pydantic."""
3from __future__ import annotations as _annotations 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
5import base64 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
6import dataclasses as _dataclasses 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
7import re 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
8from collections.abc import Hashable, Iterator 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
9from datetime import date, datetime 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
10from decimal import Decimal 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
11from enum import Enum 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
12from pathlib import Path 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
13from re import Pattern 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
14from types import ModuleType 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
15from typing import ( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
16 TYPE_CHECKING,
17 Annotated,
18 Any,
19 Callable,
20 ClassVar,
21 Generic,
22 Literal,
23 TypeVar,
24 Union,
25 cast,
26)
27from uuid import UUID 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
29import annotated_types 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
30from annotated_types import BaseMetadata, MaxLen, MinLen 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
31from pydantic_core import CoreSchema, PydanticCustomError, SchemaSerializer, core_schema 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
32from typing_extensions import Protocol, TypeAlias, TypeAliasType, deprecated, get_args, get_origin 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
33from typing_inspection.introspection import is_union_origin 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
35from ._internal import _fields, _internal_dataclass, _utils, _validators 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
36from ._migration import getattr_migration 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
37from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
38from .errors import PydanticUserError 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
39from .json_schema import JsonSchemaValue 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
40from .warnings import PydanticDeprecatedSince20 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
42if TYPE_CHECKING: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
43 from ._internal._core_metadata import CoreMetadata
45__all__ = ( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
46 'Strict',
47 'StrictStr',
48 'SocketPath',
49 'conbytes',
50 'conlist',
51 'conset',
52 'confrozenset',
53 'constr',
54 'ImportString',
55 'conint',
56 'PositiveInt',
57 'NegativeInt',
58 'NonNegativeInt',
59 'NonPositiveInt',
60 'confloat',
61 'PositiveFloat',
62 'NegativeFloat',
63 'NonNegativeFloat',
64 'NonPositiveFloat',
65 'FiniteFloat',
66 'condecimal',
67 'UUID1',
68 'UUID3',
69 'UUID4',
70 'UUID5',
71 'UUID6',
72 'UUID7',
73 'UUID8',
74 'FilePath',
75 'DirectoryPath',
76 'NewPath',
77 'Json',
78 'Secret',
79 'SecretStr',
80 'SecretBytes',
81 'StrictBool',
82 'StrictBytes',
83 'StrictInt',
84 'StrictFloat',
85 'PaymentCardNumber',
86 'ByteSize',
87 'PastDate',
88 'FutureDate',
89 'PastDatetime',
90 'FutureDatetime',
91 'condate',
92 'AwareDatetime',
93 'NaiveDatetime',
94 'AllowInfNan',
95 'EncoderProtocol',
96 'EncodedBytes',
97 'EncodedStr',
98 'Base64Encoder',
99 'Base64Bytes',
100 'Base64Str',
101 'Base64UrlBytes',
102 'Base64UrlStr',
103 'GetPydanticSchema',
104 'StringConstraints',
105 'Tag',
106 'Discriminator',
107 'JsonValue',
108 'OnErrorOmit',
109 'FailFast',
110)
113T = TypeVar('T') 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
116@_dataclasses.dataclass 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
117class Strict(_fields.PydanticMetadata, BaseMetadata): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
118 """!!! abstract "Usage Documentation"
119 [Strict Mode with `Annotated` `Strict`](../concepts/strict_mode.md#strict-mode-with-annotated-strict)
121 A field metadata class to indicate that a field should be validated in strict mode.
122 Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
124 Attributes:
125 strict: Whether to validate the field in strict mode.
127 Example:
128 ```python
129 from typing import Annotated
131 from pydantic.types import Strict
133 StrictBool = Annotated[bool, Strict()]
134 ```
135 """
137 strict: bool = True 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
139 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
140 return hash(self.strict) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
143# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
145StrictBool = Annotated[bool, Strict()] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
146"""A boolean that must be either ``True`` or ``False``.""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
148# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
151def conint( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
152 *,
153 strict: bool | None = None,
154 gt: int | None = None,
155 ge: int | None = None,
156 lt: int | None = None,
157 le: int | None = None,
158 multiple_of: int | None = None,
159) -> type[int]:
160 """
161 !!! warning "Discouraged"
162 This function is **discouraged** in favor of using
163 [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
164 [`Field`][pydantic.fields.Field] instead.
166 This function will be **deprecated** in Pydantic 3.0.
168 The reason is that `conint` returns a type, which doesn't play well with static analysis tools.
170 === ":x: Don't do this"
171 ```python
172 from pydantic import BaseModel, conint
174 class Foo(BaseModel):
175 bar: conint(strict=True, gt=0)
176 ```
178 === ":white_check_mark: Do this"
179 ```python
180 from typing import Annotated
182 from pydantic import BaseModel, Field
184 class Foo(BaseModel):
185 bar: Annotated[int, Field(strict=True, gt=0)]
186 ```
188 A wrapper around `int` that allows for additional constraints.
190 Args:
191 strict: Whether to validate the integer in strict mode. Defaults to `None`.
192 gt: The value must be greater than this.
193 ge: The value must be greater than or equal to this.
194 lt: The value must be less than this.
195 le: The value must be less than or equal to this.
196 multiple_of: The value must be a multiple of this.
198 Returns:
199 The wrapped integer type.
201 ```python
202 from pydantic import BaseModel, ValidationError, conint
204 class ConstrainedExample(BaseModel):
205 constrained_int: conint(gt=1)
207 m = ConstrainedExample(constrained_int=2)
208 print(repr(m))
209 #> ConstrainedExample(constrained_int=2)
211 try:
212 ConstrainedExample(constrained_int=0)
213 except ValidationError as e:
214 print(e.errors())
215 '''
216 [
217 {
218 'type': 'greater_than',
219 'loc': ('constrained_int',),
220 'msg': 'Input should be greater than 1',
221 'input': 0,
222 'ctx': {'gt': 1},
223 'url': 'https://errors.pydantic.dev/2/v/greater_than',
224 }
225 ]
226 '''
227 ```
229 """ # noqa: D212
230 return Annotated[ # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
231 int,
232 Strict(strict) if strict is not None else None,
233 annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
234 annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None,
235 ]
238PositiveInt = Annotated[int, annotated_types.Gt(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
239"""An integer that must be greater than zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
241```python
242from pydantic import BaseModel, PositiveInt, ValidationError
244class Model(BaseModel):
245 positive_int: PositiveInt
247m = Model(positive_int=1)
248print(repr(m))
249#> Model(positive_int=1)
251try:
252 Model(positive_int=-1)
253except ValidationError as e:
254 print(e.errors())
255 '''
256 [
257 {
258 'type': 'greater_than',
259 'loc': ('positive_int',),
260 'msg': 'Input should be greater than 0',
261 'input': -1,
262 'ctx': {'gt': 0},
263 'url': 'https://errors.pydantic.dev/2/v/greater_than',
264 }
265 ]
266 '''
267```
268"""
269NegativeInt = Annotated[int, annotated_types.Lt(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
270"""An integer that must be less than zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
272```python
273from pydantic import BaseModel, NegativeInt, ValidationError
275class Model(BaseModel):
276 negative_int: NegativeInt
278m = Model(negative_int=-1)
279print(repr(m))
280#> Model(negative_int=-1)
282try:
283 Model(negative_int=1)
284except ValidationError as e:
285 print(e.errors())
286 '''
287 [
288 {
289 'type': 'less_than',
290 'loc': ('negative_int',),
291 'msg': 'Input should be less than 0',
292 'input': 1,
293 'ctx': {'lt': 0},
294 'url': 'https://errors.pydantic.dev/2/v/less_than',
295 }
296 ]
297 '''
298```
299"""
300NonPositiveInt = Annotated[int, annotated_types.Le(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
301"""An integer that must be less than or equal to zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
303```python
304from pydantic import BaseModel, NonPositiveInt, ValidationError
306class Model(BaseModel):
307 non_positive_int: NonPositiveInt
309m = Model(non_positive_int=0)
310print(repr(m))
311#> Model(non_positive_int=0)
313try:
314 Model(non_positive_int=1)
315except ValidationError as e:
316 print(e.errors())
317 '''
318 [
319 {
320 'type': 'less_than_equal',
321 'loc': ('non_positive_int',),
322 'msg': 'Input should be less than or equal to 0',
323 'input': 1,
324 'ctx': {'le': 0},
325 'url': 'https://errors.pydantic.dev/2/v/less_than_equal',
326 }
327 ]
328 '''
329```
330"""
331NonNegativeInt = Annotated[int, annotated_types.Ge(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
332"""An integer that must be greater than or equal to zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
334```python
335from pydantic import BaseModel, NonNegativeInt, ValidationError
337class Model(BaseModel):
338 non_negative_int: NonNegativeInt
340m = Model(non_negative_int=0)
341print(repr(m))
342#> Model(non_negative_int=0)
344try:
345 Model(non_negative_int=-1)
346except ValidationError as e:
347 print(e.errors())
348 '''
349 [
350 {
351 'type': 'greater_than_equal',
352 'loc': ('non_negative_int',),
353 'msg': 'Input should be greater than or equal to 0',
354 'input': -1,
355 'ctx': {'ge': 0},
356 'url': 'https://errors.pydantic.dev/2/v/greater_than_equal',
357 }
358 ]
359 '''
360```
361"""
362StrictInt = Annotated[int, Strict()] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
363"""An integer that must be validated in strict mode. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
365```python
366from pydantic import BaseModel, StrictInt, ValidationError
368class StrictIntModel(BaseModel):
369 strict_int: StrictInt
371try:
372 StrictIntModel(strict_int=3.14159)
373except ValidationError as e:
374 print(e)
375 '''
376 1 validation error for StrictIntModel
377 strict_int
378 Input should be a valid integer [type=int_type, input_value=3.14159, input_type=float]
379 '''
380```
381"""
383# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
386@_dataclasses.dataclass 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
387class AllowInfNan(_fields.PydanticMetadata): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
388 """A field metadata class to indicate that a field should allow `-inf`, `inf`, and `nan`.
390 Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
392 Attributes:
393 allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`. Defaults to `True`.
395 Example:
396 ```python
397 from typing import Annotated
399 from pydantic.types import AllowInfNan
401 LaxFloat = Annotated[float, AllowInfNan()]
402 ```
403 """
405 allow_inf_nan: bool = True 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
407 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
408 return hash(self.allow_inf_nan) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
411def confloat( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
412 *,
413 strict: bool | None = None,
414 gt: float | None = None,
415 ge: float | None = None,
416 lt: float | None = None,
417 le: float | None = None,
418 multiple_of: float | None = None,
419 allow_inf_nan: bool | None = None,
420) -> type[float]:
421 """
422 !!! warning "Discouraged"
423 This function is **discouraged** in favor of using
424 [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
425 [`Field`][pydantic.fields.Field] instead.
427 This function will be **deprecated** in Pydantic 3.0.
429 The reason is that `confloat` returns a type, which doesn't play well with static analysis tools.
431 === ":x: Don't do this"
432 ```python
433 from pydantic import BaseModel, confloat
435 class Foo(BaseModel):
436 bar: confloat(strict=True, gt=0)
437 ```
439 === ":white_check_mark: Do this"
440 ```python
441 from typing import Annotated
443 from pydantic import BaseModel, Field
445 class Foo(BaseModel):
446 bar: Annotated[float, Field(strict=True, gt=0)]
447 ```
449 A wrapper around `float` that allows for additional constraints.
451 Args:
452 strict: Whether to validate the float in strict mode.
453 gt: The value must be greater than this.
454 ge: The value must be greater than or equal to this.
455 lt: The value must be less than this.
456 le: The value must be less than or equal to this.
457 multiple_of: The value must be a multiple of this.
458 allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`.
460 Returns:
461 The wrapped float type.
463 ```python
464 from pydantic import BaseModel, ValidationError, confloat
466 class ConstrainedExample(BaseModel):
467 constrained_float: confloat(gt=1.0)
469 m = ConstrainedExample(constrained_float=1.1)
470 print(repr(m))
471 #> ConstrainedExample(constrained_float=1.1)
473 try:
474 ConstrainedExample(constrained_float=0.9)
475 except ValidationError as e:
476 print(e.errors())
477 '''
478 [
479 {
480 'type': 'greater_than',
481 'loc': ('constrained_float',),
482 'msg': 'Input should be greater than 1',
483 'input': 0.9,
484 'ctx': {'gt': 1.0},
485 'url': 'https://errors.pydantic.dev/2/v/greater_than',
486 }
487 ]
488 '''
489 ```
490 """ # noqa: D212
491 return Annotated[ # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
492 float,
493 Strict(strict) if strict is not None else None,
494 annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
495 annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None,
496 AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None,
497 ]
500PositiveFloat = Annotated[float, annotated_types.Gt(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
501"""A float that must be greater than zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
503```python
504from pydantic import BaseModel, PositiveFloat, ValidationError
506class Model(BaseModel):
507 positive_float: PositiveFloat
509m = Model(positive_float=1.0)
510print(repr(m))
511#> Model(positive_float=1.0)
513try:
514 Model(positive_float=-1.0)
515except ValidationError as e:
516 print(e.errors())
517 '''
518 [
519 {
520 'type': 'greater_than',
521 'loc': ('positive_float',),
522 'msg': 'Input should be greater than 0',
523 'input': -1.0,
524 'ctx': {'gt': 0.0},
525 'url': 'https://errors.pydantic.dev/2/v/greater_than',
526 }
527 ]
528 '''
529```
530"""
531NegativeFloat = Annotated[float, annotated_types.Lt(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
532"""A float that must be less than zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
534```python
535from pydantic import BaseModel, NegativeFloat, ValidationError
537class Model(BaseModel):
538 negative_float: NegativeFloat
540m = Model(negative_float=-1.0)
541print(repr(m))
542#> Model(negative_float=-1.0)
544try:
545 Model(negative_float=1.0)
546except ValidationError as e:
547 print(e.errors())
548 '''
549 [
550 {
551 'type': 'less_than',
552 'loc': ('negative_float',),
553 'msg': 'Input should be less than 0',
554 'input': 1.0,
555 'ctx': {'lt': 0.0},
556 'url': 'https://errors.pydantic.dev/2/v/less_than',
557 }
558 ]
559 '''
560```
561"""
562NonPositiveFloat = Annotated[float, annotated_types.Le(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
563"""A float that must be less than or equal to zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
565```python
566from pydantic import BaseModel, NonPositiveFloat, ValidationError
568class Model(BaseModel):
569 non_positive_float: NonPositiveFloat
571m = Model(non_positive_float=0.0)
572print(repr(m))
573#> Model(non_positive_float=0.0)
575try:
576 Model(non_positive_float=1.0)
577except ValidationError as e:
578 print(e.errors())
579 '''
580 [
581 {
582 'type': 'less_than_equal',
583 'loc': ('non_positive_float',),
584 'msg': 'Input should be less than or equal to 0',
585 'input': 1.0,
586 'ctx': {'le': 0.0},
587 'url': 'https://errors.pydantic.dev/2/v/less_than_equal',
588 }
589 ]
590 '''
591```
592"""
593NonNegativeFloat = Annotated[float, annotated_types.Ge(0)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
594"""A float that must be greater than or equal to zero. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
596```python
597from pydantic import BaseModel, NonNegativeFloat, ValidationError
599class Model(BaseModel):
600 non_negative_float: NonNegativeFloat
602m = Model(non_negative_float=0.0)
603print(repr(m))
604#> Model(non_negative_float=0.0)
606try:
607 Model(non_negative_float=-1.0)
608except ValidationError as e:
609 print(e.errors())
610 '''
611 [
612 {
613 'type': 'greater_than_equal',
614 'loc': ('non_negative_float',),
615 'msg': 'Input should be greater than or equal to 0',
616 'input': -1.0,
617 'ctx': {'ge': 0.0},
618 'url': 'https://errors.pydantic.dev/2/v/greater_than_equal',
619 }
620 ]
621 '''
622```
623"""
624StrictFloat = Annotated[float, Strict(True)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
625"""A float that must be validated in strict mode. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
627```python
628from pydantic import BaseModel, StrictFloat, ValidationError
630class StrictFloatModel(BaseModel):
631 strict_float: StrictFloat
633try:
634 StrictFloatModel(strict_float='1.0')
635except ValidationError as e:
636 print(e)
637 '''
638 1 validation error for StrictFloatModel
639 strict_float
640 Input should be a valid number [type=float_type, input_value='1.0', input_type=str]
641 '''
642```
643"""
644FiniteFloat = Annotated[float, AllowInfNan(False)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
645"""A float that must be finite (not ``-inf``, ``inf``, or ``nan``). 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
647```python
648from pydantic import BaseModel, FiniteFloat
650class Model(BaseModel):
651 finite: FiniteFloat
653m = Model(finite=1.0)
654print(m)
655#> finite=1.0
656```
657"""
660# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
663def conbytes( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
664 *,
665 min_length: int | None = None,
666 max_length: int | None = None,
667 strict: bool | None = None,
668) -> type[bytes]:
669 """A wrapper around `bytes` that allows for additional constraints.
671 Args:
672 min_length: The minimum length of the bytes.
673 max_length: The maximum length of the bytes.
674 strict: Whether to validate the bytes in strict mode.
676 Returns:
677 The wrapped bytes type.
678 """
679 return Annotated[ # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
680 bytes,
681 Strict(strict) if strict is not None else None,
682 annotated_types.Len(min_length or 0, max_length),
683 ]
686StrictBytes = Annotated[bytes, Strict()] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
687"""A bytes that must be validated in strict mode.""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
690# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
693@_dataclasses.dataclass(frozen=True) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
694class StringConstraints(annotated_types.GroupedMetadata): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
695 """!!! abstract "Usage Documentation"
696 [`StringConstraints`](../concepts/fields.md#string-constraints)
698 A field metadata class to apply constraints to `str` types.
699 Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
701 Attributes:
702 strip_whitespace: Whether to remove leading and trailing whitespace.
703 to_upper: Whether to convert the string to uppercase.
704 to_lower: Whether to convert the string to lowercase.
705 strict: Whether to validate the string in strict mode.
706 min_length: The minimum length of the string.
707 max_length: The maximum length of the string.
708 pattern: A regex pattern that the string must match.
710 Example:
711 ```python
712 from typing import Annotated
714 from pydantic.types import StringConstraints
716 ConstrainedStr = Annotated[str, StringConstraints(min_length=1, max_length=10)]
717 ```
718 """
720 strip_whitespace: bool | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
721 to_upper: bool | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
722 to_lower: bool | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
723 strict: bool | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
724 min_length: int | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
725 max_length: int | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
726 pattern: str | Pattern[str] | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
728 def __iter__(self) -> Iterator[BaseMetadata]: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
729 if self.min_length is not None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
730 yield MinLen(self.min_length) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
731 if self.max_length is not None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
732 yield MaxLen(self.max_length) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
733 if self.strict is not None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
734 yield Strict(self.strict) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
735 if ( 1CDEa
736 self.strip_whitespace is not None
737 or self.pattern is not None
738 or self.to_lower is not None
739 or self.to_upper is not None
740 ):
741 yield _fields.pydantic_general_metadata( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
742 strip_whitespace=self.strip_whitespace,
743 to_upper=self.to_upper,
744 to_lower=self.to_lower,
745 pattern=self.pattern,
746 )
749def constr( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
750 *,
751 strip_whitespace: bool | None = None,
752 to_upper: bool | None = None,
753 to_lower: bool | None = None,
754 strict: bool | None = None,
755 min_length: int | None = None,
756 max_length: int | None = None,
757 pattern: str | Pattern[str] | None = None,
758) -> type[str]:
759 """
760 !!! warning "Discouraged"
761 This function is **discouraged** in favor of using
762 [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
763 [`StringConstraints`][pydantic.types.StringConstraints] instead.
765 This function will be **deprecated** in Pydantic 3.0.
767 The reason is that `constr` returns a type, which doesn't play well with static analysis tools.
769 === ":x: Don't do this"
770 ```python
771 from pydantic import BaseModel, constr
773 class Foo(BaseModel):
774 bar: constr(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$')
775 ```
777 === ":white_check_mark: Do this"
778 ```python
779 from typing import Annotated
781 from pydantic import BaseModel, StringConstraints
783 class Foo(BaseModel):
784 bar: Annotated[
785 str,
786 StringConstraints(
787 strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$'
788 ),
789 ]
790 ```
792 A wrapper around `str` that allows for additional constraints.
794 ```python
795 from pydantic import BaseModel, constr
797 class Foo(BaseModel):
798 bar: constr(strip_whitespace=True, to_upper=True)
800 foo = Foo(bar=' hello ')
801 print(foo)
802 #> bar='HELLO'
803 ```
805 Args:
806 strip_whitespace: Whether to remove leading and trailing whitespace.
807 to_upper: Whether to turn all characters to uppercase.
808 to_lower: Whether to turn all characters to lowercase.
809 strict: Whether to validate the string in strict mode.
810 min_length: The minimum length of the string.
811 max_length: The maximum length of the string.
812 pattern: A regex pattern to validate the string against.
814 Returns:
815 The wrapped string type.
816 """ # noqa: D212
817 return Annotated[ # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
818 str,
819 StringConstraints(
820 strip_whitespace=strip_whitespace,
821 to_upper=to_upper,
822 to_lower=to_lower,
823 strict=strict,
824 min_length=min_length,
825 max_length=max_length,
826 pattern=pattern,
827 ),
828 ]
831StrictStr = Annotated[str, Strict()] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
832"""A string that must be validated in strict mode.""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
835# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ COLLECTION TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
836HashableItemType = TypeVar('HashableItemType', bound=Hashable) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
839def conset( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
840 item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None
841) -> type[set[HashableItemType]]:
842 """A wrapper around `typing.Set` that allows for additional constraints.
844 Args:
845 item_type: The type of the items in the set.
846 min_length: The minimum length of the set.
847 max_length: The maximum length of the set.
849 Returns:
850 The wrapped set type.
851 """
852 return Annotated[set[item_type], annotated_types.Len(min_length or 0, max_length)] # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
855def confrozenset( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
856 item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None
857) -> type[frozenset[HashableItemType]]:
858 """A wrapper around `typing.FrozenSet` that allows for additional constraints.
860 Args:
861 item_type: The type of the items in the frozenset.
862 min_length: The minimum length of the frozenset.
863 max_length: The maximum length of the frozenset.
865 Returns:
866 The wrapped frozenset type.
867 """
868 return Annotated[frozenset[item_type], annotated_types.Len(min_length or 0, max_length)] # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
871AnyItemType = TypeVar('AnyItemType') 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
874def conlist( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
875 item_type: type[AnyItemType],
876 *,
877 min_length: int | None = None,
878 max_length: int | None = None,
879 unique_items: bool | None = None,
880) -> type[list[AnyItemType]]:
881 """A wrapper around [`list`][] that adds validation.
883 Args:
884 item_type: The type of the items in the list.
885 min_length: The minimum length of the list. Defaults to None.
886 max_length: The maximum length of the list. Defaults to None.
887 unique_items: Whether the items in the list must be unique. Defaults to None.
888 !!! warning Deprecated
889 The `unique_items` parameter is deprecated, use `Set` instead.
890 See [this issue](https://github.com/pydantic/pydantic-core/issues/296) for more details.
892 Returns:
893 The wrapped list type.
894 """
895 if unique_items is not None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
896 raise PydanticUserError( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
897 (
898 '`unique_items` is removed, use `Set` instead'
899 '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)'
900 ),
901 code='removed-kwargs',
902 )
903 return Annotated[list[item_type], annotated_types.Len(min_length or 0, max_length)] # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
906# ~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT STRING TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
908AnyType = TypeVar('AnyType') 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
909if TYPE_CHECKING: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
910 ImportString = Annotated[AnyType, ...]
911else:
913 class ImportString: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
914 """A type that can be used to import a Python object from a string.
916 `ImportString` expects a string and loads the Python object importable at that dotted path.
917 Attributes of modules may be separated from the module by `:` or `.`, e.g. if `'math:cos'` is provided,
918 the resulting field value would be the function `cos`. If a `.` is used and both an attribute and submodule
919 are present at the same path, the module will be preferred.
921 On model instantiation, pointers will be evaluated and imported. There is
922 some nuance to this behavior, demonstrated in the examples below.
924 ```python
925 import math
927 from pydantic import BaseModel, Field, ImportString, ValidationError
929 class ImportThings(BaseModel):
930 obj: ImportString
932 # A string value will cause an automatic import
933 my_cos = ImportThings(obj='math.cos')
935 # You can use the imported function as you would expect
936 cos_of_0 = my_cos.obj(0)
937 assert cos_of_0 == 1
939 # A string whose value cannot be imported will raise an error
940 try:
941 ImportThings(obj='foo.bar')
942 except ValidationError as e:
943 print(e)
944 '''
945 1 validation error for ImportThings
946 obj
947 Invalid python path: No module named 'foo.bar' [type=import_error, input_value='foo.bar', input_type=str]
948 '''
950 # Actual python objects can be assigned as well
951 my_cos = ImportThings(obj=math.cos)
952 my_cos_2 = ImportThings(obj='math.cos')
953 my_cos_3 = ImportThings(obj='math:cos')
954 assert my_cos == my_cos_2 == my_cos_3
956 # You can set default field value either as Python object:
957 class ImportThingsDefaultPyObj(BaseModel):
958 obj: ImportString = math.cos
960 # or as a string value (but only if used with `validate_default=True`)
961 class ImportThingsDefaultString(BaseModel):
962 obj: ImportString = Field(default='math.cos', validate_default=True)
964 my_cos_default1 = ImportThingsDefaultPyObj()
965 my_cos_default2 = ImportThingsDefaultString()
966 assert my_cos_default1.obj == my_cos_default2.obj == math.cos
968 # note: this will not work!
969 class ImportThingsMissingValidateDefault(BaseModel):
970 obj: ImportString = 'math.cos'
972 my_cos_default3 = ImportThingsMissingValidateDefault()
973 assert my_cos_default3.obj == 'math.cos' # just string, not evaluated
974 ```
976 Serializing an `ImportString` type to json is also possible.
978 ```python
979 from pydantic import BaseModel, ImportString
981 class ImportThings(BaseModel):
982 obj: ImportString
984 # Create an instance
985 m = ImportThings(obj='math.cos')
986 print(m)
987 #> obj=<built-in function cos>
988 print(m.model_dump_json())
989 #> {"obj":"math.cos"}
990 ```
991 """
993 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
994 def __class_getitem__(cls, item: AnyType) -> AnyType: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
995 return Annotated[item, cls()] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
997 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
998 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
999 cls, source: type[Any], handler: GetCoreSchemaHandler
1000 ) -> core_schema.CoreSchema:
1001 serializer = core_schema.plain_serializer_function_ser_schema(cls._serialize, when_used='json') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1002 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1003 # Treat bare usage of ImportString (`schema is None`) as the same as ImportString[Any]
1004 return core_schema.no_info_plain_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1005 function=_validators.import_string, serialization=serializer
1006 )
1007 else:
1008 return core_schema.no_info_before_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1009 function=_validators.import_string, schema=handler(source), serialization=serializer
1010 )
1012 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1013 def __get_pydantic_json_schema__(cls, cs: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1014 return handler(core_schema.str_schema()) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1016 @staticmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1017 def _serialize(v: Any) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1018 if isinstance(v, ModuleType): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1019 return v.__name__ 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1020 elif hasattr(v, '__module__') and hasattr(v, '__name__'): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1021 return f'{v.__module__}.{v.__name__}' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1022 # Handle special cases for sys.XXX streams
1023 # if we see more of these, we should consider a more general solution
1024 elif hasattr(v, 'name'): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1025 if v.name == '<stdout>': 1025 ↛ 1026line 1025 didn't jump to line 1026 because the condition on line 1025 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1026 return 'sys.stdout'
1027 elif v.name == '<stdin>': 1027 ↛ 1028line 1027 didn't jump to line 1028 because the condition on line 1027 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1028 return 'sys.stdin'
1029 elif v.name == '<stderr>': 1029 ↛ 1030line 1029 didn't jump to line 1030 because the condition on line 1029 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1030 return 'sys.stderr'
1031 else:
1032 return v 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1034 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1035 return 'ImportString' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1038# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1041def condecimal( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1042 *,
1043 strict: bool | None = None,
1044 gt: int | Decimal | None = None,
1045 ge: int | Decimal | None = None,
1046 lt: int | Decimal | None = None,
1047 le: int | Decimal | None = None,
1048 multiple_of: int | Decimal | None = None,
1049 max_digits: int | None = None,
1050 decimal_places: int | None = None,
1051 allow_inf_nan: bool | None = None,
1052) -> type[Decimal]:
1053 """
1054 !!! warning "Discouraged"
1055 This function is **discouraged** in favor of using
1056 [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
1057 [`Field`][pydantic.fields.Field] instead.
1059 This function will be **deprecated** in Pydantic 3.0.
1061 The reason is that `condecimal` returns a type, which doesn't play well with static analysis tools.
1063 === ":x: Don't do this"
1064 ```python
1065 from pydantic import BaseModel, condecimal
1067 class Foo(BaseModel):
1068 bar: condecimal(strict=True, allow_inf_nan=True)
1069 ```
1071 === ":white_check_mark: Do this"
1072 ```python
1073 from decimal import Decimal
1074 from typing import Annotated
1076 from pydantic import BaseModel, Field
1078 class Foo(BaseModel):
1079 bar: Annotated[Decimal, Field(strict=True, allow_inf_nan=True)]
1080 ```
1082 A wrapper around Decimal that adds validation.
1084 Args:
1085 strict: Whether to validate the value in strict mode. Defaults to `None`.
1086 gt: The value must be greater than this. Defaults to `None`.
1087 ge: The value must be greater than or equal to this. Defaults to `None`.
1088 lt: The value must be less than this. Defaults to `None`.
1089 le: The value must be less than or equal to this. Defaults to `None`.
1090 multiple_of: The value must be a multiple of this. Defaults to `None`.
1091 max_digits: The maximum number of digits. Defaults to `None`.
1092 decimal_places: The number of decimal places. Defaults to `None`.
1093 allow_inf_nan: Whether to allow infinity and NaN. Defaults to `None`.
1095 ```python
1096 from decimal import Decimal
1098 from pydantic import BaseModel, ValidationError, condecimal
1100 class ConstrainedExample(BaseModel):
1101 constrained_decimal: condecimal(gt=Decimal('1.0'))
1103 m = ConstrainedExample(constrained_decimal=Decimal('1.1'))
1104 print(repr(m))
1105 #> ConstrainedExample(constrained_decimal=Decimal('1.1'))
1107 try:
1108 ConstrainedExample(constrained_decimal=Decimal('0.9'))
1109 except ValidationError as e:
1110 print(e.errors())
1111 '''
1112 [
1113 {
1114 'type': 'greater_than',
1115 'loc': ('constrained_decimal',),
1116 'msg': 'Input should be greater than 1.0',
1117 'input': Decimal('0.9'),
1118 'ctx': {'gt': Decimal('1.0')},
1119 'url': 'https://errors.pydantic.dev/2/v/greater_than',
1120 }
1121 ]
1122 '''
1123 ```
1124 """ # noqa: D212
1125 return Annotated[ # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1126 Decimal,
1127 Strict(strict) if strict is not None else None,
1128 annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
1129 annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None,
1130 _fields.pydantic_general_metadata(max_digits=max_digits, decimal_places=decimal_places),
1131 AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None,
1132 ]
1135# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1138@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1139class UuidVersion: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1140 """A field metadata class to indicate a [UUID](https://docs.python.org/3/library/uuid.html) version.
1142 Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
1144 Attributes:
1145 uuid_version: The version of the UUID. Must be one of 1, 3, 4, 5, or 7.
1147 Example:
1148 ```python
1149 from typing import Annotated
1150 from uuid import UUID
1152 from pydantic.types import UuidVersion
1154 UUID1 = Annotated[UUID, UuidVersion(1)]
1155 ```
1156 """
1158 uuid_version: Literal[1, 3, 4, 5, 6, 7, 8] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1160 def __get_pydantic_json_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1161 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
1162 ) -> JsonSchemaValue:
1163 field_schema = handler(core_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1164 field_schema.pop('anyOf', None) # remove the bytes/str union 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1165 field_schema.update(type='string', format=f'uuid{self.uuid_version}') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1166 return field_schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1168 def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1169 if isinstance(self, source): 1169 ↛ 1171line 1169 didn't jump to line 1171 because the condition on line 1169 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1170 # used directly as a type
1171 return core_schema.uuid_schema(version=self.uuid_version)
1172 else:
1173 # update existing schema with self.uuid_version
1174 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1175 _check_annotated_type(schema['type'], 'uuid', self.__class__.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1176 schema['version'] = self.uuid_version # type: ignore 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1177 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1179 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1180 return hash(type(self.uuid_version)) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1183UUID1 = Annotated[UUID, UuidVersion(1)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1184"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 1. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1186```python
1187import uuid
1189from pydantic import UUID1, BaseModel
1191class Model(BaseModel):
1192 uuid1: UUID1
1194Model(uuid1=uuid.uuid1())
1195```
1196"""
1197UUID3 = Annotated[UUID, UuidVersion(3)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1198"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 3. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1200```python
1201import uuid
1203from pydantic import UUID3, BaseModel
1205class Model(BaseModel):
1206 uuid3: UUID3
1208Model(uuid3=uuid.uuid3(uuid.NAMESPACE_DNS, 'pydantic.org'))
1209```
1210"""
1211UUID4 = Annotated[UUID, UuidVersion(4)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1212"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 4. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1214```python
1215import uuid
1217from pydantic import UUID4, BaseModel
1219class Model(BaseModel):
1220 uuid4: UUID4
1222Model(uuid4=uuid.uuid4())
1223```
1224"""
1225UUID5 = Annotated[UUID, UuidVersion(5)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1226"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 5. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1228```python
1229import uuid
1231from pydantic import UUID5, BaseModel
1233class Model(BaseModel):
1234 uuid5: UUID5
1236Model(uuid5=uuid.uuid5(uuid.NAMESPACE_DNS, 'pydantic.org'))
1237```
1238"""
1239UUID6 = Annotated[UUID, UuidVersion(6)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1240"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 6. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1242```python
1243import uuid
1245from pydantic import UUID6, BaseModel
1247class Model(BaseModel):
1248 uuid6: UUID6
1250Model(uuid6=uuid.UUID('1efea953-c2d6-6790-aa0a-69db8c87df97'))
1251```
1252"""
1253UUID7 = Annotated[UUID, UuidVersion(7)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1254"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 7. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1256```python
1257import uuid
1259from pydantic import UUID7, BaseModel
1261class Model(BaseModel):
1262 uuid7: UUID7
1264Model(uuid7=uuid.UUID('0194fdcb-1c47-7a09-b52c-561154de0b4a'))
1265```
1266"""
1267UUID8 = Annotated[UUID, UuidVersion(8)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1268"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 8. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1270```python
1271import uuid
1273from pydantic import UUID8, BaseModel
1275class Model(BaseModel):
1276 uuid8: UUID8
1278Model(uuid8=uuid.UUID('81a0b92e-6078-8551-9c81-8ccb666bdab8'))
1279```
1280"""
1282# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1285@_dataclasses.dataclass 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1286class PathType: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1287 path_type: Literal['file', 'dir', 'new', 'socket'] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1289 def __get_pydantic_json_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1290 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
1291 ) -> JsonSchemaValue:
1292 field_schema = handler(core_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1293 format_conversion = {'file': 'file-path', 'dir': 'directory-path'} 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1294 field_schema.update(format=format_conversion.get(self.path_type, 'path'), type='string') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1295 return field_schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1297 def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1298 function_lookup = { 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1299 'file': cast(core_schema.WithInfoValidatorFunction, self.validate_file),
1300 'dir': cast(core_schema.WithInfoValidatorFunction, self.validate_directory),
1301 'new': cast(core_schema.WithInfoValidatorFunction, self.validate_new),
1302 'socket': cast(core_schema.WithInfoValidatorFunction, self.validate_socket),
1303 }
1305 return core_schema.with_info_after_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1306 function_lookup[self.path_type],
1307 handler(source),
1308 )
1310 @staticmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1311 def validate_file(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1312 if path.is_file(): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1313 return path 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1314 else:
1315 raise PydanticCustomError('path_not_file', 'Path does not point to a file') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1317 @staticmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1318 def validate_socket(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1319 if path.is_socket(): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1320 return path 1CDbcdefghijEa
1321 else:
1322 raise PydanticCustomError('path_not_socket', 'Path does not point to a socket') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1324 @staticmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1325 def validate_directory(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1326 if path.is_dir(): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1327 return path 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1328 else:
1329 raise PydanticCustomError('path_not_directory', 'Path does not point to a directory') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1331 @staticmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1332 def validate_new(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1333 if path.exists(): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1334 raise PydanticCustomError('path_exists', 'Path already exists') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1335 elif not path.parent.exists(): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1336 raise PydanticCustomError('parent_does_not_exist', 'Parent directory does not exist') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1337 else:
1338 return path 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1340 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1341 return hash(type(self.path_type)) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1344FilePath = Annotated[Path, PathType('file')] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1345"""A path that must point to a file. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1347```python
1348from pathlib import Path
1350from pydantic import BaseModel, FilePath, ValidationError
1352class Model(BaseModel):
1353 f: FilePath
1355path = Path('text.txt')
1356path.touch()
1357m = Model(f='text.txt')
1358print(m.model_dump())
1359#> {'f': PosixPath('text.txt')}
1360path.unlink()
1362path = Path('directory')
1363path.mkdir(exist_ok=True)
1364try:
1365 Model(f='directory') # directory
1366except ValidationError as e:
1367 print(e)
1368 '''
1369 1 validation error for Model
1370 f
1371 Path does not point to a file [type=path_not_file, input_value='directory', input_type=str]
1372 '''
1373path.rmdir()
1375try:
1376 Model(f='not-exists-file')
1377except ValidationError as e:
1378 print(e)
1379 '''
1380 1 validation error for Model
1381 f
1382 Path does not point to a file [type=path_not_file, input_value='not-exists-file', input_type=str]
1383 '''
1384```
1385"""
1386DirectoryPath = Annotated[Path, PathType('dir')] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1387"""A path that must point to a directory. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1389```python
1390from pathlib import Path
1392from pydantic import BaseModel, DirectoryPath, ValidationError
1394class Model(BaseModel):
1395 f: DirectoryPath
1397path = Path('directory/')
1398path.mkdir()
1399m = Model(f='directory/')
1400print(m.model_dump())
1401#> {'f': PosixPath('directory')}
1402path.rmdir()
1404path = Path('file.txt')
1405path.touch()
1406try:
1407 Model(f='file.txt') # file
1408except ValidationError as e:
1409 print(e)
1410 '''
1411 1 validation error for Model
1412 f
1413 Path does not point to a directory [type=path_not_directory, input_value='file.txt', input_type=str]
1414 '''
1415path.unlink()
1417try:
1418 Model(f='not-exists-directory')
1419except ValidationError as e:
1420 print(e)
1421 '''
1422 1 validation error for Model
1423 f
1424 Path does not point to a directory [type=path_not_directory, input_value='not-exists-directory', input_type=str]
1425 '''
1426```
1427"""
1428NewPath = Annotated[Path, PathType('new')] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1429"""A path for a new file or directory that must not already exist. The parent directory must already exist.""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1431SocketPath = Annotated[Path, PathType('socket')] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1432"""A path to an existing socket file""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
1434# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1436if TYPE_CHECKING: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1437 # Json[list[str]] will be recognized by type checkers as list[str]
1438 Json = Annotated[AnyType, ...]
1440else:
1442 class Json: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1443 """A special type wrapper which loads JSON before parsing.
1445 You can use the `Json` data type to make Pydantic first load a raw JSON string before
1446 validating the loaded data into the parametrized type:
1448 ```python
1449 from typing import Any
1451 from pydantic import BaseModel, Json, ValidationError
1453 class AnyJsonModel(BaseModel):
1454 json_obj: Json[Any]
1456 class ConstrainedJsonModel(BaseModel):
1457 json_obj: Json[list[int]]
1459 print(AnyJsonModel(json_obj='{"b": 1}'))
1460 #> json_obj={'b': 1}
1461 print(ConstrainedJsonModel(json_obj='[1, 2, 3]'))
1462 #> json_obj=[1, 2, 3]
1464 try:
1465 ConstrainedJsonModel(json_obj=12)
1466 except ValidationError as e:
1467 print(e)
1468 '''
1469 1 validation error for ConstrainedJsonModel
1470 json_obj
1471 JSON input should be string, bytes or bytearray [type=json_type, input_value=12, input_type=int]
1472 '''
1474 try:
1475 ConstrainedJsonModel(json_obj='[a, b]')
1476 except ValidationError as e:
1477 print(e)
1478 '''
1479 1 validation error for ConstrainedJsonModel
1480 json_obj
1481 Invalid JSON: expected value at line 1 column 2 [type=json_invalid, input_value='[a, b]', input_type=str]
1482 '''
1484 try:
1485 ConstrainedJsonModel(json_obj='["a", "b"]')
1486 except ValidationError as e:
1487 print(e)
1488 '''
1489 2 validation errors for ConstrainedJsonModel
1490 json_obj.0
1491 Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
1492 json_obj.1
1493 Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str]
1494 '''
1495 ```
1497 When you dump the model using `model_dump` or `model_dump_json`, the dumped value will be the result of validation,
1498 not the original JSON string. However, you can use the argument `round_trip=True` to get the original JSON string back:
1500 ```python
1501 from pydantic import BaseModel, Json
1503 class ConstrainedJsonModel(BaseModel):
1504 json_obj: Json[list[int]]
1506 print(ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json())
1507 #> {"json_obj":[1,2,3]}
1508 print(
1509 ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json(round_trip=True)
1510 )
1511 #> {"json_obj":"[1,2,3]"}
1512 ```
1513 """
1515 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1516 def __class_getitem__(cls, item: AnyType) -> AnyType: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1517 return Annotated[item, cls()] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1519 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1520 def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1521 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1522 return core_schema.json_schema(None) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1523 else:
1524 return core_schema.json_schema(handler(source)) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1526 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1527 return 'Json' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1529 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1530 return hash(type(self)) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1532 def __eq__(self, other: Any) -> bool: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1533 return type(other) is type(self) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1536# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1538SecretType = TypeVar('SecretType') 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1541class _SecretBase(Generic[SecretType]): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1542 def __init__(self, secret_value: SecretType) -> None: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1543 self._secret_value: SecretType = secret_value 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1545 def get_secret_value(self) -> SecretType: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1546 """Get the secret value.
1548 Returns:
1549 The secret value.
1550 """
1551 return self._secret_value 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1553 def __eq__(self, other: Any) -> bool: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1554 return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value() 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1556 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1557 return hash(self.get_secret_value()) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1559 def __str__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1560 return str(self._display()) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1562 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1563 return f'{self.__class__.__name__}({self._display()!r})' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1565 def _display(self) -> str | bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1566 raise NotImplementedError
1569def _serialize_secret(value: Secret[SecretType], info: core_schema.SerializationInfo) -> str | Secret[SecretType]: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1570 if info.mode == 'json': 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1571 return str(value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1572 else:
1573 return value 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1576class Secret(_SecretBase[SecretType]): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1577 """A generic base class used for defining a field with sensitive information that you do not want to be visible in logging or tracebacks.
1579 You may either directly parametrize `Secret` with a type, or subclass from `Secret` with a parametrized type. The benefit of subclassing
1580 is that you can define a custom `_display` method, which will be used for `repr()` and `str()` methods. The examples below demonstrate both
1581 ways of using `Secret` to create a new secret type.
1583 1. Directly parametrizing `Secret` with a type:
1585 ```python
1586 from pydantic import BaseModel, Secret
1588 SecretBool = Secret[bool]
1590 class Model(BaseModel):
1591 secret_bool: SecretBool
1593 m = Model(secret_bool=True)
1594 print(m.model_dump())
1595 #> {'secret_bool': Secret('**********')}
1597 print(m.model_dump_json())
1598 #> {"secret_bool":"**********"}
1600 print(m.secret_bool.get_secret_value())
1601 #> True
1602 ```
1604 2. Subclassing from parametrized `Secret`:
1606 ```python
1607 from datetime import date
1609 from pydantic import BaseModel, Secret
1611 class SecretDate(Secret[date]):
1612 def _display(self) -> str:
1613 return '****/**/**'
1615 class Model(BaseModel):
1616 secret_date: SecretDate
1618 m = Model(secret_date=date(2022, 1, 1))
1619 print(m.model_dump())
1620 #> {'secret_date': SecretDate('****/**/**')}
1622 print(m.model_dump_json())
1623 #> {"secret_date":"****/**/**"}
1625 print(m.secret_date.get_secret_value())
1626 #> 2022-01-01
1627 ```
1629 The value returned by the `_display` method will be used for `repr()` and `str()`.
1631 You can enforce constraints on the underlying type through annotations:
1632 For example:
1634 ```python
1635 from typing import Annotated
1637 from pydantic import BaseModel, Field, Secret, ValidationError
1639 SecretPosInt = Secret[Annotated[int, Field(gt=0, strict=True)]]
1641 class Model(BaseModel):
1642 sensitive_int: SecretPosInt
1644 m = Model(sensitive_int=42)
1645 print(m.model_dump())
1646 #> {'sensitive_int': Secret('**********')}
1648 try:
1649 m = Model(sensitive_int=-42) # (1)!
1650 except ValidationError as exc_info:
1651 print(exc_info.errors(include_url=False, include_input=False))
1652 '''
1653 [
1654 {
1655 'type': 'greater_than',
1656 'loc': ('sensitive_int',),
1657 'msg': 'Input should be greater than 0',
1658 'ctx': {'gt': 0},
1659 }
1660 ]
1661 '''
1663 try:
1664 m = Model(sensitive_int='42') # (2)!
1665 except ValidationError as exc_info:
1666 print(exc_info.errors(include_url=False, include_input=False))
1667 '''
1668 [
1669 {
1670 'type': 'int_type',
1671 'loc': ('sensitive_int',),
1672 'msg': 'Input should be a valid integer',
1673 }
1674 ]
1675 '''
1676 ```
1678 1. The input value is not greater than 0, so it raises a validation error.
1679 2. The input value is not an integer, so it raises a validation error because the `SecretPosInt` type has strict mode enabled.
1680 """
1682 def _display(self) -> str | bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1683 return '**********' if self.get_secret_value() else '' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1685 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1686 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1687 inner_type = None 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1688 # if origin_type is Secret, then cls is a GenericAlias, and we can extract the inner type directly
1689 origin_type = get_origin(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1690 if origin_type is not None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1691 inner_type = get_args(source)[0] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1692 # otherwise, we need to get the inner type from the base class
1693 else:
1694 bases = getattr(cls, '__orig_bases__', getattr(cls, '__bases__', [])) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1695 for base in bases: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1696 if get_origin(base) is Secret: 1696 ↛ 1695line 1696 didn't jump to line 1695 because the condition on line 1696 was always true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1697 inner_type = get_args(base)[0] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1698 if bases == [] or inner_type is None: 1698 ↛ 1699line 1698 didn't jump to line 1699 because the condition on line 1698 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1699 raise TypeError(
1700 f"Can't get secret type from {cls.__name__}. "
1701 'Please use Secret[<type>], or subclass from Secret[<type>] instead.'
1702 )
1704 inner_schema = handler.generate_schema(inner_type) # type: ignore 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1706 def validate_secret_value(value, handler) -> Secret[SecretType]: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1707 if isinstance(value, Secret): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1708 value = value.get_secret_value() 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1709 validated_inner = handler(value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1710 return cls(validated_inner) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1712 return core_schema.json_or_python_schema( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1713 python_schema=core_schema.no_info_wrap_validator_function(
1714 validate_secret_value,
1715 inner_schema,
1716 ),
1717 json_schema=core_schema.no_info_after_validator_function(lambda x: cls(x), inner_schema),
1718 serialization=core_schema.plain_serializer_function_ser_schema(
1719 _serialize_secret,
1720 info_arg=True,
1721 when_used='always',
1722 ),
1723 )
1725 __pydantic_serializer__ = SchemaSerializer( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1726 core_schema.any_schema(
1727 serialization=core_schema.plain_serializer_function_ser_schema(
1728 _serialize_secret,
1729 info_arg=True,
1730 when_used='always',
1731 )
1732 )
1733 )
1736def _secret_display(value: SecretType) -> str: # type: ignore 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1737 return '**********' if value else '' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1740def _serialize_secret_field( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1741 value: _SecretField[SecretType], info: core_schema.SerializationInfo
1742) -> str | _SecretField[SecretType]:
1743 if info.mode == 'json': 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1744 # we want the output to always be string without the `b'` prefix for bytes,
1745 # hence we just use `secret_display`
1746 return _secret_display(value.get_secret_value()) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1747 else:
1748 return value 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1751class _SecretField(_SecretBase[SecretType]): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1752 _inner_schema: ClassVar[CoreSchema] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1753 _error_kind: ClassVar[str] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1755 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1756 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1757 def get_json_schema(_core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1758 json_schema = handler(cls._inner_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1759 _utils.update_not_none( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1760 json_schema,
1761 type='string',
1762 writeOnly=True,
1763 format='password',
1764 )
1765 return json_schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1767 def get_secret_schema(strict: bool) -> CoreSchema: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1768 inner_schema = {**cls._inner_schema, 'strict': strict} 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1769 json_schema = core_schema.no_info_after_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1770 source, # construct the type
1771 inner_schema, # pyright: ignore[reportArgumentType]
1772 )
1773 return core_schema.json_or_python_schema( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1774 python_schema=core_schema.union_schema(
1775 [
1776 core_schema.is_instance_schema(source),
1777 json_schema,
1778 ],
1779 custom_error_type=cls._error_kind,
1780 ),
1781 json_schema=json_schema,
1782 serialization=core_schema.plain_serializer_function_ser_schema(
1783 _serialize_secret_field,
1784 info_arg=True,
1785 when_used='always',
1786 ),
1787 )
1789 return core_schema.lax_or_strict_schema( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1790 lax_schema=get_secret_schema(strict=False),
1791 strict_schema=get_secret_schema(strict=True),
1792 metadata={'pydantic_js_functions': [get_json_schema]},
1793 )
1795 __pydantic_serializer__ = SchemaSerializer( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1796 core_schema.any_schema(
1797 serialization=core_schema.plain_serializer_function_ser_schema(
1798 _serialize_secret_field,
1799 info_arg=True,
1800 when_used='always',
1801 )
1802 )
1803 )
1806class SecretStr(_SecretField[str]): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1807 """A string used for storing sensitive information that you do not want to be visible in logging or tracebacks.
1809 When the secret value is nonempty, it is displayed as `'**********'` instead of the underlying value in
1810 calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `''`.
1812 ```python
1813 from pydantic import BaseModel, SecretStr
1815 class User(BaseModel):
1816 username: str
1817 password: SecretStr
1819 user = User(username='scolvin', password='password1')
1821 print(user)
1822 #> username='scolvin' password=SecretStr('**********')
1823 print(user.password.get_secret_value())
1824 #> password1
1825 print((SecretStr('password'), SecretStr('')))
1826 #> (SecretStr('**********'), SecretStr(''))
1827 ```
1829 As seen above, by default, [`SecretStr`][pydantic.types.SecretStr] (and [`SecretBytes`][pydantic.types.SecretBytes])
1830 will be serialized as `**********` when serializing to json.
1832 You can use the [`field_serializer`][pydantic.functional_serializers.field_serializer] to dump the
1833 secret as plain-text when serializing to json.
1835 ```python
1836 from pydantic import BaseModel, SecretBytes, SecretStr, field_serializer
1838 class Model(BaseModel):
1839 password: SecretStr
1840 password_bytes: SecretBytes
1842 @field_serializer('password', 'password_bytes', when_used='json')
1843 def dump_secret(self, v):
1844 return v.get_secret_value()
1846 model = Model(password='IAmSensitive', password_bytes=b'IAmSensitiveBytes')
1847 print(model)
1848 #> password=SecretStr('**********') password_bytes=SecretBytes(b'**********')
1849 print(model.password)
1850 #> **********
1851 print(model.model_dump())
1852 '''
1853 {
1854 'password': SecretStr('**********'),
1855 'password_bytes': SecretBytes(b'**********'),
1856 }
1857 '''
1858 print(model.model_dump_json())
1859 #> {"password":"IAmSensitive","password_bytes":"IAmSensitiveBytes"}
1860 ```
1861 """
1863 _inner_schema: ClassVar[CoreSchema] = core_schema.str_schema() 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1864 _error_kind: ClassVar[str] = 'string_type' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1866 def __len__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1867 return len(self._secret_value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1869 def _display(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1870 return _secret_display(self._secret_value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1873class SecretBytes(_SecretField[bytes]): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1874 """A bytes used for storing sensitive information that you do not want to be visible in logging or tracebacks.
1876 It displays `b'**********'` instead of the string value on `repr()` and `str()` calls.
1877 When the secret value is nonempty, it is displayed as `b'**********'` instead of the underlying value in
1878 calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `b''`.
1880 ```python
1881 from pydantic import BaseModel, SecretBytes
1883 class User(BaseModel):
1884 username: str
1885 password: SecretBytes
1887 user = User(username='scolvin', password=b'password1')
1888 #> username='scolvin' password=SecretBytes(b'**********')
1889 print(user.password.get_secret_value())
1890 #> b'password1'
1891 print((SecretBytes(b'password'), SecretBytes(b'')))
1892 #> (SecretBytes(b'**********'), SecretBytes(b''))
1893 ```
1894 """
1896 _inner_schema: ClassVar[CoreSchema] = core_schema.bytes_schema() 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1897 _error_kind: ClassVar[str] = 'bytes_type' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1899 def __len__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1900 return len(self._secret_value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1902 def _display(self) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1903 return _secret_display(self._secret_value).encode() 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1906# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1909class PaymentCardBrand(str, Enum): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1910 amex = 'American Express' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1911 mastercard = 'Mastercard' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1912 visa = 'Visa' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1913 other = 'other' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1915 def __str__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1916 return self.value 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1919@deprecated( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1920 'The `PaymentCardNumber` class is deprecated, use `pydantic_extra_types` instead. '
1921 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_payment/#pydantic_extra_types.payment.PaymentCardNumber.',
1922 category=PydanticDeprecatedSince20,
1923)
1924class PaymentCardNumber(str): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1925 """Based on: https://en.wikipedia.org/wiki/Payment_card_number."""
1927 strip_whitespace: ClassVar[bool] = True 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1928 min_length: ClassVar[int] = 12 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1929 max_length: ClassVar[int] = 19 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1930 bin: str 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1931 last4: str 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1932 brand: PaymentCardBrand 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1934 def __init__(self, card_number: str): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1935 self.validate_digits(card_number) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1937 card_number = self.validate_luhn_check_digit(card_number) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1939 self.bin = card_number[:6] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1940 self.last4 = card_number[-4:] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1941 self.brand = self.validate_brand(card_number) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1943 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1944 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1945 return core_schema.with_info_after_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1946 cls.validate,
1947 core_schema.str_schema(
1948 min_length=cls.min_length, max_length=cls.max_length, strip_whitespace=cls.strip_whitespace
1949 ),
1950 )
1952 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1953 def validate(cls, input_value: str, /, _: core_schema.ValidationInfo) -> PaymentCardNumber: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1954 """Validate the card number and return a `PaymentCardNumber` instance."""
1955 return cls(input_value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1957 @property 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1958 def masked(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1959 """Mask all but the last 4 digits of the card number.
1961 Returns:
1962 A masked card number string.
1963 """
1964 num_masked = len(self) - 10 # len(bin) + len(last4) == 10 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1965 return f'{self.bin}{"*" * num_masked}{self.last4}' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1967 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1968 def validate_digits(cls, card_number: str) -> None: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1969 """Validate that the card number is all digits."""
1970 if not card_number.isdigit(): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1971 raise PydanticCustomError('payment_card_number_digits', 'Card number is not all digits') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1973 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1974 def validate_luhn_check_digit(cls, card_number: str) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1975 """Based on: https://en.wikipedia.org/wiki/Luhn_algorithm."""
1976 sum_ = int(card_number[-1]) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1977 length = len(card_number) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1978 parity = length % 2 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1979 for i in range(length - 1): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1980 digit = int(card_number[i]) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1981 if i % 2 == parity: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1982 digit *= 2 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1983 if digit > 9: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1984 digit -= 9 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1985 sum_ += digit 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1986 valid = sum_ % 10 == 0 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1987 if not valid: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1988 raise PydanticCustomError('payment_card_number_luhn', 'Card number is not luhn valid') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1989 return card_number 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1991 @staticmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1992 def validate_brand(card_number: str) -> PaymentCardBrand: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
1993 """Validate length based on BIN for major brands:
1994 https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN).
1995 """
1996 if card_number[0] == '4': 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1997 brand = PaymentCardBrand.visa 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1998 elif 51 <= int(card_number[:2]) <= 55: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
1999 brand = PaymentCardBrand.mastercard 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2000 elif card_number[:2] in {'34', '37'}: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2001 brand = PaymentCardBrand.amex 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2002 else:
2003 brand = PaymentCardBrand.other 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2005 required_length: None | int | str = None 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2006 if brand in PaymentCardBrand.mastercard: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2007 required_length = 16 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2008 valid = len(card_number) == required_length 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2009 elif brand == PaymentCardBrand.visa: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2010 required_length = '13, 16 or 19' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2011 valid = len(card_number) in {13, 16, 19} 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2012 elif brand == PaymentCardBrand.amex: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2013 required_length = 15 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2014 valid = len(card_number) == required_length 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2015 else:
2016 valid = True 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2018 if not valid: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2019 raise PydanticCustomError( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2020 'payment_card_number_brand',
2021 'Length for a {brand} card must be {required_length}',
2022 {'brand': brand, 'required_length': required_length},
2023 )
2024 return brand 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2027# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2030class ByteSize(int): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2031 """Converts a string representing a number of bytes with units (such as `'1KB'` or `'11.5MiB'`) into an integer.
2033 You can use the `ByteSize` data type to (case-insensitively) convert a string representation of a number of bytes into
2034 an integer, and also to print out human-readable strings representing a number of bytes.
2036 In conformance with [IEC 80000-13 Standard](https://en.wikipedia.org/wiki/ISO/IEC_80000) we interpret `'1KB'` to mean 1000 bytes,
2037 and `'1KiB'` to mean 1024 bytes. In general, including a middle `'i'` will cause the unit to be interpreted as a power of 2,
2038 rather than a power of 10 (so, for example, `'1 MB'` is treated as `1_000_000` bytes, whereas `'1 MiB'` is treated as `1_048_576` bytes).
2040 !!! info
2041 Note that `1b` will be parsed as "1 byte" and not "1 bit".
2043 ```python
2044 from pydantic import BaseModel, ByteSize
2046 class MyModel(BaseModel):
2047 size: ByteSize
2049 print(MyModel(size=52000).size)
2050 #> 52000
2051 print(MyModel(size='3000 KiB').size)
2052 #> 3072000
2054 m = MyModel(size='50 PB')
2055 print(m.size.human_readable())
2056 #> 44.4PiB
2057 print(m.size.human_readable(decimal=True))
2058 #> 50.0PB
2059 print(m.size.human_readable(separator=' '))
2060 #> 44.4 PiB
2062 print(m.size.to('TiB'))
2063 #> 45474.73508864641
2064 ```
2065 """
2067 byte_sizes = { 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2068 'b': 1,
2069 'kb': 10**3,
2070 'mb': 10**6,
2071 'gb': 10**9,
2072 'tb': 10**12,
2073 'pb': 10**15,
2074 'eb': 10**18,
2075 'kib': 2**10,
2076 'mib': 2**20,
2077 'gib': 2**30,
2078 'tib': 2**40,
2079 'pib': 2**50,
2080 'eib': 2**60,
2081 'bit': 1 / 8,
2082 'kbit': 10**3 / 8,
2083 'mbit': 10**6 / 8,
2084 'gbit': 10**9 / 8,
2085 'tbit': 10**12 / 8,
2086 'pbit': 10**15 / 8,
2087 'ebit': 10**18 / 8,
2088 'kibit': 2**10 / 8,
2089 'mibit': 2**20 / 8,
2090 'gibit': 2**30 / 8,
2091 'tibit': 2**40 / 8,
2092 'pibit': 2**50 / 8,
2093 'eibit': 2**60 / 8,
2094 }
2095 byte_sizes.update({k.lower()[0]: v for k, v in byte_sizes.items() if 'i' not in k}) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2097 byte_string_pattern = r'^\s*(\d*\.?\d+)\s*(\w+)?' 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2098 byte_string_re = re.compile(byte_string_pattern, re.IGNORECASE) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2100 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2101 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2102 return core_schema.with_info_after_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2103 function=cls._validate,
2104 schema=core_schema.union_schema(
2105 [
2106 core_schema.str_schema(pattern=cls.byte_string_pattern),
2107 core_schema.int_schema(ge=0),
2108 ],
2109 custom_error_type='byte_size',
2110 custom_error_message='could not parse value and unit from byte string',
2111 ),
2112 serialization=core_schema.plain_serializer_function_ser_schema(
2113 int, return_schema=core_schema.int_schema(ge=0)
2114 ),
2115 )
2117 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2118 def _validate(cls, input_value: Any, /, _: core_schema.ValidationInfo) -> ByteSize: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2119 try: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2120 return cls(int(input_value)) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2121 except ValueError: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2122 pass 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2124 str_match = cls.byte_string_re.match(str(input_value)) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2125 if str_match is None: 2125 ↛ 2126line 2125 didn't jump to line 2126 because the condition on line 2125 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2126 raise PydanticCustomError('byte_size', 'could not parse value and unit from byte string')
2128 scalar, unit = str_match.groups() 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2129 if unit is None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2130 unit = 'b' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2132 try: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2133 unit_mult = cls.byte_sizes[unit.lower()] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2134 except KeyError: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2135 raise PydanticCustomError('byte_size_unit', 'could not interpret byte unit: {unit}', {'unit': unit}) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2137 return cls(int(float(scalar) * unit_mult)) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2139 def human_readable(self, decimal: bool = False, separator: str = '') -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2140 """Converts a byte size to a human readable string.
2142 Args:
2143 decimal: If True, use decimal units (e.g. 1000 bytes per KB). If False, use binary units
2144 (e.g. 1024 bytes per KiB).
2145 separator: A string used to split the value and unit. Defaults to an empty string ('').
2147 Returns:
2148 A human readable string representation of the byte size.
2149 """
2150 if decimal: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2151 divisor = 1000 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2152 units = 'B', 'KB', 'MB', 'GB', 'TB', 'PB' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2153 final_unit = 'EB' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2154 else:
2155 divisor = 1024 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2156 units = 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2157 final_unit = 'EiB' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2159 num = float(self) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2160 for unit in units: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2161 if abs(num) < divisor: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2162 if unit == 'B': 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2163 return f'{num:0.0f}{separator}{unit}' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2164 else:
2165 return f'{num:0.1f}{separator}{unit}' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2166 num /= divisor 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2168 return f'{num:0.1f}{separator}{final_unit}' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2170 def to(self, unit: str) -> float: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2171 """Converts a byte size to another unit, including both byte and bit units.
2173 Args:
2174 unit: The unit to convert to. Must be one of the following: B, KB, MB, GB, TB, PB, EB,
2175 KiB, MiB, GiB, TiB, PiB, EiB (byte units) and
2176 bit, kbit, mbit, gbit, tbit, pbit, ebit,
2177 kibit, mibit, gibit, tibit, pibit, eibit (bit units).
2179 Returns:
2180 The byte size in the new unit.
2181 """
2182 try: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2183 unit_div = self.byte_sizes[unit.lower()] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2184 except KeyError: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2185 raise PydanticCustomError('byte_size_unit', 'Could not interpret byte unit: {unit}', {'unit': unit}) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2187 return self / unit_div 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2190# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2193def _check_annotated_type(annotated_type: str, expected_type: str, annotation: str) -> None: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2194 if annotated_type != expected_type: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2195 raise PydanticUserError(f"'{annotation}' cannot annotate '{annotated_type}'.", code='invalid-annotated-type') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2198if TYPE_CHECKING: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2199 PastDate = Annotated[date, ...]
2200 FutureDate = Annotated[date, ...]
2201else:
2203 class PastDate: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2204 """A date in the past."""
2206 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2207 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2208 cls, source: type[Any], handler: GetCoreSchemaHandler
2209 ) -> core_schema.CoreSchema:
2210 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2211 # used directly as a type
2212 return core_schema.date_schema(now_op='past') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2213 else:
2214 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2215 _check_annotated_type(schema['type'], 'date', cls.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2216 schema['now_op'] = 'past' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2217 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2219 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2220 return 'PastDate' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2222 class FutureDate: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2223 """A date in the future."""
2225 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2226 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2227 cls, source: type[Any], handler: GetCoreSchemaHandler
2228 ) -> core_schema.CoreSchema:
2229 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2230 # used directly as a type
2231 return core_schema.date_schema(now_op='future') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2232 else:
2233 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2234 _check_annotated_type(schema['type'], 'date', cls.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2235 schema['now_op'] = 'future' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2236 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2238 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2239 return 'FutureDate' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2242def condate( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2243 *,
2244 strict: bool | None = None,
2245 gt: date | None = None,
2246 ge: date | None = None,
2247 lt: date | None = None,
2248 le: date | None = None,
2249) -> type[date]:
2250 """A wrapper for date that adds constraints.
2252 Args:
2253 strict: Whether to validate the date value in strict mode. Defaults to `None`.
2254 gt: The value must be greater than this. Defaults to `None`.
2255 ge: The value must be greater than or equal to this. Defaults to `None`.
2256 lt: The value must be less than this. Defaults to `None`.
2257 le: The value must be less than or equal to this. Defaults to `None`.
2259 Returns:
2260 A date type with the specified constraints.
2261 """
2262 return Annotated[ # pyright: ignore[reportReturnType] 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2263 date,
2264 Strict(strict) if strict is not None else None,
2265 annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
2266 ]
2269# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATETIME TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2271if TYPE_CHECKING: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2272 AwareDatetime = Annotated[datetime, ...]
2273 NaiveDatetime = Annotated[datetime, ...]
2274 PastDatetime = Annotated[datetime, ...]
2275 FutureDatetime = Annotated[datetime, ...]
2277else:
2279 class AwareDatetime: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2280 """A datetime that requires timezone info."""
2282 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2283 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2284 cls, source: type[Any], handler: GetCoreSchemaHandler
2285 ) -> core_schema.CoreSchema:
2286 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2287 # used directly as a type
2288 return core_schema.datetime_schema(tz_constraint='aware') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2289 else:
2290 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2291 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2292 schema['tz_constraint'] = 'aware' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2293 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2295 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2296 return 'AwareDatetime' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2298 class NaiveDatetime: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2299 """A datetime that doesn't require timezone info."""
2301 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2302 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2303 cls, source: type[Any], handler: GetCoreSchemaHandler
2304 ) -> core_schema.CoreSchema:
2305 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2306 # used directly as a type
2307 return core_schema.datetime_schema(tz_constraint='naive') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2308 else:
2309 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2310 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2311 schema['tz_constraint'] = 'naive' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2312 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2314 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2315 return 'NaiveDatetime' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2317 class PastDatetime: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2318 """A datetime that must be in the past."""
2320 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2321 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2322 cls, source: type[Any], handler: GetCoreSchemaHandler
2323 ) -> core_schema.CoreSchema:
2324 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2325 # used directly as a type
2326 return core_schema.datetime_schema(now_op='past') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2327 else:
2328 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2329 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2330 schema['now_op'] = 'past' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2331 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2333 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2334 return 'PastDatetime' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2336 class FutureDatetime: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2337 """A datetime that must be in the future."""
2339 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2340 def __get_pydantic_core_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2341 cls, source: type[Any], handler: GetCoreSchemaHandler
2342 ) -> core_schema.CoreSchema:
2343 if cls is source: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2344 # used directly as a type
2345 return core_schema.datetime_schema(now_op='future') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2346 else:
2347 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2348 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2349 schema['now_op'] = 'future' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2350 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2352 def __repr__(self) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2353 return 'FutureDatetime' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2356# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Encoded TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2359class EncoderProtocol(Protocol): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2360 """Protocol for encoding and decoding data to and from bytes.""" 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2362 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2363 def decode(cls, data: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2364 """Decode the data using the encoder.
2366 Args:
2367 data: The data to decode.
2369 Returns:
2370 The decoded data.
2371 """
2372 ...
2374 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2375 def encode(cls, value: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2376 """Encode the data using the encoder.
2378 Args:
2379 value: The data to encode.
2381 Returns:
2382 The encoded data.
2383 """
2384 ...
2386 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2387 def get_json_format(cls) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2388 """Get the JSON format for the encoded data.
2390 Returns:
2391 The JSON format for the encoded data.
2392 """
2393 ...
2396class Base64Encoder(EncoderProtocol): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2397 """Standard (non-URL-safe) Base64 encoder."""
2399 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2400 def decode(cls, data: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2401 """Decode the data from base64 encoded bytes to original bytes data.
2403 Args:
2404 data: The data to decode.
2406 Returns:
2407 The decoded data.
2408 """
2409 try: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2410 return base64.b64decode(data) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2411 except ValueError as e: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2412 raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2414 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2415 def encode(cls, value: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2416 """Encode the data from bytes to a base64 encoded bytes.
2418 Args:
2419 value: The data to encode.
2421 Returns:
2422 The encoded data.
2423 """
2424 return base64.b64encode(value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2426 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2427 def get_json_format(cls) -> Literal['base64']: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2428 """Get the JSON format for the encoded data.
2430 Returns:
2431 The JSON format for the encoded data.
2432 """
2433 return 'base64' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2436class Base64UrlEncoder(EncoderProtocol): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2437 """URL-safe Base64 encoder."""
2439 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2440 def decode(cls, data: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2441 """Decode the data from base64 encoded bytes to original bytes data.
2443 Args:
2444 data: The data to decode.
2446 Returns:
2447 The decoded data.
2448 """
2449 try: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2450 return base64.urlsafe_b64decode(data) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2451 except ValueError as e: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2452 raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2454 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2455 def encode(cls, value: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2456 """Encode the data from bytes to a base64 encoded bytes.
2458 Args:
2459 value: The data to encode.
2461 Returns:
2462 The encoded data.
2463 """
2464 return base64.urlsafe_b64encode(value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2466 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2467 def get_json_format(cls) -> Literal['base64url']: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2468 """Get the JSON format for the encoded data.
2470 Returns:
2471 The JSON format for the encoded data.
2472 """
2473 return 'base64url' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2476@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2477class EncodedBytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2478 """A bytes type that is encoded and decoded using the specified encoder.
2480 `EncodedBytes` needs an encoder that implements `EncoderProtocol` to operate.
2482 ```python
2483 from typing import Annotated
2485 from pydantic import BaseModel, EncodedBytes, EncoderProtocol, ValidationError
2487 class MyEncoder(EncoderProtocol):
2488 @classmethod
2489 def decode(cls, data: bytes) -> bytes:
2490 if data == b'**undecodable**':
2491 raise ValueError('Cannot decode data')
2492 return data[13:]
2494 @classmethod
2495 def encode(cls, value: bytes) -> bytes:
2496 return b'**encoded**: ' + value
2498 @classmethod
2499 def get_json_format(cls) -> str:
2500 return 'my-encoder'
2502 MyEncodedBytes = Annotated[bytes, EncodedBytes(encoder=MyEncoder)]
2504 class Model(BaseModel):
2505 my_encoded_bytes: MyEncodedBytes
2507 # Initialize the model with encoded data
2508 m = Model(my_encoded_bytes=b'**encoded**: some bytes')
2510 # Access decoded value
2511 print(m.my_encoded_bytes)
2512 #> b'some bytes'
2514 # Serialize into the encoded form
2515 print(m.model_dump())
2516 #> {'my_encoded_bytes': b'**encoded**: some bytes'}
2518 # Validate encoded data
2519 try:
2520 Model(my_encoded_bytes=b'**undecodable**')
2521 except ValidationError as e:
2522 print(e)
2523 '''
2524 1 validation error for Model
2525 my_encoded_bytes
2526 Value error, Cannot decode data [type=value_error, input_value=b'**undecodable**', input_type=bytes]
2527 '''
2528 ```
2529 """
2531 encoder: type[EncoderProtocol] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2533 def __get_pydantic_json_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2534 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2535 ) -> JsonSchemaValue:
2536 field_schema = handler(core_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2537 field_schema.update(type='string', format=self.encoder.get_json_format()) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2538 return field_schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2540 def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2541 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2542 _check_annotated_type(schema['type'], 'bytes', self.__class__.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2543 return core_schema.with_info_after_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2544 function=self.decode,
2545 schema=schema,
2546 serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode),
2547 )
2549 def decode(self, data: bytes, _: core_schema.ValidationInfo) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2550 """Decode the data using the specified encoder.
2552 Args:
2553 data: The data to decode.
2555 Returns:
2556 The decoded data.
2557 """
2558 return self.encoder.decode(data) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2560 def encode(self, value: bytes) -> bytes: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2561 """Encode the data using the specified encoder.
2563 Args:
2564 value: The data to encode.
2566 Returns:
2567 The encoded data.
2568 """
2569 return self.encoder.encode(value) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2571 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2572 return hash(self.encoder) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2575@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2576class EncodedStr: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2577 """A str type that is encoded and decoded using the specified encoder.
2579 `EncodedStr` needs an encoder that implements `EncoderProtocol` to operate.
2581 ```python
2582 from typing import Annotated
2584 from pydantic import BaseModel, EncodedStr, EncoderProtocol, ValidationError
2586 class MyEncoder(EncoderProtocol):
2587 @classmethod
2588 def decode(cls, data: bytes) -> bytes:
2589 if data == b'**undecodable**':
2590 raise ValueError('Cannot decode data')
2591 return data[13:]
2593 @classmethod
2594 def encode(cls, value: bytes) -> bytes:
2595 return b'**encoded**: ' + value
2597 @classmethod
2598 def get_json_format(cls) -> str:
2599 return 'my-encoder'
2601 MyEncodedStr = Annotated[str, EncodedStr(encoder=MyEncoder)]
2603 class Model(BaseModel):
2604 my_encoded_str: MyEncodedStr
2606 # Initialize the model with encoded data
2607 m = Model(my_encoded_str='**encoded**: some str')
2609 # Access decoded value
2610 print(m.my_encoded_str)
2611 #> some str
2613 # Serialize into the encoded form
2614 print(m.model_dump())
2615 #> {'my_encoded_str': '**encoded**: some str'}
2617 # Validate encoded data
2618 try:
2619 Model(my_encoded_str='**undecodable**')
2620 except ValidationError as e:
2621 print(e)
2622 '''
2623 1 validation error for Model
2624 my_encoded_str
2625 Value error, Cannot decode data [type=value_error, input_value='**undecodable**', input_type=str]
2626 '''
2627 ```
2628 """
2630 encoder: type[EncoderProtocol] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2632 def __get_pydantic_json_schema__( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2633 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2634 ) -> JsonSchemaValue:
2635 field_schema = handler(core_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2636 field_schema.update(type='string', format=self.encoder.get_json_format()) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2637 return field_schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2639 def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2640 schema = handler(source) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2641 _check_annotated_type(schema['type'], 'str', self.__class__.__name__) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2642 return core_schema.with_info_after_validator_function( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2643 function=self.decode_str,
2644 schema=schema,
2645 serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode_str),
2646 )
2648 def decode_str(self, data: str, _: core_schema.ValidationInfo) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2649 """Decode the data using the specified encoder.
2651 Args:
2652 data: The data to decode.
2654 Returns:
2655 The decoded data.
2656 """
2657 return self.encoder.decode(data.encode()).decode() 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2659 def encode_str(self, value: str) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2660 """Encode the data using the specified encoder.
2662 Args:
2663 value: The data to encode.
2665 Returns:
2666 The encoded data.
2667 """
2668 return self.encoder.encode(value.encode()).decode() # noqa: UP008 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2670 def __hash__(self) -> int: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2671 return hash(self.encoder) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2674Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64Encoder)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2675"""A bytes type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
2677Note:
2678 Under the hood, `Base64Bytes` uses the standard library `base64.b64encode` and `base64.b64decode` functions.
2680 As a result, attempting to decode url-safe base64 data using the `Base64Bytes` type may fail or produce an incorrect
2681 decoding.
2683Warning:
2684 In versions of Pydantic prior to v2.10, `Base64Bytes` used [`base64.encodebytes`][base64.encodebytes]
2685 and [`base64.decodebytes`][base64.decodebytes] functions. According to the [base64 documentation](https://docs.python.org/3/library/base64.html),
2686 these methods are considered legacy implementation, and thus, Pydantic v2.10+ now uses the modern
2687 [`base64.b64encode`][base64.b64encode] and [`base64.b64decode`][base64.b64decode] functions.
2689 If you'd still like to use these legacy encoders / decoders, you can achieve this by creating a custom annotated type,
2690 like follows:
2692 ```python
2693 import base64
2694 from typing import Annotated, Literal
2696 from pydantic_core import PydanticCustomError
2698 from pydantic import EncodedBytes, EncoderProtocol
2700 class LegacyBase64Encoder(EncoderProtocol):
2701 @classmethod
2702 def decode(cls, data: bytes) -> bytes:
2703 try:
2704 return base64.decodebytes(data)
2705 except ValueError as e:
2706 raise PydanticCustomError(
2707 'base64_decode',
2708 "Base64 decoding error: '{error}'",
2709 {'error': str(e)},
2710 )
2712 @classmethod
2713 def encode(cls, value: bytes) -> bytes:
2714 return base64.encodebytes(value)
2716 @classmethod
2717 def get_json_format(cls) -> Literal['base64']:
2718 return 'base64'
2720 LegacyBase64Bytes = Annotated[bytes, EncodedBytes(encoder=LegacyBase64Encoder)]
2721 ```
2723```python
2724from pydantic import Base64Bytes, BaseModel, ValidationError
2726class Model(BaseModel):
2727 base64_bytes: Base64Bytes
2729# Initialize the model with base64 data
2730m = Model(base64_bytes=b'VGhpcyBpcyB0aGUgd2F5')
2732# Access decoded value
2733print(m.base64_bytes)
2734#> b'This is the way'
2736# Serialize into the base64 form
2737print(m.model_dump())
2738#> {'base64_bytes': b'VGhpcyBpcyB0aGUgd2F5'}
2740# Validate base64 data
2741try:
2742 print(Model(base64_bytes=b'undecodable').base64_bytes)
2743except ValidationError as e:
2744 print(e)
2745 '''
2746 1 validation error for Model
2747 base64_bytes
2748 Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value=b'undecodable', input_type=bytes]
2749 '''
2750```
2751"""
2752Base64Str = Annotated[str, EncodedStr(encoder=Base64Encoder)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2753"""A str type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
2755Note:
2756 Under the hood, `Base64Str` uses the standard library `base64.b64encode` and `base64.b64decode` functions.
2758 As a result, attempting to decode url-safe base64 data using the `Base64Str` type may fail or produce an incorrect
2759 decoding.
2761Warning:
2762 In versions of Pydantic prior to v2.10, `Base64Str` used [`base64.encodebytes`][base64.encodebytes]
2763 and [`base64.decodebytes`][base64.decodebytes] functions. According to the [base64 documentation](https://docs.python.org/3/library/base64.html),
2764 these methods are considered legacy implementation, and thus, Pydantic v2.10+ now uses the modern
2765 [`base64.b64encode`][base64.b64encode] and [`base64.b64decode`][base64.b64decode] functions.
2767 See the [`Base64Bytes`][pydantic.types.Base64Bytes] type for more information on how to
2768 replicate the old behavior with the legacy encoders / decoders.
2770```python
2771from pydantic import Base64Str, BaseModel, ValidationError
2773class Model(BaseModel):
2774 base64_str: Base64Str
2776# Initialize the model with base64 data
2777m = Model(base64_str='VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y')
2779# Access decoded value
2780print(m.base64_str)
2781#> These aren't the droids you're looking for
2783# Serialize into the base64 form
2784print(m.model_dump())
2785#> {'base64_str': 'VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y'}
2787# Validate base64 data
2788try:
2789 print(Model(base64_str='undecodable').base64_str)
2790except ValidationError as e:
2791 print(e)
2792 '''
2793 1 validation error for Model
2794 base64_str
2795 Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value='undecodable', input_type=str]
2796 '''
2797```
2798"""
2799Base64UrlBytes = Annotated[bytes, EncodedBytes(encoder=Base64UrlEncoder)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2800"""A bytes type that is encoded and decoded using the URL-safe base64 encoder. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
2802Note:
2803 Under the hood, `Base64UrlBytes` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode`
2804 functions.
2806 As a result, the `Base64UrlBytes` type can be used to faithfully decode "vanilla" base64 data
2807 (using `'+'` and `'/'`).
2809```python
2810from pydantic import Base64UrlBytes, BaseModel
2812class Model(BaseModel):
2813 base64url_bytes: Base64UrlBytes
2815# Initialize the model with base64 data
2816m = Model(base64url_bytes=b'SHc_dHc-TXc==')
2817print(m)
2818#> base64url_bytes=b'Hw?tw>Mw'
2819```
2820"""
2821Base64UrlStr = Annotated[str, EncodedStr(encoder=Base64UrlEncoder)] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2822"""A str type that is encoded and decoded using the URL-safe base64 encoder. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
2824Note:
2825 Under the hood, `Base64UrlStr` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode`
2826 functions.
2828 As a result, the `Base64UrlStr` type can be used to faithfully decode "vanilla" base64 data (using `'+'` and `'/'`).
2830```python
2831from pydantic import Base64UrlStr, BaseModel
2833class Model(BaseModel):
2834 base64url_str: Base64UrlStr
2836# Initialize the model with base64 data
2837m = Model(base64url_str='SHc_dHc-TXc==')
2838print(m)
2839#> base64url_str='Hw?tw>Mw'
2840```
2841"""
2844__getattr__ = getattr_migration(__name__) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2847@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2848class GetPydanticSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2849 """!!! abstract "Usage Documentation"
2850 [Using `GetPydanticSchema` to Reduce Boilerplate](../concepts/types.md#using-getpydanticschema-to-reduce-boilerplate)
2852 A convenience class for creating an annotation that provides pydantic custom type hooks.
2854 This class is intended to eliminate the need to create a custom "marker" which defines the
2855 `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__` custom hook methods.
2857 For example, to have a field treated by type checkers as `int`, but by pydantic as `Any`, you can do:
2858 ```python
2859 from typing import Annotated, Any
2861 from pydantic import BaseModel, GetPydanticSchema
2863 HandleAsAny = GetPydanticSchema(lambda _s, h: h(Any))
2865 class Model(BaseModel):
2866 x: Annotated[int, HandleAsAny] # pydantic sees `x: Any`
2868 print(repr(Model(x='abc').x))
2869 #> 'abc'
2870 ```
2871 """
2873 get_pydantic_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2874 get_pydantic_json_schema: Callable[[Any, GetJsonSchemaHandler], JsonSchemaValue] | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2876 # Note: we may want to consider adding a convenience staticmethod `def for_type(type_: Any) -> GetPydanticSchema:`
2877 # which returns `GetPydanticSchema(lambda _s, h: h(type_))`
2879 if not TYPE_CHECKING: 2879 ↛ 2891line 2879 didn't jump to line 2891 because the condition on line 2879 was always true1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2880 # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
2882 def __getattr__(self, item: str) -> Any: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2883 """Use this rather than defining `__get_pydantic_core_schema__` etc. to reduce the number of nested calls."""
2884 if item == '__get_pydantic_core_schema__' and self.get_pydantic_core_schema: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2885 return self.get_pydantic_core_schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2886 elif item == '__get_pydantic_json_schema__' and self.get_pydantic_json_schema: 2886 ↛ 2887line 2886 didn't jump to line 2887 because the condition on line 2886 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2887 return self.get_pydantic_json_schema
2888 else:
2889 return object.__getattribute__(self, item) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2891 __hash__ = object.__hash__ 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2894@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2895class Tag: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2896 """Provides a way to specify the expected tag to use for a case of a (callable) discriminated union.
2898 Also provides a way to label a union case in error messages.
2900 When using a callable `Discriminator`, attach a `Tag` to each case in the `Union` to specify the tag that
2901 should be used to identify that case. For example, in the below example, the `Tag` is used to specify that
2902 if `get_discriminator_value` returns `'apple'`, the input should be validated as an `ApplePie`, and if it
2903 returns `'pumpkin'`, the input should be validated as a `PumpkinPie`.
2905 The primary role of the `Tag` here is to map the return value from the callable `Discriminator` function to
2906 the appropriate member of the `Union` in question.
2908 ```python
2909 from typing import Annotated, Any, Literal, Union
2911 from pydantic import BaseModel, Discriminator, Tag
2913 class Pie(BaseModel):
2914 time_to_cook: int
2915 num_ingredients: int
2917 class ApplePie(Pie):
2918 fruit: Literal['apple'] = 'apple'
2920 class PumpkinPie(Pie):
2921 filling: Literal['pumpkin'] = 'pumpkin'
2923 def get_discriminator_value(v: Any) -> str:
2924 if isinstance(v, dict):
2925 return v.get('fruit', v.get('filling'))
2926 return getattr(v, 'fruit', getattr(v, 'filling', None))
2928 class ThanksgivingDinner(BaseModel):
2929 dessert: Annotated[
2930 Union[
2931 Annotated[ApplePie, Tag('apple')],
2932 Annotated[PumpkinPie, Tag('pumpkin')],
2933 ],
2934 Discriminator(get_discriminator_value),
2935 ]
2937 apple_variation = ThanksgivingDinner.model_validate(
2938 {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}}
2939 )
2940 print(repr(apple_variation))
2941 '''
2942 ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple'))
2943 '''
2945 pumpkin_variation = ThanksgivingDinner.model_validate(
2946 {
2947 'dessert': {
2948 'filling': 'pumpkin',
2949 'time_to_cook': 40,
2950 'num_ingredients': 6,
2951 }
2952 }
2953 )
2954 print(repr(pumpkin_variation))
2955 '''
2956 ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin'))
2957 '''
2958 ```
2960 !!! note
2961 You must specify a `Tag` for every case in a `Tag` that is associated with a
2962 callable `Discriminator`. Failing to do so will result in a `PydanticUserError` with code
2963 [`callable-discriminator-no-tag`](../errors/usage_errors.md#callable-discriminator-no-tag).
2965 See the [Discriminated Unions] concepts docs for more details on how to use `Tag`s.
2967 [Discriminated Unions]: ../concepts/unions.md#discriminated-unions
2968 """
2970 tag: str 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2972 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2973 schema = handler(source_type) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2974 metadata = cast('CoreMetadata', schema.setdefault('metadata', {})) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2975 metadata['pydantic_internal_union_tag_key'] = self.tag 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2976 return schema 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
2979@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2980class Discriminator: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
2981 """!!! abstract "Usage Documentation"
2982 [Discriminated Unions with `Callable` `Discriminator`](../concepts/unions.md#discriminated-unions-with-callable-discriminator)
2984 Provides a way to use a custom callable as the way to extract the value of a union discriminator.
2986 This allows you to get validation behavior like you'd get from `Field(discriminator=<field_name>)`,
2987 but without needing to have a single shared field across all the union choices. This also makes it
2988 possible to handle unions of models and primitive types with discriminated-union-style validation errors.
2989 Finally, this allows you to use a custom callable as the way to identify which member of a union a value
2990 belongs to, while still seeing all the performance benefits of a discriminated union.
2992 Consider this example, which is much more performant with the use of `Discriminator` and thus a `TaggedUnion`
2993 than it would be as a normal `Union`.
2995 ```python
2996 from typing import Annotated, Any, Literal, Union
2998 from pydantic import BaseModel, Discriminator, Tag
3000 class Pie(BaseModel):
3001 time_to_cook: int
3002 num_ingredients: int
3004 class ApplePie(Pie):
3005 fruit: Literal['apple'] = 'apple'
3007 class PumpkinPie(Pie):
3008 filling: Literal['pumpkin'] = 'pumpkin'
3010 def get_discriminator_value(v: Any) -> str:
3011 if isinstance(v, dict):
3012 return v.get('fruit', v.get('filling'))
3013 return getattr(v, 'fruit', getattr(v, 'filling', None))
3015 class ThanksgivingDinner(BaseModel):
3016 dessert: Annotated[
3017 Union[
3018 Annotated[ApplePie, Tag('apple')],
3019 Annotated[PumpkinPie, Tag('pumpkin')],
3020 ],
3021 Discriminator(get_discriminator_value),
3022 ]
3024 apple_variation = ThanksgivingDinner.model_validate(
3025 {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}}
3026 )
3027 print(repr(apple_variation))
3028 '''
3029 ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple'))
3030 '''
3032 pumpkin_variation = ThanksgivingDinner.model_validate(
3033 {
3034 'dessert': {
3035 'filling': 'pumpkin',
3036 'time_to_cook': 40,
3037 'num_ingredients': 6,
3038 }
3039 }
3040 )
3041 print(repr(pumpkin_variation))
3042 '''
3043 ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin'))
3044 '''
3045 ```
3047 See the [Discriminated Unions] concepts docs for more details on how to use `Discriminator`s.
3049 [Discriminated Unions]: ../concepts/unions.md#discriminated-unions
3050 """
3052 discriminator: str | Callable[[Any], Hashable] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3053 """The callable or field name for discriminating the type in a tagged union. 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
3055 A `Callable` discriminator must extract the value of the discriminator from the input.
3056 A `str` discriminator must be the name of a field to discriminate against.
3057 """
3058 custom_error_type: str | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3059 """Type to use in [custom errors](../errors/errors.md) replacing the standard discriminated union 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
3060 validation errors.
3061 """
3062 custom_error_message: str | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3063 """Message to use in custom errors.""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
3064 custom_error_context: dict[str, int | str | float] | None = None 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3065 """Context to use in custom errors.""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
3067 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3068 if not is_union_origin(get_origin(source_type)): 3068 ↛ 3069line 3068 didn't jump to line 3069 because the condition on line 3068 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3069 raise TypeError(f'{type(self).__name__} must be used with a Union type, not {source_type}')
3071 if isinstance(self.discriminator, str): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3072 from pydantic import Field 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3074 return handler(Annotated[source_type, Field(discriminator=self.discriminator)]) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3075 else:
3076 original_schema = handler(source_type) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3077 return self._convert_schema(original_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3079 def _convert_schema(self, original_schema: core_schema.CoreSchema) -> core_schema.TaggedUnionSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3080 if original_schema['type'] != 'union': 3080 ↛ 3085line 3080 didn't jump to line 3085 because the condition on line 3080 was never true1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3081 # This likely indicates that the schema was a single-item union that was simplified.
3082 # In this case, we do the same thing we do in
3083 # `pydantic._internal._discriminated_union._ApplyInferredDiscriminator._apply_to_root`, namely,
3084 # package the generated schema back into a single-item union.
3085 original_schema = core_schema.union_schema([original_schema])
3087 tagged_union_choices = {} 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3088 for choice in original_schema['choices']: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3089 tag = None 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3090 if isinstance(choice, tuple): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3091 choice, tag = choice 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3092 metadata = cast('CoreMetadata | None', choice.get('metadata')) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3093 if metadata is not None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3094 tag = metadata.get('pydantic_internal_union_tag_key') or tag 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3095 if tag is None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3096 raise PydanticUserError( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3097 f'`Tag` not provided for choice {choice} used with `Discriminator`',
3098 code='callable-discriminator-no-tag',
3099 )
3100 tagged_union_choices[tag] = choice 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3102 # Have to do these verbose checks to ensure falsy values ('' and {}) don't get ignored
3103 custom_error_type = self.custom_error_type 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3104 if custom_error_type is None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3105 custom_error_type = original_schema.get('custom_error_type') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3107 custom_error_message = self.custom_error_message 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3108 if custom_error_message is None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3109 custom_error_message = original_schema.get('custom_error_message') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3111 custom_error_context = self.custom_error_context 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3112 if custom_error_context is None: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3113 custom_error_context = original_schema.get('custom_error_context') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3115 custom_error_type = original_schema.get('custom_error_type') if custom_error_type is None else custom_error_type 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3116 return core_schema.tagged_union_schema( 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3117 tagged_union_choices,
3118 self.discriminator,
3119 custom_error_type=custom_error_type,
3120 custom_error_message=custom_error_message,
3121 custom_error_context=custom_error_context,
3122 strict=original_schema.get('strict'),
3123 ref=original_schema.get('ref'),
3124 metadata=original_schema.get('metadata'),
3125 serialization=original_schema.get('serialization'),
3126 )
3129_JSON_TYPES = {int, float, str, bool, list, dict, type(None)} 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3132def _get_type_name(x: Any) -> str: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3133 type_ = type(x) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3134 if type_ in _JSON_TYPES: 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3135 return type_.__name__ 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3137 # Handle proper subclasses; note we don't need to handle None or bool here
3138 if isinstance(x, int): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3139 return 'int' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3140 if isinstance(x, float): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3141 return 'float' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3142 if isinstance(x, str): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3143 return 'str' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3144 if isinstance(x, list): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3145 return 'list' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3146 if isinstance(x, dict): 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3147 return 'dict' 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3149 # Fail by returning the type's actual name
3150 return getattr(type_, '__name__', '<no type name>') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3153class _AllowAnyJson: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3154 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3155 def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3156 python_schema = handler(source_type) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3157 return core_schema.json_or_python_schema(json_schema=core_schema.any_schema(), python_schema=python_schema) 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3160if TYPE_CHECKING: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3161 # This seems to only be necessary for mypy
3162 JsonValue: TypeAlias = Union[
3163 list['JsonValue'],
3164 dict[str, 'JsonValue'],
3165 str,
3166 bool,
3167 int,
3168 float,
3169 None,
3170 ]
3171 """A `JsonValue` is used to represent a value that can be serialized to JSON.
3173 It may be one of:
3175 * `list['JsonValue']`
3176 * `dict[str, 'JsonValue']`
3177 * `str`
3178 * `bool`
3179 * `int`
3180 * `float`
3181 * `None`
3183 The following example demonstrates how to use `JsonValue` to validate JSON data,
3184 and what kind of errors to expect when input data is not json serializable.
3186 ```python
3187 import json
3189 from pydantic import BaseModel, JsonValue, ValidationError
3191 class Model(BaseModel):
3192 j: JsonValue
3194 valid_json_data = {'j': {'a': {'b': {'c': 1, 'd': [2, None]}}}}
3195 invalid_json_data = {'j': {'a': {'b': ...}}}
3197 print(repr(Model.model_validate(valid_json_data)))
3198 #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}})
3199 print(repr(Model.model_validate_json(json.dumps(valid_json_data))))
3200 #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}})
3202 try:
3203 Model.model_validate(invalid_json_data)
3204 except ValidationError as e:
3205 print(e)
3206 '''
3207 1 validation error for Model
3208 j.dict.a.dict.b
3209 input was not a valid JSON value [type=invalid-json-value, input_value=Ellipsis, input_type=ellipsis]
3210 '''
3211 ```
3212 """
3214else:
3215 JsonValue = TypeAliasType( 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3216 'JsonValue',
3217 Annotated[
3218 Union[
3219 Annotated[list['JsonValue'], Tag('list')],
3220 Annotated[dict[str, 'JsonValue'], Tag('dict')],
3221 Annotated[str, Tag('str')],
3222 Annotated[bool, Tag('bool')],
3223 Annotated[int, Tag('int')],
3224 Annotated[float, Tag('float')],
3225 Annotated[None, Tag('NoneType')],
3226 ],
3227 Discriminator(
3228 _get_type_name,
3229 custom_error_type='invalid-json-value',
3230 custom_error_message='input was not a valid JSON value',
3231 ),
3232 _AllowAnyJson,
3233 ],
3234 )
3237class _OnErrorOmit: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3238 @classmethod 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3239 def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3240 # there is no actual default value here but we use with_default_schema since it already has the on_error
3241 # behavior implemented and it would be no more efficient to implement it on every other validator
3242 # or as a standalone validator
3243 return core_schema.with_default_schema(schema=handler(source_type), on_error='omit') 1CDbcdefghijEaFGklmnopqrsHItuvwxyzAB
3246OnErrorOmit = Annotated[T, _OnErrorOmit] 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3247""" 1bcdefghijaklmnopqrsJKLMNOtuvwxyzAB
3248When used as an item in a list, the key type in a dict, optional values of a TypedDict, etc.
3249this annotation omits the item from the iteration if there is any error validating it.
3250That is, instead of a [`ValidationError`][pydantic_core.ValidationError] being propagated up and the entire iterable being discarded
3251any invalid items are discarded and the valid ones are returned.
3252"""
3255@_dataclasses.dataclass 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3256class FailFast(_fields.PydanticMetadata, BaseMetadata): 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB
3257 """A `FailFast` annotation can be used to specify that validation should stop at the first error.
3259 This can be useful when you want to validate a large amount of data and you only need to know if it's valid or not.
3261 You might want to enable this setting if you want to validate your data faster (basically, if you use this,
3262 validation will be more performant with the caveat that you get less information).
3264 ```python
3265 from typing import Annotated
3267 from pydantic import BaseModel, FailFast, ValidationError
3269 class Model(BaseModel):
3270 x: Annotated[list[int], FailFast()]
3272 # This will raise a single error for the first invalid value and stop validation
3273 try:
3274 obj = Model(x=[1, 2, 'a', 4, 5, 'b', 7, 8, 9, 'c'])
3275 except ValidationError as e:
3276 print(e)
3277 '''
3278 1 validation error for Model
3279 x.2
3280 Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
3281 '''
3282 ```
3283 """
3285 fail_fast: bool = True 1CDbcdefghijEaFGklmnopqrsPJKLMNOHItuvwxyzAB