Coverage for pydantic/types.py: 97.64%

660 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-07-01 21:48 +0000

1"""The types module contains custom types used by pydantic.""" 

2 

3from __future__ import annotations as _annotations 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

4 

5import base64 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

6import dataclasses as _dataclasses 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

7import re 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

8from collections.abc import Hashable, Iterator 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

9from datetime import date, datetime 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

10from decimal import Decimal 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

11from enum import Enum 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

12from pathlib import Path 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

13from re import Pattern 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

14from types import ModuleType 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

15from typing import ( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

28 

29import annotated_types 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

30from annotated_types import BaseMetadata, MaxLen, MinLen 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

31from pydantic_core import CoreSchema, PydanticCustomError, SchemaSerializer, core_schema 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

32from typing_extensions import Protocol, TypeAlias, TypeAliasType, deprecated, get_args, get_origin 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

33from typing_inspection.introspection import is_union_origin 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

34 

35from ._internal import _fields, _internal_dataclass, _utils, _validators 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

36from ._migration import getattr_migration 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

37from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

38from .errors import PydanticUserError 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

39from .json_schema import JsonSchemaValue 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

40from .warnings import PydanticDeprecatedSince20 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

41 

42if TYPE_CHECKING: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

43 from ._internal._core_metadata import CoreMetadata 

44 

45__all__ = ( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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) 

111 

112 

113T = TypeVar('T') 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

114 

115 

116@_dataclasses.dataclass 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

117class Strict(_fields.PydanticMetadata, BaseMetadata): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

118 """!!! abstract "Usage Documentation" 

119 [Strict Mode with `Annotated` `Strict`](../concepts/strict_mode.md#strict-mode-with-annotated-strict) 

120 

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. 

123 

124 Attributes: 

125 strict: Whether to validate the field in strict mode. 

126 

127 Example: 

128 ```python 

129 from typing import Annotated 

130 

131 from pydantic.types import Strict 

132 

133 StrictBool = Annotated[bool, Strict()] 

134 ``` 

135 """ 

136 

137 strict: bool = True 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

138 

139 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

140 return hash(self.strict) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

141 

142 

143# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

144 

145StrictBool = Annotated[bool, Strict()] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

146"""A boolean that must be either ``True`` or ``False``.""" 1abcdefghijklmnopqrsJtuvwxyzAB

147 

148# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

149 

150 

151def conint( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

165 

166 This function will be **deprecated** in Pydantic 3.0. 

167 

168 The reason is that `conint` returns a type, which doesn't play well with static analysis tools. 

169 

170 === ":x: Don't do this" 

171 ```python 

172 from pydantic import BaseModel, conint 

173 

174 class Foo(BaseModel): 

175 bar: conint(strict=True, gt=0) 

176 ``` 

177 

178 === ":white_check_mark: Do this" 

179 ```python 

180 from typing import Annotated 

181 

182 from pydantic import BaseModel, Field 

183 

184 class Foo(BaseModel): 

185 bar: Annotated[int, Field(strict=True, gt=0)] 

186 ``` 

187 

188 A wrapper around `int` that allows for additional constraints. 

189 

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. 

197 

198 Returns: 

199 The wrapped integer type. 

200 

201 ```python 

202 from pydantic import BaseModel, ValidationError, conint 

203 

204 class ConstrainedExample(BaseModel): 

205 constrained_int: conint(gt=1) 

206 

207 m = ConstrainedExample(constrained_int=2) 

208 print(repr(m)) 

209 #> ConstrainedExample(constrained_int=2) 

210 

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 ``` 

228 

229 """ # noqa: D212 

230 return Annotated[ # pyright: ignore[reportReturnType] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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 ] 

236 

237 

238PositiveInt = Annotated[int, annotated_types.Gt(0)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

239"""An integer that must be greater than zero. 1abcdefghijklmnopqrsJtuvwxyzAB

240 

241```python 

242from pydantic import BaseModel, PositiveInt, ValidationError 

243 

244class Model(BaseModel): 

245 positive_int: PositiveInt 

246 

247m = Model(positive_int=1) 

248print(repr(m)) 

249#> Model(positive_int=1) 

250 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

270"""An integer that must be less than zero. 1abcdefghijklmnopqrsJtuvwxyzAB

271 

272```python 

273from pydantic import BaseModel, NegativeInt, ValidationError 

274 

275class Model(BaseModel): 

276 negative_int: NegativeInt 

277 

278m = Model(negative_int=-1) 

279print(repr(m)) 

280#> Model(negative_int=-1) 

281 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

301"""An integer that must be less than or equal to zero. 1abcdefghijklmnopqrsJtuvwxyzAB

302 

303```python 

304from pydantic import BaseModel, NonPositiveInt, ValidationError 

305 

306class Model(BaseModel): 

307 non_positive_int: NonPositiveInt 

308 

309m = Model(non_positive_int=0) 

310print(repr(m)) 

311#> Model(non_positive_int=0) 

312 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

332"""An integer that must be greater than or equal to zero. 1abcdefghijklmnopqrsJtuvwxyzAB

333 

334```python 

335from pydantic import BaseModel, NonNegativeInt, ValidationError 

336 

337class Model(BaseModel): 

338 non_negative_int: NonNegativeInt 

339 

340m = Model(non_negative_int=0) 

341print(repr(m)) 

342#> Model(non_negative_int=0) 

343 

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()] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

363"""An integer that must be validated in strict mode. 1abcdefghijklmnopqrsJtuvwxyzAB

364 

365```python 

366from pydantic import BaseModel, StrictInt, ValidationError 

367 

368class StrictIntModel(BaseModel): 

369 strict_int: StrictInt 

370 

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""" 

382 

383# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

384 

385 

386@_dataclasses.dataclass 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

387class AllowInfNan(_fields.PydanticMetadata): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

388 """A field metadata class to indicate that a field should allow `-inf`, `inf`, and `nan`. 

389 

390 Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below. 

391 

392 Attributes: 

393 allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`. Defaults to `True`. 

394 

395 Example: 

396 ```python 

397 from typing import Annotated 

398 

399 from pydantic.types import AllowInfNan 

400 

401 LaxFloat = Annotated[float, AllowInfNan()] 

402 ``` 

403 """ 

404 

405 allow_inf_nan: bool = True 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

406 

407 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

408 return hash(self.allow_inf_nan) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

409 

410 

411def confloat( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

426 

427 This function will be **deprecated** in Pydantic 3.0. 

428 

429 The reason is that `confloat` returns a type, which doesn't play well with static analysis tools. 

430 

431 === ":x: Don't do this" 

432 ```python 

433 from pydantic import BaseModel, confloat 

434 

435 class Foo(BaseModel): 

436 bar: confloat(strict=True, gt=0) 

437 ``` 

438 

439 === ":white_check_mark: Do this" 

440 ```python 

441 from typing import Annotated 

442 

443 from pydantic import BaseModel, Field 

444 

445 class Foo(BaseModel): 

446 bar: Annotated[float, Field(strict=True, gt=0)] 

447 ``` 

448 

449 A wrapper around `float` that allows for additional constraints. 

450 

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`. 

459 

460 Returns: 

461 The wrapped float type. 

462 

463 ```python 

464 from pydantic import BaseModel, ValidationError, confloat 

465 

466 class ConstrainedExample(BaseModel): 

467 constrained_float: confloat(gt=1.0) 

468 

469 m = ConstrainedExample(constrained_float=1.1) 

470 print(repr(m)) 

471 #> ConstrainedExample(constrained_float=1.1) 

472 

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] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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 ] 

498 

499 

500PositiveFloat = Annotated[float, annotated_types.Gt(0)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

501"""A float that must be greater than zero. 1abcdefghijklmnopqrsJtuvwxyzAB

502 

503```python 

504from pydantic import BaseModel, PositiveFloat, ValidationError 

505 

506class Model(BaseModel): 

507 positive_float: PositiveFloat 

508 

509m = Model(positive_float=1.0) 

510print(repr(m)) 

511#> Model(positive_float=1.0) 

512 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

532"""A float that must be less than zero. 1abcdefghijklmnopqrsJtuvwxyzAB

533 

534```python 

535from pydantic import BaseModel, NegativeFloat, ValidationError 

536 

537class Model(BaseModel): 

538 negative_float: NegativeFloat 

539 

540m = Model(negative_float=-1.0) 

541print(repr(m)) 

542#> Model(negative_float=-1.0) 

543 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

563"""A float that must be less than or equal to zero. 1abcdefghijklmnopqrsJtuvwxyzAB

564 

565```python 

566from pydantic import BaseModel, NonPositiveFloat, ValidationError 

567 

568class Model(BaseModel): 

569 non_positive_float: NonPositiveFloat 

570 

571m = Model(non_positive_float=0.0) 

572print(repr(m)) 

573#> Model(non_positive_float=0.0) 

574 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

594"""A float that must be greater than or equal to zero. 1abcdefghijklmnopqrsJtuvwxyzAB

595 

596```python 

597from pydantic import BaseModel, NonNegativeFloat, ValidationError 

598 

599class Model(BaseModel): 

600 non_negative_float: NonNegativeFloat 

601 

602m = Model(non_negative_float=0.0) 

603print(repr(m)) 

604#> Model(non_negative_float=0.0) 

605 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

625"""A float that must be validated in strict mode. 1abcdefghijklmnopqrsJtuvwxyzAB

626 

627```python 

628from pydantic import BaseModel, StrictFloat, ValidationError 

629 

630class StrictFloatModel(BaseModel): 

631 strict_float: StrictFloat 

632 

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)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

645"""A float that must be finite (not ``-inf``, ``inf``, or ``nan``). 1abcdefghijklmnopqrsJtuvwxyzAB

646 

647```python 

648from pydantic import BaseModel, FiniteFloat 

649 

650class Model(BaseModel): 

651 finite: FiniteFloat 

652 

653m = Model(finite=1.0) 

654print(m) 

655#> finite=1.0 

656``` 

657""" 

658 

659 

660# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

661 

662 

663def conbytes( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

670 

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. 

675 

676 Returns: 

677 The wrapped bytes type. 

678 """ 

679 return Annotated[ # pyright: ignore[reportReturnType] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

680 bytes, 

681 Strict(strict) if strict is not None else None, 

682 annotated_types.Len(min_length or 0, max_length), 

683 ] 

684 

685 

686StrictBytes = Annotated[bytes, Strict()] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

687"""A bytes that must be validated in strict mode.""" 1abcdefghijklmnopqrsJtuvwxyzAB

688 

689 

690# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

691 

692 

693@_dataclasses.dataclass(frozen=True) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

694class StringConstraints(annotated_types.GroupedMetadata): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

695 """!!! abstract "Usage Documentation" 

696 [`StringConstraints`](../concepts/fields.md#string-constraints) 

697 

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. 

700 

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. 

709 

710 Example: 

711 ```python 

712 from typing import Annotated 

713 

714 from pydantic.types import StringConstraints 

715 

716 ConstrainedStr = Annotated[str, StringConstraints(min_length=1, max_length=10)] 

717 ``` 

718 """ 

719 

720 strip_whitespace: bool | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

721 to_upper: bool | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

722 to_lower: bool | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

723 strict: bool | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

724 min_length: int | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

725 max_length: int | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

726 pattern: str | Pattern[str] | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

727 

728 def __iter__(self) -> Iterator[BaseMetadata]: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

729 if self.min_length is not None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

730 yield MinLen(self.min_length) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

731 if self.max_length is not None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

732 yield MaxLen(self.max_length) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

733 if self.strict is not None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

734 yield Strict(self.strict) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

735 if ( 1CDEj

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( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

742 strip_whitespace=self.strip_whitespace, 

743 to_upper=self.to_upper, 

744 to_lower=self.to_lower, 

745 pattern=self.pattern, 

746 ) 

747 

748 

749def constr( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

764 

765 This function will be **deprecated** in Pydantic 3.0. 

766 

767 The reason is that `constr` returns a type, which doesn't play well with static analysis tools. 

768 

769 === ":x: Don't do this" 

770 ```python 

771 from pydantic import BaseModel, constr 

772 

773 class Foo(BaseModel): 

774 bar: constr(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$') 

775 ``` 

776 

777 === ":white_check_mark: Do this" 

778 ```python 

779 from typing import Annotated 

780 

781 from pydantic import BaseModel, StringConstraints 

782 

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 ``` 

791 

792 A wrapper around `str` that allows for additional constraints. 

793 

794 ```python 

795 from pydantic import BaseModel, constr 

796 

797 class Foo(BaseModel): 

798 bar: constr(strip_whitespace=True, to_upper=True) 

799 

800 foo = Foo(bar=' hello ') 

801 print(foo) 

802 #> bar='HELLO' 

803 ``` 

804 

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. 

813 

814 Returns: 

815 The wrapped string type. 

816 """ # noqa: D212 

817 return Annotated[ # pyright: ignore[reportReturnType] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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 ] 

829 

830 

831StrictStr = Annotated[str, Strict()] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

832"""A string that must be validated in strict mode.""" 1abcdefghijklmnopqrsJtuvwxyzAB

833 

834 

835# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ COLLECTION TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

836HashableItemType = TypeVar('HashableItemType', bound=Hashable) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

837 

838 

839def conset( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

843 

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. 

848 

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] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

853 

854 

855def confrozenset( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

859 

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. 

864 

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] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

869 

870 

871AnyItemType = TypeVar('AnyItemType') 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

872 

873 

874def conlist( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

882 

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. 

891 

892 Returns: 

893 The wrapped list type. 

894 """ 

895 if unique_items is not None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

896 raise PydanticUserError( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

904 

905 

906# ~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT STRING TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

907 

908AnyType = TypeVar('AnyType') 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

909if TYPE_CHECKING: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

910 ImportString = Annotated[AnyType, ...] 

911else: 

912 

913 class ImportString: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

914 """A type that can be used to import a Python object from a string. 

915 

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. 

920 

921 On model instantiation, pointers will be evaluated and imported. There is 

922 some nuance to this behavior, demonstrated in the examples below. 

923 

924 ```python 

925 import math 

926 

927 from pydantic import BaseModel, Field, ImportString, ValidationError 

928 

929 class ImportThings(BaseModel): 

930 obj: ImportString 

931 

932 # A string value will cause an automatic import 

933 my_cos = ImportThings(obj='math.cos') 

934 

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 

938 

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 ''' 

949 

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 

955 

956 # You can set default field value either as Python object: 

957 class ImportThingsDefaultPyObj(BaseModel): 

958 obj: ImportString = math.cos 

959 

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) 

963 

964 my_cos_default1 = ImportThingsDefaultPyObj() 

965 my_cos_default2 = ImportThingsDefaultString() 

966 assert my_cos_default1.obj == my_cos_default2.obj == math.cos 

967 

968 # note: this will not work! 

969 class ImportThingsMissingValidateDefault(BaseModel): 

970 obj: ImportString = 'math.cos' 

971 

972 my_cos_default3 = ImportThingsMissingValidateDefault() 

973 assert my_cos_default3.obj == 'math.cos' # just string, not evaluated 

974 ``` 

975 

976 Serializing an `ImportString` type to json is also possible. 

977 

978 ```python 

979 from pydantic import BaseModel, ImportString 

980 

981 class ImportThings(BaseModel): 

982 obj: ImportString 

983 

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 """ 

992 

993 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

994 def __class_getitem__(cls, item: AnyType) -> AnyType: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

995 return Annotated[item, cls()] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

996 

997 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

998 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1002 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1003 # Treat bare usage of ImportString (`schema is None`) as the same as ImportString[Any] 

1004 return core_schema.no_info_plain_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1005 function=_validators.import_string, serialization=serializer 

1006 ) 

1007 else: 

1008 return core_schema.no_info_before_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1009 function=_validators.import_string, schema=handler(source), serialization=serializer 

1010 ) 

1011 

1012 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1013 def __get_pydantic_json_schema__(cls, cs: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1014 return handler(core_schema.str_schema()) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1015 

1016 @staticmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1017 def _serialize(v: Any) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1018 if isinstance(v, ModuleType): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1019 return v.__name__ 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1020 elif hasattr(v, '__module__') and hasattr(v, '__name__'): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1021 return f'{v.__module__}.{v.__name__}' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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'): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1025 if v.name == '<stdout>': 1025 ↛ 1026line 1025 didn't jump to line 1026 because the condition on line 1025 was never true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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 true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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 true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1030 return 'sys.stderr' 

1031 else: 

1032 return v 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1033 

1034 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1035 return 'ImportString' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1036 

1037 

1038# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

1039 

1040 

1041def condecimal( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

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. 

1058 

1059 This function will be **deprecated** in Pydantic 3.0. 

1060 

1061 The reason is that `condecimal` returns a type, which doesn't play well with static analysis tools. 

1062 

1063 === ":x: Don't do this" 

1064 ```python 

1065 from pydantic import BaseModel, condecimal 

1066 

1067 class Foo(BaseModel): 

1068 bar: condecimal(strict=True, allow_inf_nan=True) 

1069 ``` 

1070 

1071 === ":white_check_mark: Do this" 

1072 ```python 

1073 from decimal import Decimal 

1074 from typing import Annotated 

1075 

1076 from pydantic import BaseModel, Field 

1077 

1078 class Foo(BaseModel): 

1079 bar: Annotated[Decimal, Field(strict=True, allow_inf_nan=True)] 

1080 ``` 

1081 

1082 A wrapper around Decimal that adds validation. 

1083 

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`. 

1094 

1095 ```python 

1096 from decimal import Decimal 

1097 

1098 from pydantic import BaseModel, ValidationError, condecimal 

1099 

1100 class ConstrainedExample(BaseModel): 

1101 constrained_decimal: condecimal(gt=Decimal('1.0')) 

1102 

1103 m = ConstrainedExample(constrained_decimal=Decimal('1.1')) 

1104 print(repr(m)) 

1105 #> ConstrainedExample(constrained_decimal=Decimal('1.1')) 

1106 

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] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

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 ] 

1133 

1134 

1135# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

1136 

1137 

1138@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1139class UuidVersion: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1140 """A field metadata class to indicate a [UUID](https://docs.python.org/3/library/uuid.html) version. 

1141 

1142 Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below. 

1143 

1144 Attributes: 

1145 uuid_version: The version of the UUID. Must be one of 1, 3, 4, 5, 6, 7 or 8. 

1146 

1147 Example: 

1148 ```python 

1149 from typing import Annotated 

1150 from uuid import UUID 

1151 

1152 from pydantic.types import UuidVersion 

1153 

1154 UUID1 = Annotated[UUID, UuidVersion(1)] 

1155 ``` 

1156 """ 

1157 

1158 uuid_version: Literal[1, 3, 4, 5, 6, 7, 8] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1159 

1160 def __get_pydantic_json_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1161 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

1162 ) -> JsonSchemaValue: 

1163 field_schema = handler(core_schema) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1164 field_schema.pop('anyOf', None) # remove the bytes/str union 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1165 field_schema.update(type='string', format=f'uuid{self.uuid_version}') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1166 return field_schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1167 

1168 def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1169 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1170 _check_annotated_type(schema['type'], 'uuid', self.__class__.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1171 schema['version'] = self.uuid_version # type: ignore 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1172 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1173 

1174 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1175 return hash(type(self.uuid_version)) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1176 

1177 

1178UUID1 = Annotated[UUID, UuidVersion(1)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1179"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 1. 1abcdefghijklmnopqrsJtuvwxyzAB

1180 

1181```python 

1182import uuid 

1183 

1184from pydantic import UUID1, BaseModel 

1185 

1186class Model(BaseModel): 

1187 uuid1: UUID1 

1188 

1189Model(uuid1=uuid.uuid1()) 

1190``` 

1191""" 

1192UUID3 = Annotated[UUID, UuidVersion(3)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1193"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 3. 1abcdefghijklmnopqrsJtuvwxyzAB

1194 

1195```python 

1196import uuid 

1197 

1198from pydantic import UUID3, BaseModel 

1199 

1200class Model(BaseModel): 

1201 uuid3: UUID3 

1202 

1203Model(uuid3=uuid.uuid3(uuid.NAMESPACE_DNS, 'pydantic.org')) 

1204``` 

1205""" 

1206UUID4 = Annotated[UUID, UuidVersion(4)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1207"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 4. 1abcdefghijklmnopqrsJtuvwxyzAB

1208 

1209```python 

1210import uuid 

1211 

1212from pydantic import UUID4, BaseModel 

1213 

1214class Model(BaseModel): 

1215 uuid4: UUID4 

1216 

1217Model(uuid4=uuid.uuid4()) 

1218``` 

1219""" 

1220UUID5 = Annotated[UUID, UuidVersion(5)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1221"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 5. 1abcdefghijklmnopqrsJtuvwxyzAB

1222 

1223```python 

1224import uuid 

1225 

1226from pydantic import UUID5, BaseModel 

1227 

1228class Model(BaseModel): 

1229 uuid5: UUID5 

1230 

1231Model(uuid5=uuid.uuid5(uuid.NAMESPACE_DNS, 'pydantic.org')) 

1232``` 

1233""" 

1234UUID6 = Annotated[UUID, UuidVersion(6)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1235"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 6. 1abcdefghijklmnopqrsJtuvwxyzAB

1236 

1237```python 

1238import uuid 

1239 

1240from pydantic import UUID6, BaseModel 

1241 

1242class Model(BaseModel): 

1243 uuid6: UUID6 

1244 

1245Model(uuid6=uuid.UUID('1efea953-c2d6-6790-aa0a-69db8c87df97')) 

1246``` 

1247""" 

1248UUID7 = Annotated[UUID, UuidVersion(7)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1249"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 7. 1abcdefghijklmnopqrsJtuvwxyzAB

1250 

1251```python 

1252import uuid 

1253 

1254from pydantic import UUID7, BaseModel 

1255 

1256class Model(BaseModel): 

1257 uuid7: UUID7 

1258 

1259Model(uuid7=uuid.UUID('0194fdcb-1c47-7a09-b52c-561154de0b4a')) 

1260``` 

1261""" 

1262UUID8 = Annotated[UUID, UuidVersion(8)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1263"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 8. 1abcdefghijklmnopqrsJtuvwxyzAB

1264 

1265```python 

1266import uuid 

1267 

1268from pydantic import UUID8, BaseModel 

1269 

1270class Model(BaseModel): 

1271 uuid8: UUID8 

1272 

1273Model(uuid8=uuid.UUID('81a0b92e-6078-8551-9c81-8ccb666bdab8')) 

1274``` 

1275""" 

1276 

1277# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

1278 

1279 

1280@_dataclasses.dataclass 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1281class PathType: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1282 path_type: Literal['file', 'dir', 'new', 'socket'] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1283 

1284 def __get_pydantic_json_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1285 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

1286 ) -> JsonSchemaValue: 

1287 field_schema = handler(core_schema) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1288 format_conversion = {'file': 'file-path', 'dir': 'directory-path'} 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1289 field_schema.update(format=format_conversion.get(self.path_type, 'path'), type='string') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1290 return field_schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1291 

1292 def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1293 function_lookup = { 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1294 'file': cast(core_schema.WithInfoValidatorFunction, self.validate_file), 

1295 'dir': cast(core_schema.WithInfoValidatorFunction, self.validate_directory), 

1296 'new': cast(core_schema.WithInfoValidatorFunction, self.validate_new), 

1297 'socket': cast(core_schema.WithInfoValidatorFunction, self.validate_socket), 

1298 } 

1299 

1300 return core_schema.with_info_after_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1301 function_lookup[self.path_type], 

1302 handler(source), 

1303 ) 

1304 

1305 @staticmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1306 def validate_file(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1307 if path.is_file(): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1308 return path 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1309 else: 

1310 raise PydanticCustomError('path_not_file', 'Path does not point to a file') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1311 

1312 @staticmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1313 def validate_socket(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1314 if path.is_socket(): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1315 return path 1CDabcdefghiEj

1316 else: 

1317 raise PydanticCustomError('path_not_socket', 'Path does not point to a socket') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1318 

1319 @staticmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1320 def validate_directory(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1321 if path.is_dir(): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1322 return path 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1323 else: 

1324 raise PydanticCustomError('path_not_directory', 'Path does not point to a directory') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1325 

1326 @staticmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1327 def validate_new(path: Path, _: core_schema.ValidationInfo) -> Path: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1328 if path.exists(): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1329 raise PydanticCustomError('path_exists', 'Path already exists') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1330 elif not path.parent.exists(): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1331 raise PydanticCustomError('parent_does_not_exist', 'Parent directory does not exist') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1332 else: 

1333 return path 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1334 

1335 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1336 return hash(type(self.path_type)) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1337 

1338 

1339FilePath = Annotated[Path, PathType('file')] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1340"""A path that must point to a file. 1abcdefghijklmnopqrsJtuvwxyzAB

1341 

1342```python 

1343from pathlib import Path 

1344 

1345from pydantic import BaseModel, FilePath, ValidationError 

1346 

1347class Model(BaseModel): 

1348 f: FilePath 

1349 

1350path = Path('text.txt') 

1351path.touch() 

1352m = Model(f='text.txt') 

1353print(m.model_dump()) 

1354#> {'f': PosixPath('text.txt')} 

1355path.unlink() 

1356 

1357path = Path('directory') 

1358path.mkdir(exist_ok=True) 

1359try: 

1360 Model(f='directory') # directory 

1361except ValidationError as e: 

1362 print(e) 

1363 ''' 

1364 1 validation error for Model 

1365 f 

1366 Path does not point to a file [type=path_not_file, input_value='directory', input_type=str] 

1367 ''' 

1368path.rmdir() 

1369 

1370try: 

1371 Model(f='not-exists-file') 

1372except ValidationError as e: 

1373 print(e) 

1374 ''' 

1375 1 validation error for Model 

1376 f 

1377 Path does not point to a file [type=path_not_file, input_value='not-exists-file', input_type=str] 

1378 ''' 

1379``` 

1380""" 

1381DirectoryPath = Annotated[Path, PathType('dir')] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1382"""A path that must point to a directory. 1abcdefghijklmnopqrsJtuvwxyzAB

1383 

1384```python 

1385from pathlib import Path 

1386 

1387from pydantic import BaseModel, DirectoryPath, ValidationError 

1388 

1389class Model(BaseModel): 

1390 f: DirectoryPath 

1391 

1392path = Path('directory/') 

1393path.mkdir() 

1394m = Model(f='directory/') 

1395print(m.model_dump()) 

1396#> {'f': PosixPath('directory')} 

1397path.rmdir() 

1398 

1399path = Path('file.txt') 

1400path.touch() 

1401try: 

1402 Model(f='file.txt') # file 

1403except ValidationError as e: 

1404 print(e) 

1405 ''' 

1406 1 validation error for Model 

1407 f 

1408 Path does not point to a directory [type=path_not_directory, input_value='file.txt', input_type=str] 

1409 ''' 

1410path.unlink() 

1411 

1412try: 

1413 Model(f='not-exists-directory') 

1414except ValidationError as e: 

1415 print(e) 

1416 ''' 

1417 1 validation error for Model 

1418 f 

1419 Path does not point to a directory [type=path_not_directory, input_value='not-exists-directory', input_type=str] 

1420 ''' 

1421``` 

1422""" 

1423NewPath = Annotated[Path, PathType('new')] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1424"""A path for a new file or directory that must not already exist. The parent directory must already exist.""" 1abcdefghijklmnopqrsJtuvwxyzAB

1425 

1426SocketPath = Annotated[Path, PathType('socket')] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1427"""A path to an existing socket file""" 1abcdefghijklmnopqrsJtuvwxyzAB

1428 

1429# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

1430 

1431if TYPE_CHECKING: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1432 # Json[list[str]] will be recognized by type checkers as list[str] 

1433 Json = Annotated[AnyType, ...] 

1434 

1435else: 

1436 

1437 class Json: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1438 """A special type wrapper which loads JSON before parsing. 

1439 

1440 You can use the `Json` data type to make Pydantic first load a raw JSON string before 

1441 validating the loaded data into the parametrized type: 

1442 

1443 ```python 

1444 from typing import Any 

1445 

1446 from pydantic import BaseModel, Json, ValidationError 

1447 

1448 class AnyJsonModel(BaseModel): 

1449 json_obj: Json[Any] 

1450 

1451 class ConstrainedJsonModel(BaseModel): 

1452 json_obj: Json[list[int]] 

1453 

1454 print(AnyJsonModel(json_obj='{"b": 1}')) 

1455 #> json_obj={'b': 1} 

1456 print(ConstrainedJsonModel(json_obj='[1, 2, 3]')) 

1457 #> json_obj=[1, 2, 3] 

1458 

1459 try: 

1460 ConstrainedJsonModel(json_obj=12) 

1461 except ValidationError as e: 

1462 print(e) 

1463 ''' 

1464 1 validation error for ConstrainedJsonModel 

1465 json_obj 

1466 JSON input should be string, bytes or bytearray [type=json_type, input_value=12, input_type=int] 

1467 ''' 

1468 

1469 try: 

1470 ConstrainedJsonModel(json_obj='[a, b]') 

1471 except ValidationError as e: 

1472 print(e) 

1473 ''' 

1474 1 validation error for ConstrainedJsonModel 

1475 json_obj 

1476 Invalid JSON: expected value at line 1 column 2 [type=json_invalid, input_value='[a, b]', input_type=str] 

1477 ''' 

1478 

1479 try: 

1480 ConstrainedJsonModel(json_obj='["a", "b"]') 

1481 except ValidationError as e: 

1482 print(e) 

1483 ''' 

1484 2 validation errors for ConstrainedJsonModel 

1485 json_obj.0 

1486 Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str] 

1487 json_obj.1 

1488 Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str] 

1489 ''' 

1490 ``` 

1491 

1492 When you dump the model using `model_dump` or `model_dump_json`, the dumped value will be the result of validation, 

1493 not the original JSON string. However, you can use the argument `round_trip=True` to get the original JSON string back: 

1494 

1495 ```python 

1496 from pydantic import BaseModel, Json 

1497 

1498 class ConstrainedJsonModel(BaseModel): 

1499 json_obj: Json[list[int]] 

1500 

1501 print(ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json()) 

1502 #> {"json_obj":[1,2,3]} 

1503 print( 

1504 ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json(round_trip=True) 

1505 ) 

1506 #> {"json_obj":"[1,2,3]"} 

1507 ``` 

1508 """ 

1509 

1510 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1511 def __class_getitem__(cls, item: AnyType) -> AnyType: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1512 return Annotated[item, cls()] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1513 

1514 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1515 def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1516 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1517 return core_schema.json_schema(None) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1518 else: 

1519 return core_schema.json_schema(handler(source)) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1520 

1521 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1522 return 'Json' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1523 

1524 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1525 return hash(type(self)) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1526 

1527 def __eq__(self, other: Any) -> bool: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1528 return type(other) is type(self) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1529 

1530 

1531# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

1532 

1533# The `Secret` class being conceptually immutable, make the type variable covariant: 

1534SecretType = TypeVar('SecretType', covariant=True) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1535 

1536 

1537class _SecretBase(Generic[SecretType]): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1538 def __init__(self, secret_value: SecretType) -> None: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1539 self._secret_value: SecretType = secret_value 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1540 

1541 def get_secret_value(self) -> SecretType: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1542 """Get the secret value. 

1543 

1544 Returns: 

1545 The secret value. 

1546 """ 

1547 return self._secret_value 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1548 

1549 def __eq__(self, other: Any) -> bool: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1550 return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value() 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1551 

1552 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1553 return hash(self.get_secret_value()) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1554 

1555 def __str__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1556 return str(self._display()) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1557 

1558 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1559 return f'{self.__class__.__name__}({self._display()!r})' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1560 

1561 def _display(self) -> str | bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1562 raise NotImplementedError 

1563 

1564 

1565def _serialize_secret(value: Secret[SecretType], info: core_schema.SerializationInfo) -> str | Secret[SecretType]: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1566 if info.mode == 'json': 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1567 return str(value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1568 else: 

1569 return value 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1570 

1571 

1572class Secret(_SecretBase[SecretType]): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1573 """A generic base class used for defining a field with sensitive information that you do not want to be visible in logging or tracebacks. 

1574 

1575 You may either directly parametrize `Secret` with a type, or subclass from `Secret` with a parametrized type. The benefit of subclassing 

1576 is that you can define a custom `_display` method, which will be used for `repr()` and `str()` methods. The examples below demonstrate both 

1577 ways of using `Secret` to create a new secret type. 

1578 

1579 1. Directly parametrizing `Secret` with a type: 

1580 

1581 ```python 

1582 from pydantic import BaseModel, Secret 

1583 

1584 SecretBool = Secret[bool] 

1585 

1586 class Model(BaseModel): 

1587 secret_bool: SecretBool 

1588 

1589 m = Model(secret_bool=True) 

1590 print(m.model_dump()) 

1591 #> {'secret_bool': Secret('**********')} 

1592 

1593 print(m.model_dump_json()) 

1594 #> {"secret_bool":"**********"} 

1595 

1596 print(m.secret_bool.get_secret_value()) 

1597 #> True 

1598 ``` 

1599 

1600 2. Subclassing from parametrized `Secret`: 

1601 

1602 ```python 

1603 from datetime import date 

1604 

1605 from pydantic import BaseModel, Secret 

1606 

1607 class SecretDate(Secret[date]): 

1608 def _display(self) -> str: 

1609 return '****/**/**' 

1610 

1611 class Model(BaseModel): 

1612 secret_date: SecretDate 

1613 

1614 m = Model(secret_date=date(2022, 1, 1)) 

1615 print(m.model_dump()) 

1616 #> {'secret_date': SecretDate('****/**/**')} 

1617 

1618 print(m.model_dump_json()) 

1619 #> {"secret_date":"****/**/**"} 

1620 

1621 print(m.secret_date.get_secret_value()) 

1622 #> 2022-01-01 

1623 ``` 

1624 

1625 The value returned by the `_display` method will be used for `repr()` and `str()`. 

1626 

1627 You can enforce constraints on the underlying type through annotations: 

1628 For example: 

1629 

1630 ```python 

1631 from typing import Annotated 

1632 

1633 from pydantic import BaseModel, Field, Secret, ValidationError 

1634 

1635 SecretPosInt = Secret[Annotated[int, Field(gt=0, strict=True)]] 

1636 

1637 class Model(BaseModel): 

1638 sensitive_int: SecretPosInt 

1639 

1640 m = Model(sensitive_int=42) 

1641 print(m.model_dump()) 

1642 #> {'sensitive_int': Secret('**********')} 

1643 

1644 try: 

1645 m = Model(sensitive_int=-42) # (1)! 

1646 except ValidationError as exc_info: 

1647 print(exc_info.errors(include_url=False, include_input=False)) 

1648 ''' 

1649 [ 

1650 { 

1651 'type': 'greater_than', 

1652 'loc': ('sensitive_int',), 

1653 'msg': 'Input should be greater than 0', 

1654 'ctx': {'gt': 0}, 

1655 } 

1656 ] 

1657 ''' 

1658 

1659 try: 

1660 m = Model(sensitive_int='42') # (2)! 

1661 except ValidationError as exc_info: 

1662 print(exc_info.errors(include_url=False, include_input=False)) 

1663 ''' 

1664 [ 

1665 { 

1666 'type': 'int_type', 

1667 'loc': ('sensitive_int',), 

1668 'msg': 'Input should be a valid integer', 

1669 } 

1670 ] 

1671 ''' 

1672 ``` 

1673 

1674 1. The input value is not greater than 0, so it raises a validation error. 

1675 2. The input value is not an integer, so it raises a validation error because the `SecretPosInt` type has strict mode enabled. 

1676 """ 

1677 

1678 def _display(self) -> str | bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1679 return '**********' if self.get_secret_value() else '' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1680 

1681 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1682 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1683 inner_type = None 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1684 # if origin_type is Secret, then cls is a GenericAlias, and we can extract the inner type directly 

1685 origin_type = get_origin(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1686 if origin_type is not None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1687 inner_type = get_args(source)[0] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1688 # otherwise, we need to get the inner type from the base class 

1689 else: 

1690 bases = getattr(cls, '__orig_bases__', getattr(cls, '__bases__', [])) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1691 for base in bases: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1692 if get_origin(base) is Secret: 1692 ↛ 1691line 1692 didn't jump to line 1691 because the condition on line 1692 was always true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1693 inner_type = get_args(base)[0] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1694 if bases == [] or inner_type is None: 1694 ↛ 1695line 1694 didn't jump to line 1695 because the condition on line 1694 was never true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1695 raise TypeError( 

1696 f"Can't get secret type from {cls.__name__}. " 

1697 'Please use Secret[<type>], or subclass from Secret[<type>] instead.' 

1698 ) 

1699 

1700 inner_schema = handler.generate_schema(inner_type) # type: ignore 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1701 

1702 def validate_secret_value(value, handler) -> Secret[SecretType]: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1703 if isinstance(value, Secret): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1704 value = value.get_secret_value() 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1705 validated_inner = handler(value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1706 return cls(validated_inner) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1707 

1708 return core_schema.json_or_python_schema( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1709 python_schema=core_schema.no_info_wrap_validator_function( 

1710 validate_secret_value, 

1711 inner_schema, 

1712 ), 

1713 json_schema=core_schema.no_info_after_validator_function(lambda x: cls(x), inner_schema), 

1714 serialization=core_schema.plain_serializer_function_ser_schema( 

1715 _serialize_secret, 

1716 info_arg=True, 

1717 when_used='always', 

1718 ), 

1719 ) 

1720 

1721 __pydantic_serializer__ = SchemaSerializer( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1722 core_schema.any_schema( 

1723 serialization=core_schema.plain_serializer_function_ser_schema( 

1724 _serialize_secret, 

1725 info_arg=True, 

1726 when_used='always', 

1727 ) 

1728 ) 

1729 ) 

1730 

1731 

1732def _secret_display(value: SecretType) -> str: # type: ignore 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1733 return '**********' if value else '' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1734 

1735 

1736def _serialize_secret_field( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1737 value: _SecretField[SecretType], info: core_schema.SerializationInfo 

1738) -> str | _SecretField[SecretType]: 

1739 if info.mode == 'json': 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1740 # we want the output to always be string without the `b'` prefix for bytes, 

1741 # hence we just use `secret_display` 

1742 return _secret_display(value.get_secret_value()) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1743 else: 

1744 return value 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1745 

1746 

1747class _SecretField(_SecretBase[SecretType]): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1748 _inner_schema: ClassVar[CoreSchema] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1749 _error_kind: ClassVar[str] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1750 

1751 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1752 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1753 def get_json_schema(_core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1754 json_schema = handler(cls._inner_schema) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1755 _utils.update_not_none( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1756 json_schema, 

1757 type='string', 

1758 writeOnly=True, 

1759 format='password', 

1760 ) 

1761 return json_schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1762 

1763 def get_secret_schema(strict: bool) -> CoreSchema: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1764 inner_schema = {**cls._inner_schema, 'strict': strict} 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1765 json_schema = core_schema.no_info_after_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1766 source, # construct the type 

1767 inner_schema, # pyright: ignore[reportArgumentType] 

1768 ) 

1769 return core_schema.json_or_python_schema( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1770 python_schema=core_schema.union_schema( 

1771 [ 

1772 core_schema.is_instance_schema(source), 

1773 json_schema, 

1774 ], 

1775 custom_error_type=cls._error_kind, 

1776 ), 

1777 json_schema=json_schema, 

1778 serialization=core_schema.plain_serializer_function_ser_schema( 

1779 _serialize_secret_field, 

1780 info_arg=True, 

1781 when_used='always', 

1782 ), 

1783 ) 

1784 

1785 return core_schema.lax_or_strict_schema( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1786 lax_schema=get_secret_schema(strict=False), 

1787 strict_schema=get_secret_schema(strict=True), 

1788 metadata={'pydantic_js_functions': [get_json_schema]}, 

1789 ) 

1790 

1791 __pydantic_serializer__ = SchemaSerializer( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1792 core_schema.any_schema( 

1793 serialization=core_schema.plain_serializer_function_ser_schema( 

1794 _serialize_secret_field, 

1795 info_arg=True, 

1796 when_used='always', 

1797 ) 

1798 ) 

1799 ) 

1800 

1801 

1802class SecretStr(_SecretField[str]): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1803 """A string used for storing sensitive information that you do not want to be visible in logging or tracebacks. 

1804 

1805 When the secret value is nonempty, it is displayed as `'**********'` instead of the underlying value in 

1806 calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `''`. 

1807 

1808 ```python 

1809 from pydantic import BaseModel, SecretStr 

1810 

1811 class User(BaseModel): 

1812 username: str 

1813 password: SecretStr 

1814 

1815 user = User(username='scolvin', password='password1') 

1816 

1817 print(user) 

1818 #> username='scolvin' password=SecretStr('**********') 

1819 print(user.password.get_secret_value()) 

1820 #> password1 

1821 print((SecretStr('password'), SecretStr(''))) 

1822 #> (SecretStr('**********'), SecretStr('')) 

1823 ``` 

1824 

1825 As seen above, by default, [`SecretStr`][pydantic.types.SecretStr] (and [`SecretBytes`][pydantic.types.SecretBytes]) 

1826 will be serialized as `**********` when serializing to json. 

1827 

1828 You can use the [`field_serializer`][pydantic.functional_serializers.field_serializer] to dump the 

1829 secret as plain-text when serializing to json. 

1830 

1831 ```python 

1832 from pydantic import BaseModel, SecretBytes, SecretStr, field_serializer 

1833 

1834 class Model(BaseModel): 

1835 password: SecretStr 

1836 password_bytes: SecretBytes 

1837 

1838 @field_serializer('password', 'password_bytes', when_used='json') 

1839 def dump_secret(self, v): 

1840 return v.get_secret_value() 

1841 

1842 model = Model(password='IAmSensitive', password_bytes=b'IAmSensitiveBytes') 

1843 print(model) 

1844 #> password=SecretStr('**********') password_bytes=SecretBytes(b'**********') 

1845 print(model.password) 

1846 #> ********** 

1847 print(model.model_dump()) 

1848 ''' 

1849 { 

1850 'password': SecretStr('**********'), 

1851 'password_bytes': SecretBytes(b'**********'), 

1852 } 

1853 ''' 

1854 print(model.model_dump_json()) 

1855 #> {"password":"IAmSensitive","password_bytes":"IAmSensitiveBytes"} 

1856 ``` 

1857 """ 

1858 

1859 _inner_schema: ClassVar[CoreSchema] = core_schema.str_schema() 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1860 _error_kind: ClassVar[str] = 'string_type' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1861 

1862 def __len__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1863 return len(self._secret_value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1864 

1865 def _display(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1866 return _secret_display(self._secret_value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1867 

1868 

1869class SecretBytes(_SecretField[bytes]): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1870 """A bytes used for storing sensitive information that you do not want to be visible in logging or tracebacks. 

1871 

1872 It displays `b'**********'` instead of the string value on `repr()` and `str()` calls. 

1873 When the secret value is nonempty, it is displayed as `b'**********'` instead of the underlying value in 

1874 calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `b''`. 

1875 

1876 ```python 

1877 from pydantic import BaseModel, SecretBytes 

1878 

1879 class User(BaseModel): 

1880 username: str 

1881 password: SecretBytes 

1882 

1883 user = User(username='scolvin', password=b'password1') 

1884 #> username='scolvin' password=SecretBytes(b'**********') 

1885 print(user.password.get_secret_value()) 

1886 #> b'password1' 

1887 print((SecretBytes(b'password'), SecretBytes(b''))) 

1888 #> (SecretBytes(b'**********'), SecretBytes(b'')) 

1889 ``` 

1890 """ 

1891 

1892 _inner_schema: ClassVar[CoreSchema] = core_schema.bytes_schema() 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1893 _error_kind: ClassVar[str] = 'bytes_type' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1894 

1895 def __len__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1896 return len(self._secret_value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1897 

1898 def _display(self) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1899 return _secret_display(self._secret_value).encode() 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1900 

1901 

1902# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

1903 

1904 

1905class PaymentCardBrand(str, Enum): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1906 amex = 'American Express' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1907 mastercard = 'Mastercard' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1908 visa = 'Visa' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1909 other = 'other' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1910 

1911 def __str__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1912 return self.value 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1913 

1914 

1915@deprecated( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1916 'The `PaymentCardNumber` class is deprecated, use `pydantic_extra_types` instead. ' 

1917 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_payment/#pydantic_extra_types.payment.PaymentCardNumber.', 

1918 category=PydanticDeprecatedSince20, 

1919) 

1920class PaymentCardNumber(str): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1921 """Based on: https://en.wikipedia.org/wiki/Payment_card_number.""" 

1922 

1923 strip_whitespace: ClassVar[bool] = True 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1924 min_length: ClassVar[int] = 12 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1925 max_length: ClassVar[int] = 19 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1926 bin: str 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1927 last4: str 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1928 brand: PaymentCardBrand 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1929 

1930 def __init__(self, card_number: str): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1931 self.validate_digits(card_number) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1932 

1933 card_number = self.validate_luhn_check_digit(card_number) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1934 

1935 self.bin = card_number[:6] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1936 self.last4 = card_number[-4:] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1937 self.brand = self.validate_brand(card_number) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1938 

1939 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1940 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1941 return core_schema.with_info_after_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1942 cls.validate, 

1943 core_schema.str_schema( 

1944 min_length=cls.min_length, max_length=cls.max_length, strip_whitespace=cls.strip_whitespace 

1945 ), 

1946 ) 

1947 

1948 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1949 def validate(cls, input_value: str, /, _: core_schema.ValidationInfo) -> PaymentCardNumber: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1950 """Validate the card number and return a `PaymentCardNumber` instance.""" 

1951 return cls(input_value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1952 

1953 @property 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1954 def masked(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1955 """Mask all but the last 4 digits of the card number. 

1956 

1957 Returns: 

1958 A masked card number string. 

1959 """ 

1960 num_masked = len(self) - 10 # len(bin) + len(last4) == 10 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1961 return f'{self.bin}{"*" * num_masked}{self.last4}' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1962 

1963 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1964 def validate_digits(cls, card_number: str) -> None: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1965 """Validate that the card number is all digits.""" 

1966 if not card_number.isdigit(): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1967 raise PydanticCustomError('payment_card_number_digits', 'Card number is not all digits') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1968 

1969 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1970 def validate_luhn_check_digit(cls, card_number: str) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1971 """Based on: https://en.wikipedia.org/wiki/Luhn_algorithm.""" 

1972 sum_ = int(card_number[-1]) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1973 length = len(card_number) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1974 parity = length % 2 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1975 for i in range(length - 1): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1976 digit = int(card_number[i]) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1977 if i % 2 == parity: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1978 digit *= 2 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1979 if digit > 9: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1980 digit -= 9 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1981 sum_ += digit 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1982 valid = sum_ % 10 == 0 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1983 if not valid: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1984 raise PydanticCustomError('payment_card_number_luhn', 'Card number is not luhn valid') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1985 return card_number 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1986 

1987 @staticmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1988 def validate_brand(card_number: str) -> PaymentCardBrand: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

1989 """Validate length based on BIN for major brands: 

1990 https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN). 

1991 """ 

1992 if card_number[0] == '4': 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1993 brand = PaymentCardBrand.visa 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1994 elif 51 <= int(card_number[:2]) <= 55: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1995 brand = PaymentCardBrand.mastercard 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1996 elif card_number[:2] in {'34', '37'}: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1997 brand = PaymentCardBrand.amex 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

1998 else: 

1999 brand = PaymentCardBrand.other 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2000 

2001 required_length: None | int | str = None 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2002 if brand in PaymentCardBrand.mastercard: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2003 required_length = 16 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2004 valid = len(card_number) == required_length 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2005 elif brand == PaymentCardBrand.visa: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2006 required_length = '13, 16 or 19' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2007 valid = len(card_number) in {13, 16, 19} 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2008 elif brand == PaymentCardBrand.amex: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2009 required_length = 15 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2010 valid = len(card_number) == required_length 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2011 else: 

2012 valid = True 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2013 

2014 if not valid: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2015 raise PydanticCustomError( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2016 'payment_card_number_brand', 

2017 'Length for a {brand} card must be {required_length}', 

2018 {'brand': brand, 'required_length': required_length}, 

2019 ) 

2020 return brand 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2021 

2022 

2023# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

2024 

2025 

2026class ByteSize(int): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2027 """Converts a string representing a number of bytes with units (such as `'1KB'` or `'11.5MiB'`) into an integer. 

2028 

2029 You can use the `ByteSize` data type to (case-insensitively) convert a string representation of a number of bytes into 

2030 an integer, and also to print out human-readable strings representing a number of bytes. 

2031 

2032 In conformance with [IEC 80000-13 Standard](https://en.wikipedia.org/wiki/ISO/IEC_80000) we interpret `'1KB'` to mean 1000 bytes, 

2033 and `'1KiB'` to mean 1024 bytes. In general, including a middle `'i'` will cause the unit to be interpreted as a power of 2, 

2034 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). 

2035 

2036 !!! info 

2037 Note that `1b` will be parsed as "1 byte" and not "1 bit". 

2038 

2039 ```python 

2040 from pydantic import BaseModel, ByteSize 

2041 

2042 class MyModel(BaseModel): 

2043 size: ByteSize 

2044 

2045 print(MyModel(size=52000).size) 

2046 #> 52000 

2047 print(MyModel(size='3000 KiB').size) 

2048 #> 3072000 

2049 

2050 m = MyModel(size='50 PB') 

2051 print(m.size.human_readable()) 

2052 #> 44.4PiB 

2053 print(m.size.human_readable(decimal=True)) 

2054 #> 50.0PB 

2055 print(m.size.human_readable(separator=' ')) 

2056 #> 44.4 PiB 

2057 

2058 print(m.size.to('TiB')) 

2059 #> 45474.73508864641 

2060 ``` 

2061 """ 

2062 

2063 byte_sizes = { 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2064 'b': 1, 

2065 'kb': 10**3, 

2066 'mb': 10**6, 

2067 'gb': 10**9, 

2068 'tb': 10**12, 

2069 'pb': 10**15, 

2070 'eb': 10**18, 

2071 'kib': 2**10, 

2072 'mib': 2**20, 

2073 'gib': 2**30, 

2074 'tib': 2**40, 

2075 'pib': 2**50, 

2076 'eib': 2**60, 

2077 'bit': 1 / 8, 

2078 'kbit': 10**3 / 8, 

2079 'mbit': 10**6 / 8, 

2080 'gbit': 10**9 / 8, 

2081 'tbit': 10**12 / 8, 

2082 'pbit': 10**15 / 8, 

2083 'ebit': 10**18 / 8, 

2084 'kibit': 2**10 / 8, 

2085 'mibit': 2**20 / 8, 

2086 'gibit': 2**30 / 8, 

2087 'tibit': 2**40 / 8, 

2088 'pibit': 2**50 / 8, 

2089 'eibit': 2**60 / 8, 

2090 } 

2091 byte_sizes.update({k.lower()[0]: v for k, v in byte_sizes.items() if 'i' not in k}) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2092 

2093 byte_string_pattern = r'^\s*(\d*\.?\d+)\s*(\w+)?' 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2094 byte_string_re = re.compile(byte_string_pattern, re.IGNORECASE) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2095 

2096 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2097 def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2098 return core_schema.with_info_after_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2099 function=cls._validate, 

2100 schema=core_schema.union_schema( 

2101 [ 

2102 core_schema.str_schema(pattern=cls.byte_string_pattern), 

2103 core_schema.int_schema(ge=0), 

2104 ], 

2105 custom_error_type='byte_size', 

2106 custom_error_message='could not parse value and unit from byte string', 

2107 ), 

2108 serialization=core_schema.plain_serializer_function_ser_schema( 

2109 int, return_schema=core_schema.int_schema(ge=0) 

2110 ), 

2111 ) 

2112 

2113 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2114 def _validate(cls, input_value: Any, /, _: core_schema.ValidationInfo) -> ByteSize: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2115 try: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2116 return cls(int(input_value)) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2117 except ValueError: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2118 pass 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2119 

2120 str_match = cls.byte_string_re.match(str(input_value)) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2121 if str_match is None: 2121 ↛ 2122line 2121 didn't jump to line 2122 because the condition on line 2121 was never true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2122 raise PydanticCustomError('byte_size', 'could not parse value and unit from byte string') 

2123 

2124 scalar, unit = str_match.groups() 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2125 if unit is None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2126 unit = 'b' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2127 

2128 try: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2129 unit_mult = cls.byte_sizes[unit.lower()] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2130 except KeyError: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2131 raise PydanticCustomError('byte_size_unit', 'could not interpret byte unit: {unit}', {'unit': unit}) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2132 

2133 return cls(int(float(scalar) * unit_mult)) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2134 

2135 def human_readable(self, decimal: bool = False, separator: str = '') -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2136 """Converts a byte size to a human readable string. 

2137 

2138 Args: 

2139 decimal: If True, use decimal units (e.g. 1000 bytes per KB). If False, use binary units 

2140 (e.g. 1024 bytes per KiB). 

2141 separator: A string used to split the value and unit. Defaults to an empty string (''). 

2142 

2143 Returns: 

2144 A human readable string representation of the byte size. 

2145 """ 

2146 if decimal: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2147 divisor = 1000 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2148 units = 'B', 'KB', 'MB', 'GB', 'TB', 'PB' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2149 final_unit = 'EB' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2150 else: 

2151 divisor = 1024 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2152 units = 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2153 final_unit = 'EiB' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2154 

2155 num = float(self) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2156 for unit in units: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2157 if abs(num) < divisor: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2158 if unit == 'B': 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2159 return f'{num:0.0f}{separator}{unit}' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2160 else: 

2161 return f'{num:0.1f}{separator}{unit}' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2162 num /= divisor 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2163 

2164 return f'{num:0.1f}{separator}{final_unit}' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2165 

2166 def to(self, unit: str) -> float: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2167 """Converts a byte size to another unit, including both byte and bit units. 

2168 

2169 Args: 

2170 unit: The unit to convert to. Must be one of the following: B, KB, MB, GB, TB, PB, EB, 

2171 KiB, MiB, GiB, TiB, PiB, EiB (byte units) and 

2172 bit, kbit, mbit, gbit, tbit, pbit, ebit, 

2173 kibit, mibit, gibit, tibit, pibit, eibit (bit units). 

2174 

2175 Returns: 

2176 The byte size in the new unit. 

2177 """ 

2178 try: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2179 unit_div = self.byte_sizes[unit.lower()] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2180 except KeyError: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2181 raise PydanticCustomError('byte_size_unit', 'Could not interpret byte unit: {unit}', {'unit': unit}) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2182 

2183 return self / unit_div 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2184 

2185 

2186# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

2187 

2188 

2189def _check_annotated_type(annotated_type: str, expected_type: str, annotation: str) -> None: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2190 if annotated_type != expected_type: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2191 raise PydanticUserError(f"'{annotation}' cannot annotate '{annotated_type}'.", code='invalid-annotated-type') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2192 

2193 

2194if TYPE_CHECKING: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2195 PastDate = Annotated[date, ...] 

2196 FutureDate = Annotated[date, ...] 

2197else: 

2198 

2199 class PastDate: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2200 """A date in the past.""" 

2201 

2202 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2203 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2204 cls, source: type[Any], handler: GetCoreSchemaHandler 

2205 ) -> core_schema.CoreSchema: 

2206 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2207 # used directly as a type 

2208 return core_schema.date_schema(now_op='past') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2209 else: 

2210 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2211 _check_annotated_type(schema['type'], 'date', cls.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2212 schema['now_op'] = 'past' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2213 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2214 

2215 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2216 return 'PastDate' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2217 

2218 class FutureDate: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2219 """A date in the future.""" 

2220 

2221 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2222 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2223 cls, source: type[Any], handler: GetCoreSchemaHandler 

2224 ) -> core_schema.CoreSchema: 

2225 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2226 # used directly as a type 

2227 return core_schema.date_schema(now_op='future') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2228 else: 

2229 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2230 _check_annotated_type(schema['type'], 'date', cls.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2231 schema['now_op'] = 'future' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2232 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2233 

2234 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2235 return 'FutureDate' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2236 

2237 

2238def condate( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2239 *, 

2240 strict: bool | None = None, 

2241 gt: date | None = None, 

2242 ge: date | None = None, 

2243 lt: date | None = None, 

2244 le: date | None = None, 

2245) -> type[date]: 

2246 """A wrapper for date that adds constraints. 

2247 

2248 Args: 

2249 strict: Whether to validate the date value in strict mode. Defaults to `None`. 

2250 gt: The value must be greater than this. Defaults to `None`. 

2251 ge: The value must be greater than or equal to this. Defaults to `None`. 

2252 lt: The value must be less than this. Defaults to `None`. 

2253 le: The value must be less than or equal to this. Defaults to `None`. 

2254 

2255 Returns: 

2256 A date type with the specified constraints. 

2257 """ 

2258 return Annotated[ # pyright: ignore[reportReturnType] 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2259 date, 

2260 Strict(strict) if strict is not None else None, 

2261 annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), 

2262 ] 

2263 

2264 

2265# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATETIME TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

2266 

2267if TYPE_CHECKING: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2268 AwareDatetime = Annotated[datetime, ...] 

2269 NaiveDatetime = Annotated[datetime, ...] 

2270 PastDatetime = Annotated[datetime, ...] 

2271 FutureDatetime = Annotated[datetime, ...] 

2272 

2273else: 

2274 

2275 class AwareDatetime: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2276 """A datetime that requires timezone info.""" 

2277 

2278 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2279 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2280 cls, source: type[Any], handler: GetCoreSchemaHandler 

2281 ) -> core_schema.CoreSchema: 

2282 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2283 # used directly as a type 

2284 return core_schema.datetime_schema(tz_constraint='aware') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2285 else: 

2286 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2287 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2288 schema['tz_constraint'] = 'aware' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2289 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2290 

2291 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2292 return 'AwareDatetime' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2293 

2294 class NaiveDatetime: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2295 """A datetime that doesn't require timezone info.""" 

2296 

2297 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2298 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2299 cls, source: type[Any], handler: GetCoreSchemaHandler 

2300 ) -> core_schema.CoreSchema: 

2301 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2302 # used directly as a type 

2303 return core_schema.datetime_schema(tz_constraint='naive') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2304 else: 

2305 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2306 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2307 schema['tz_constraint'] = 'naive' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2308 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2309 

2310 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2311 return 'NaiveDatetime' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2312 

2313 class PastDatetime: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2314 """A datetime that must be in the past.""" 

2315 

2316 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2317 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2318 cls, source: type[Any], handler: GetCoreSchemaHandler 

2319 ) -> core_schema.CoreSchema: 

2320 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2321 # used directly as a type 

2322 return core_schema.datetime_schema(now_op='past') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2323 else: 

2324 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2325 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2326 schema['now_op'] = 'past' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2327 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2328 

2329 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2330 return 'PastDatetime' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2331 

2332 class FutureDatetime: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2333 """A datetime that must be in the future.""" 

2334 

2335 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2336 def __get_pydantic_core_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2337 cls, source: type[Any], handler: GetCoreSchemaHandler 

2338 ) -> core_schema.CoreSchema: 

2339 if cls is source: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2340 # used directly as a type 

2341 return core_schema.datetime_schema(now_op='future') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2342 else: 

2343 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2344 _check_annotated_type(schema['type'], 'datetime', cls.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2345 schema['now_op'] = 'future' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2346 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2347 

2348 def __repr__(self) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2349 return 'FutureDatetime' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2350 

2351 

2352# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Encoded TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

2353 

2354 

2355class EncoderProtocol(Protocol): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2356 """Protocol for encoding and decoding data to and from bytes.""" 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2357 

2358 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2359 def decode(cls, data: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2360 """Decode the data using the encoder. 

2361 

2362 Args: 

2363 data: The data to decode. 

2364 

2365 Returns: 

2366 The decoded data. 

2367 """ 

2368 ... 

2369 

2370 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2371 def encode(cls, value: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2372 """Encode the data using the encoder. 

2373 

2374 Args: 

2375 value: The data to encode. 

2376 

2377 Returns: 

2378 The encoded data. 

2379 """ 

2380 ... 

2381 

2382 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2383 def get_json_format(cls) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2384 """Get the JSON format for the encoded data. 

2385 

2386 Returns: 

2387 The JSON format for the encoded data. 

2388 """ 

2389 ... 

2390 

2391 

2392class Base64Encoder(EncoderProtocol): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2393 """Standard (non-URL-safe) Base64 encoder.""" 

2394 

2395 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2396 def decode(cls, data: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2397 """Decode the data from base64 encoded bytes to original bytes data. 

2398 

2399 Args: 

2400 data: The data to decode. 

2401 

2402 Returns: 

2403 The decoded data. 

2404 """ 

2405 try: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2406 return base64.b64decode(data) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2407 except ValueError as e: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2408 raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2409 

2410 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2411 def encode(cls, value: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2412 """Encode the data from bytes to a base64 encoded bytes. 

2413 

2414 Args: 

2415 value: The data to encode. 

2416 

2417 Returns: 

2418 The encoded data. 

2419 """ 

2420 return base64.b64encode(value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2421 

2422 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2423 def get_json_format(cls) -> Literal['base64']: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2424 """Get the JSON format for the encoded data. 

2425 

2426 Returns: 

2427 The JSON format for the encoded data. 

2428 """ 

2429 return 'base64' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2430 

2431 

2432class Base64UrlEncoder(EncoderProtocol): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2433 """URL-safe Base64 encoder.""" 

2434 

2435 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2436 def decode(cls, data: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2437 """Decode the data from base64 encoded bytes to original bytes data. 

2438 

2439 Args: 

2440 data: The data to decode. 

2441 

2442 Returns: 

2443 The decoded data. 

2444 """ 

2445 try: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2446 return base64.urlsafe_b64decode(data) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2447 except ValueError as e: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2448 raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2449 

2450 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2451 def encode(cls, value: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2452 """Encode the data from bytes to a base64 encoded bytes. 

2453 

2454 Args: 

2455 value: The data to encode. 

2456 

2457 Returns: 

2458 The encoded data. 

2459 """ 

2460 return base64.urlsafe_b64encode(value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2461 

2462 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2463 def get_json_format(cls) -> Literal['base64url']: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2464 """Get the JSON format for the encoded data. 

2465 

2466 Returns: 

2467 The JSON format for the encoded data. 

2468 """ 

2469 return 'base64url' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2470 

2471 

2472@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2473class EncodedBytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2474 """A bytes type that is encoded and decoded using the specified encoder. 

2475 

2476 `EncodedBytes` needs an encoder that implements `EncoderProtocol` to operate. 

2477 

2478 ```python 

2479 from typing import Annotated 

2480 

2481 from pydantic import BaseModel, EncodedBytes, EncoderProtocol, ValidationError 

2482 

2483 class MyEncoder(EncoderProtocol): 

2484 @classmethod 

2485 def decode(cls, data: bytes) -> bytes: 

2486 if data == b'**undecodable**': 

2487 raise ValueError('Cannot decode data') 

2488 return data[13:] 

2489 

2490 @classmethod 

2491 def encode(cls, value: bytes) -> bytes: 

2492 return b'**encoded**: ' + value 

2493 

2494 @classmethod 

2495 def get_json_format(cls) -> str: 

2496 return 'my-encoder' 

2497 

2498 MyEncodedBytes = Annotated[bytes, EncodedBytes(encoder=MyEncoder)] 

2499 

2500 class Model(BaseModel): 

2501 my_encoded_bytes: MyEncodedBytes 

2502 

2503 # Initialize the model with encoded data 

2504 m = Model(my_encoded_bytes=b'**encoded**: some bytes') 

2505 

2506 # Access decoded value 

2507 print(m.my_encoded_bytes) 

2508 #> b'some bytes' 

2509 

2510 # Serialize into the encoded form 

2511 print(m.model_dump()) 

2512 #> {'my_encoded_bytes': b'**encoded**: some bytes'} 

2513 

2514 # Validate encoded data 

2515 try: 

2516 Model(my_encoded_bytes=b'**undecodable**') 

2517 except ValidationError as e: 

2518 print(e) 

2519 ''' 

2520 1 validation error for Model 

2521 my_encoded_bytes 

2522 Value error, Cannot decode data [type=value_error, input_value=b'**undecodable**', input_type=bytes] 

2523 ''' 

2524 ``` 

2525 """ 

2526 

2527 encoder: type[EncoderProtocol] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2528 

2529 def __get_pydantic_json_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2530 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

2531 ) -> JsonSchemaValue: 

2532 field_schema = handler(core_schema) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2533 field_schema.update(type='string', format=self.encoder.get_json_format()) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2534 return field_schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2535 

2536 def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2537 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2538 _check_annotated_type(schema['type'], 'bytes', self.__class__.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2539 return core_schema.with_info_after_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2540 function=self.decode, 

2541 schema=schema, 

2542 serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode), 

2543 ) 

2544 

2545 def decode(self, data: bytes, _: core_schema.ValidationInfo) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2546 """Decode the data using the specified encoder. 

2547 

2548 Args: 

2549 data: The data to decode. 

2550 

2551 Returns: 

2552 The decoded data. 

2553 """ 

2554 return self.encoder.decode(data) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2555 

2556 def encode(self, value: bytes) -> bytes: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2557 """Encode the data using the specified encoder. 

2558 

2559 Args: 

2560 value: The data to encode. 

2561 

2562 Returns: 

2563 The encoded data. 

2564 """ 

2565 return self.encoder.encode(value) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2566 

2567 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2568 return hash(self.encoder) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2569 

2570 

2571@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2572class EncodedStr: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2573 """A str type that is encoded and decoded using the specified encoder. 

2574 

2575 `EncodedStr` needs an encoder that implements `EncoderProtocol` to operate. 

2576 

2577 ```python 

2578 from typing import Annotated 

2579 

2580 from pydantic import BaseModel, EncodedStr, EncoderProtocol, ValidationError 

2581 

2582 class MyEncoder(EncoderProtocol): 

2583 @classmethod 

2584 def decode(cls, data: bytes) -> bytes: 

2585 if data == b'**undecodable**': 

2586 raise ValueError('Cannot decode data') 

2587 return data[13:] 

2588 

2589 @classmethod 

2590 def encode(cls, value: bytes) -> bytes: 

2591 return b'**encoded**: ' + value 

2592 

2593 @classmethod 

2594 def get_json_format(cls) -> str: 

2595 return 'my-encoder' 

2596 

2597 MyEncodedStr = Annotated[str, EncodedStr(encoder=MyEncoder)] 

2598 

2599 class Model(BaseModel): 

2600 my_encoded_str: MyEncodedStr 

2601 

2602 # Initialize the model with encoded data 

2603 m = Model(my_encoded_str='**encoded**: some str') 

2604 

2605 # Access decoded value 

2606 print(m.my_encoded_str) 

2607 #> some str 

2608 

2609 # Serialize into the encoded form 

2610 print(m.model_dump()) 

2611 #> {'my_encoded_str': '**encoded**: some str'} 

2612 

2613 # Validate encoded data 

2614 try: 

2615 Model(my_encoded_str='**undecodable**') 

2616 except ValidationError as e: 

2617 print(e) 

2618 ''' 

2619 1 validation error for Model 

2620 my_encoded_str 

2621 Value error, Cannot decode data [type=value_error, input_value='**undecodable**', input_type=str] 

2622 ''' 

2623 ``` 

2624 """ 

2625 

2626 encoder: type[EncoderProtocol] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2627 

2628 def __get_pydantic_json_schema__( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2629 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

2630 ) -> JsonSchemaValue: 

2631 field_schema = handler(core_schema) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2632 field_schema.update(type='string', format=self.encoder.get_json_format()) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2633 return field_schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2634 

2635 def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2636 schema = handler(source) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2637 _check_annotated_type(schema['type'], 'str', self.__class__.__name__) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2638 return core_schema.with_info_after_validator_function( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2639 function=self.decode_str, 

2640 schema=schema, 

2641 serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode_str), 

2642 ) 

2643 

2644 def decode_str(self, data: str, _: core_schema.ValidationInfo) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2645 """Decode the data using the specified encoder. 

2646 

2647 Args: 

2648 data: The data to decode. 

2649 

2650 Returns: 

2651 The decoded data. 

2652 """ 

2653 return self.encoder.decode(data.encode()).decode() 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2654 

2655 def encode_str(self, value: str) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2656 """Encode the data using the specified encoder. 

2657 

2658 Args: 

2659 value: The data to encode. 

2660 

2661 Returns: 

2662 The encoded data. 

2663 """ 

2664 return self.encoder.encode(value.encode()).decode() # noqa: UP008 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2665 

2666 def __hash__(self) -> int: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2667 return hash(self.encoder) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2668 

2669 

2670Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64Encoder)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2671"""A bytes type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. 1abcdefghijklmnopqrsJtuvwxyzAB

2672 

2673Note: 

2674 Under the hood, `Base64Bytes` uses the standard library `base64.b64encode` and `base64.b64decode` functions. 

2675 

2676 As a result, attempting to decode url-safe base64 data using the `Base64Bytes` type may fail or produce an incorrect 

2677 decoding. 

2678 

2679Warning: 

2680 In versions of Pydantic prior to v2.10, `Base64Bytes` used [`base64.encodebytes`][base64.encodebytes] 

2681 and [`base64.decodebytes`][base64.decodebytes] functions. According to the [base64 documentation](https://docs.python.org/3/library/base64.html), 

2682 these methods are considered legacy implementation, and thus, Pydantic v2.10+ now uses the modern 

2683 [`base64.b64encode`][base64.b64encode] and [`base64.b64decode`][base64.b64decode] functions. 

2684 

2685 If you'd still like to use these legacy encoders / decoders, you can achieve this by creating a custom annotated type, 

2686 like follows: 

2687 

2688 ```python 

2689 import base64 

2690 from typing import Annotated, Literal 

2691 

2692 from pydantic_core import PydanticCustomError 

2693 

2694 from pydantic import EncodedBytes, EncoderProtocol 

2695 

2696 class LegacyBase64Encoder(EncoderProtocol): 

2697 @classmethod 

2698 def decode(cls, data: bytes) -> bytes: 

2699 try: 

2700 return base64.decodebytes(data) 

2701 except ValueError as e: 

2702 raise PydanticCustomError( 

2703 'base64_decode', 

2704 "Base64 decoding error: '{error}'", 

2705 {'error': str(e)}, 

2706 ) 

2707 

2708 @classmethod 

2709 def encode(cls, value: bytes) -> bytes: 

2710 return base64.encodebytes(value) 

2711 

2712 @classmethod 

2713 def get_json_format(cls) -> Literal['base64']: 

2714 return 'base64' 

2715 

2716 LegacyBase64Bytes = Annotated[bytes, EncodedBytes(encoder=LegacyBase64Encoder)] 

2717 ``` 

2718 

2719```python 

2720from pydantic import Base64Bytes, BaseModel, ValidationError 

2721 

2722class Model(BaseModel): 

2723 base64_bytes: Base64Bytes 

2724 

2725# Initialize the model with base64 data 

2726m = Model(base64_bytes=b'VGhpcyBpcyB0aGUgd2F5') 

2727 

2728# Access decoded value 

2729print(m.base64_bytes) 

2730#> b'This is the way' 

2731 

2732# Serialize into the base64 form 

2733print(m.model_dump()) 

2734#> {'base64_bytes': b'VGhpcyBpcyB0aGUgd2F5'} 

2735 

2736# Validate base64 data 

2737try: 

2738 print(Model(base64_bytes=b'undecodable').base64_bytes) 

2739except ValidationError as e: 

2740 print(e) 

2741 ''' 

2742 1 validation error for Model 

2743 base64_bytes 

2744 Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value=b'undecodable', input_type=bytes] 

2745 ''' 

2746``` 

2747""" 

2748Base64Str = Annotated[str, EncodedStr(encoder=Base64Encoder)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2749"""A str type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. 1abcdefghijklmnopqrsJtuvwxyzAB

2750 

2751Note: 

2752 Under the hood, `Base64Str` uses the standard library `base64.b64encode` and `base64.b64decode` functions. 

2753 

2754 As a result, attempting to decode url-safe base64 data using the `Base64Str` type may fail or produce an incorrect 

2755 decoding. 

2756 

2757Warning: 

2758 In versions of Pydantic prior to v2.10, `Base64Str` used [`base64.encodebytes`][base64.encodebytes] 

2759 and [`base64.decodebytes`][base64.decodebytes] functions. According to the [base64 documentation](https://docs.python.org/3/library/base64.html), 

2760 these methods are considered legacy implementation, and thus, Pydantic v2.10+ now uses the modern 

2761 [`base64.b64encode`][base64.b64encode] and [`base64.b64decode`][base64.b64decode] functions. 

2762 

2763 See the [`Base64Bytes`][pydantic.types.Base64Bytes] type for more information on how to 

2764 replicate the old behavior with the legacy encoders / decoders. 

2765 

2766```python 

2767from pydantic import Base64Str, BaseModel, ValidationError 

2768 

2769class Model(BaseModel): 

2770 base64_str: Base64Str 

2771 

2772# Initialize the model with base64 data 

2773m = Model(base64_str='VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y') 

2774 

2775# Access decoded value 

2776print(m.base64_str) 

2777#> These aren't the droids you're looking for 

2778 

2779# Serialize into the base64 form 

2780print(m.model_dump()) 

2781#> {'base64_str': 'VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y'} 

2782 

2783# Validate base64 data 

2784try: 

2785 print(Model(base64_str='undecodable').base64_str) 

2786except ValidationError as e: 

2787 print(e) 

2788 ''' 

2789 1 validation error for Model 

2790 base64_str 

2791 Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value='undecodable', input_type=str] 

2792 ''' 

2793``` 

2794""" 

2795Base64UrlBytes = Annotated[bytes, EncodedBytes(encoder=Base64UrlEncoder)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2796"""A bytes type that is encoded and decoded using the URL-safe base64 encoder. 1abcdefghijklmnopqrsJtuvwxyzAB

2797 

2798Note: 

2799 Under the hood, `Base64UrlBytes` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode` 

2800 functions. 

2801 

2802 As a result, the `Base64UrlBytes` type can be used to faithfully decode "vanilla" base64 data 

2803 (using `'+'` and `'/'`). 

2804 

2805```python 

2806from pydantic import Base64UrlBytes, BaseModel 

2807 

2808class Model(BaseModel): 

2809 base64url_bytes: Base64UrlBytes 

2810 

2811# Initialize the model with base64 data 

2812m = Model(base64url_bytes=b'SHc_dHc-TXc==') 

2813print(m) 

2814#> base64url_bytes=b'Hw?tw>Mw' 

2815``` 

2816""" 

2817Base64UrlStr = Annotated[str, EncodedStr(encoder=Base64UrlEncoder)] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2818"""A str type that is encoded and decoded using the URL-safe base64 encoder. 1abcdefghijklmnopqrsJtuvwxyzAB

2819 

2820Note: 

2821 Under the hood, `Base64UrlStr` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode` 

2822 functions. 

2823 

2824 As a result, the `Base64UrlStr` type can be used to faithfully decode "vanilla" base64 data (using `'+'` and `'/'`). 

2825 

2826```python 

2827from pydantic import Base64UrlStr, BaseModel 

2828 

2829class Model(BaseModel): 

2830 base64url_str: Base64UrlStr 

2831 

2832# Initialize the model with base64 data 

2833m = Model(base64url_str='SHc_dHc-TXc==') 

2834print(m) 

2835#> base64url_str='Hw?tw>Mw' 

2836``` 

2837""" 

2838 

2839 

2840__getattr__ = getattr_migration(__name__) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2841 

2842 

2843@_dataclasses.dataclass(**_internal_dataclass.slots_true) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2844class GetPydanticSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2845 """!!! abstract "Usage Documentation" 

2846 [Using `GetPydanticSchema` to Reduce Boilerplate](../concepts/types.md#using-getpydanticschema-to-reduce-boilerplate) 

2847 

2848 A convenience class for creating an annotation that provides pydantic custom type hooks. 

2849 

2850 This class is intended to eliminate the need to create a custom "marker" which defines the 

2851 `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__` custom hook methods. 

2852 

2853 For example, to have a field treated by type checkers as `int`, but by pydantic as `Any`, you can do: 

2854 ```python 

2855 from typing import Annotated, Any 

2856 

2857 from pydantic import BaseModel, GetPydanticSchema 

2858 

2859 HandleAsAny = GetPydanticSchema(lambda _s, h: h(Any)) 

2860 

2861 class Model(BaseModel): 

2862 x: Annotated[int, HandleAsAny] # pydantic sees `x: Any` 

2863 

2864 print(repr(Model(x='abc').x)) 

2865 #> 'abc' 

2866 ``` 

2867 """ 

2868 

2869 get_pydantic_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2870 get_pydantic_json_schema: Callable[[Any, GetJsonSchemaHandler], JsonSchemaValue] | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2871 

2872 # Note: we may want to consider adding a convenience staticmethod `def for_type(type_: Any) -> GetPydanticSchema:` 

2873 # which returns `GetPydanticSchema(lambda _s, h: h(type_))` 

2874 

2875 if not TYPE_CHECKING: 2875 ↛ 2887line 2875 didn't jump to line 2887 because the condition on line 2875 was always true1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2876 # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access 

2877 

2878 def __getattr__(self, item: str) -> Any: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2879 """Use this rather than defining `__get_pydantic_core_schema__` etc. to reduce the number of nested calls.""" 

2880 if item == '__get_pydantic_core_schema__' and self.get_pydantic_core_schema: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2881 return self.get_pydantic_core_schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2882 elif item == '__get_pydantic_json_schema__' and self.get_pydantic_json_schema: 2882 ↛ 2883line 2882 didn't jump to line 2883 because the condition on line 2882 was never true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2883 return self.get_pydantic_json_schema 

2884 else: 

2885 return object.__getattribute__(self, item) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2886 

2887 __hash__ = object.__hash__ 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2888 

2889 

2890@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2891class Tag: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2892 """Provides a way to specify the expected tag to use for a case of a (callable) discriminated union. 

2893 

2894 Also provides a way to label a union case in error messages. 

2895 

2896 When using a callable `Discriminator`, attach a `Tag` to each case in the `Union` to specify the tag that 

2897 should be used to identify that case. For example, in the below example, the `Tag` is used to specify that 

2898 if `get_discriminator_value` returns `'apple'`, the input should be validated as an `ApplePie`, and if it 

2899 returns `'pumpkin'`, the input should be validated as a `PumpkinPie`. 

2900 

2901 The primary role of the `Tag` here is to map the return value from the callable `Discriminator` function to 

2902 the appropriate member of the `Union` in question. 

2903 

2904 ```python 

2905 from typing import Annotated, Any, Literal, Union 

2906 

2907 from pydantic import BaseModel, Discriminator, Tag 

2908 

2909 class Pie(BaseModel): 

2910 time_to_cook: int 

2911 num_ingredients: int 

2912 

2913 class ApplePie(Pie): 

2914 fruit: Literal['apple'] = 'apple' 

2915 

2916 class PumpkinPie(Pie): 

2917 filling: Literal['pumpkin'] = 'pumpkin' 

2918 

2919 def get_discriminator_value(v: Any) -> str: 

2920 if isinstance(v, dict): 

2921 return v.get('fruit', v.get('filling')) 

2922 return getattr(v, 'fruit', getattr(v, 'filling', None)) 

2923 

2924 class ThanksgivingDinner(BaseModel): 

2925 dessert: Annotated[ 

2926 Union[ 

2927 Annotated[ApplePie, Tag('apple')], 

2928 Annotated[PumpkinPie, Tag('pumpkin')], 

2929 ], 

2930 Discriminator(get_discriminator_value), 

2931 ] 

2932 

2933 apple_variation = ThanksgivingDinner.model_validate( 

2934 {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}} 

2935 ) 

2936 print(repr(apple_variation)) 

2937 ''' 

2938 ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple')) 

2939 ''' 

2940 

2941 pumpkin_variation = ThanksgivingDinner.model_validate( 

2942 { 

2943 'dessert': { 

2944 'filling': 'pumpkin', 

2945 'time_to_cook': 40, 

2946 'num_ingredients': 6, 

2947 } 

2948 } 

2949 ) 

2950 print(repr(pumpkin_variation)) 

2951 ''' 

2952 ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin')) 

2953 ''' 

2954 ``` 

2955 

2956 !!! note 

2957 You must specify a `Tag` for every case in a `Tag` that is associated with a 

2958 callable `Discriminator`. Failing to do so will result in a `PydanticUserError` with code 

2959 [`callable-discriminator-no-tag`](../errors/usage_errors.md#callable-discriminator-no-tag). 

2960 

2961 See the [Discriminated Unions] concepts docs for more details on how to use `Tag`s. 

2962 

2963 [Discriminated Unions]: ../concepts/unions.md#discriminated-unions 

2964 """ 

2965 

2966 tag: str 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2967 

2968 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2969 schema = handler(source_type) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2970 metadata = cast('CoreMetadata', schema.setdefault('metadata', {})) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2971 metadata['pydantic_internal_union_tag_key'] = self.tag 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2972 return schema 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

2973 

2974 

2975@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2976class Discriminator: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

2977 """!!! abstract "Usage Documentation" 

2978 [Discriminated Unions with `Callable` `Discriminator`](../concepts/unions.md#discriminated-unions-with-callable-discriminator) 

2979 

2980 Provides a way to use a custom callable as the way to extract the value of a union discriminator. 

2981 

2982 This allows you to get validation behavior like you'd get from `Field(discriminator=<field_name>)`, 

2983 but without needing to have a single shared field across all the union choices. This also makes it 

2984 possible to handle unions of models and primitive types with discriminated-union-style validation errors. 

2985 Finally, this allows you to use a custom callable as the way to identify which member of a union a value 

2986 belongs to, while still seeing all the performance benefits of a discriminated union. 

2987 

2988 Consider this example, which is much more performant with the use of `Discriminator` and thus a `TaggedUnion` 

2989 than it would be as a normal `Union`. 

2990 

2991 ```python 

2992 from typing import Annotated, Any, Literal, Union 

2993 

2994 from pydantic import BaseModel, Discriminator, Tag 

2995 

2996 class Pie(BaseModel): 

2997 time_to_cook: int 

2998 num_ingredients: int 

2999 

3000 class ApplePie(Pie): 

3001 fruit: Literal['apple'] = 'apple' 

3002 

3003 class PumpkinPie(Pie): 

3004 filling: Literal['pumpkin'] = 'pumpkin' 

3005 

3006 def get_discriminator_value(v: Any) -> str: 

3007 if isinstance(v, dict): 

3008 return v.get('fruit', v.get('filling')) 

3009 return getattr(v, 'fruit', getattr(v, 'filling', None)) 

3010 

3011 class ThanksgivingDinner(BaseModel): 

3012 dessert: Annotated[ 

3013 Union[ 

3014 Annotated[ApplePie, Tag('apple')], 

3015 Annotated[PumpkinPie, Tag('pumpkin')], 

3016 ], 

3017 Discriminator(get_discriminator_value), 

3018 ] 

3019 

3020 apple_variation = ThanksgivingDinner.model_validate( 

3021 {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}} 

3022 ) 

3023 print(repr(apple_variation)) 

3024 ''' 

3025 ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple')) 

3026 ''' 

3027 

3028 pumpkin_variation = ThanksgivingDinner.model_validate( 

3029 { 

3030 'dessert': { 

3031 'filling': 'pumpkin', 

3032 'time_to_cook': 40, 

3033 'num_ingredients': 6, 

3034 } 

3035 } 

3036 ) 

3037 print(repr(pumpkin_variation)) 

3038 ''' 

3039 ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin')) 

3040 ''' 

3041 ``` 

3042 

3043 See the [Discriminated Unions] concepts docs for more details on how to use `Discriminator`s. 

3044 

3045 [Discriminated Unions]: ../concepts/unions.md#discriminated-unions 

3046 """ 

3047 

3048 discriminator: str | Callable[[Any], Hashable] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3049 """The callable or field name for discriminating the type in a tagged union. 1abcdefghijklmnopqrsJtuvwxyzAB

3050 

3051 A `Callable` discriminator must extract the value of the discriminator from the input. 

3052 A `str` discriminator must be the name of a field to discriminate against. 

3053 """ 

3054 custom_error_type: str | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3055 """Type to use in [custom errors](../errors/errors.md) replacing the standard discriminated union 1abcdefghijklmnopqrsJtuvwxyzAB

3056 validation errors. 

3057 """ 

3058 custom_error_message: str | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3059 """Message to use in custom errors.""" 1abcdefghijklmnopqrsJtuvwxyzAB

3060 custom_error_context: dict[str, int | str | float] | None = None 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3061 """Context to use in custom errors.""" 1abcdefghijklmnopqrsJtuvwxyzAB

3062 

3063 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3064 if not is_union_origin(get_origin(source_type)): 3064 ↛ 3065line 3064 didn't jump to line 3065 because the condition on line 3064 was never true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3065 raise TypeError(f'{type(self).__name__} must be used with a Union type, not {source_type}') 

3066 

3067 if isinstance(self.discriminator, str): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3068 from pydantic import Field 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3069 

3070 return handler(Annotated[source_type, Field(discriminator=self.discriminator)]) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3071 else: 

3072 original_schema = handler(source_type) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3073 return self._convert_schema(original_schema, handler) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3074 

3075 def _convert_schema( 1CDabcdefghiFGklmnopqrsJHItuvwxyzAB

3076 self, original_schema: core_schema.CoreSchema, handler: GetCoreSchemaHandler | None = None 

3077 ) -> core_schema.TaggedUnionSchema: 

3078 if original_schema['type'] != 'union': 3078 ↛ 3083line 3078 didn't jump to line 3083 because the condition on line 3078 was never true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3079 # This likely indicates that the schema was a single-item union that was simplified. 

3080 # In this case, we do the same thing we do in 

3081 # `pydantic._internal._discriminated_union._ApplyInferredDiscriminator._apply_to_root`, namely, 

3082 # package the generated schema back into a single-item union. 

3083 original_schema = core_schema.union_schema([original_schema]) 

3084 

3085 tagged_union_choices = {} 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3086 for choice in original_schema['choices']: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3087 tag = None 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3088 if isinstance(choice, tuple): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3089 choice, tag = choice 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3090 metadata = cast('CoreMetadata | None', choice.get('metadata')) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3091 if metadata is not None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3092 tag = metadata.get('pydantic_internal_union_tag_key') or tag 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3093 if tag is None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3094 # `handler` is None when this method is called from `apply_discriminator()` (deferred discriminators) 

3095 if handler is not None and choice['type'] == 'definition-ref': 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3096 # If choice was built from a PEP 695 type alias, try to resolve the def: 

3097 try: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3098 choice = handler.resolve_ref_schema(choice) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3099 except LookupError: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3100 pass 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3101 else: 

3102 metadata = cast('CoreMetadata | None', choice.get('metadata')) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3103 if metadata is not None: 3103 ↛ 3106line 3103 didn't jump to line 3106 because the condition on line 3103 was always true1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3104 tag = metadata.get('pydantic_internal_union_tag_key') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3105 

3106 if tag is None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3107 raise PydanticUserError( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3108 f'`Tag` not provided for choice {choice} used with `Discriminator`', 

3109 code='callable-discriminator-no-tag', 

3110 ) 

3111 tagged_union_choices[tag] = choice 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3112 

3113 # Have to do these verbose checks to ensure falsy values ('' and {}) don't get ignored 

3114 custom_error_type = self.custom_error_type 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3115 if custom_error_type is None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3116 custom_error_type = original_schema.get('custom_error_type') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3117 

3118 custom_error_message = self.custom_error_message 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3119 if custom_error_message is None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3120 custom_error_message = original_schema.get('custom_error_message') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3121 

3122 custom_error_context = self.custom_error_context 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3123 if custom_error_context is None: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3124 custom_error_context = original_schema.get('custom_error_context') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3125 

3126 custom_error_type = original_schema.get('custom_error_type') if custom_error_type is None else custom_error_type 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3127 return core_schema.tagged_union_schema( 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3128 tagged_union_choices, 

3129 self.discriminator, 

3130 custom_error_type=custom_error_type, 

3131 custom_error_message=custom_error_message, 

3132 custom_error_context=custom_error_context, 

3133 strict=original_schema.get('strict'), 

3134 ref=original_schema.get('ref'), 

3135 metadata=original_schema.get('metadata'), 

3136 serialization=original_schema.get('serialization'), 

3137 ) 

3138 

3139 

3140_JSON_TYPES = {int, float, str, bool, list, dict, type(None)} 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3141 

3142 

3143def _get_type_name(x: Any) -> str: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3144 type_ = type(x) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3145 if type_ in _JSON_TYPES: 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3146 return type_.__name__ 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3147 

3148 # Handle proper subclasses; note we don't need to handle None or bool here 

3149 if isinstance(x, int): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3150 return 'int' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3151 if isinstance(x, float): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3152 return 'float' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3153 if isinstance(x, str): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3154 return 'str' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3155 if isinstance(x, list): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3156 return 'list' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3157 if isinstance(x, dict): 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3158 return 'dict' 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3159 

3160 # Fail by returning the type's actual name 

3161 return getattr(type_, '__name__', '<no type name>') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3162 

3163 

3164class _AllowAnyJson: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3165 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3166 def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3167 python_schema = handler(source_type) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3168 return core_schema.json_or_python_schema(json_schema=core_schema.any_schema(), python_schema=python_schema) 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3169 

3170 

3171if TYPE_CHECKING: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3172 # This seems to only be necessary for mypy 

3173 JsonValue: TypeAlias = Union[ 

3174 list['JsonValue'], 

3175 dict[str, 'JsonValue'], 

3176 str, 

3177 bool, 

3178 int, 

3179 float, 

3180 None, 

3181 ] 

3182 """A `JsonValue` is used to represent a value that can be serialized to JSON. 

3183 

3184 It may be one of: 

3185 

3186 * `list['JsonValue']` 

3187 * `dict[str, 'JsonValue']` 

3188 * `str` 

3189 * `bool` 

3190 * `int` 

3191 * `float` 

3192 * `None` 

3193 

3194 The following example demonstrates how to use `JsonValue` to validate JSON data, 

3195 and what kind of errors to expect when input data is not json serializable. 

3196 

3197 ```python 

3198 import json 

3199 

3200 from pydantic import BaseModel, JsonValue, ValidationError 

3201 

3202 class Model(BaseModel): 

3203 j: JsonValue 

3204 

3205 valid_json_data = {'j': {'a': {'b': {'c': 1, 'd': [2, None]}}}} 

3206 invalid_json_data = {'j': {'a': {'b': ...}}} 

3207 

3208 print(repr(Model.model_validate(valid_json_data))) 

3209 #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}}) 

3210 print(repr(Model.model_validate_json(json.dumps(valid_json_data)))) 

3211 #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}}) 

3212 

3213 try: 

3214 Model.model_validate(invalid_json_data) 

3215 except ValidationError as e: 

3216 print(e) 

3217 ''' 

3218 1 validation error for Model 

3219 j.dict.a.dict.b 

3220 input was not a valid JSON value [type=invalid-json-value, input_value=Ellipsis, input_type=ellipsis] 

3221 ''' 

3222 ``` 

3223 """ 

3224 

3225else: 

3226 JsonValue = TypeAliasType( 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3227 'JsonValue', 

3228 Annotated[ 

3229 Union[ 

3230 Annotated[list['JsonValue'], Tag('list')], 

3231 Annotated[dict[str, 'JsonValue'], Tag('dict')], 

3232 Annotated[str, Tag('str')], 

3233 Annotated[bool, Tag('bool')], 

3234 Annotated[int, Tag('int')], 

3235 Annotated[float, Tag('float')], 

3236 Annotated[None, Tag('NoneType')], 

3237 ], 

3238 Discriminator( 

3239 _get_type_name, 

3240 custom_error_type='invalid-json-value', 

3241 custom_error_message='input was not a valid JSON value', 

3242 ), 

3243 _AllowAnyJson, 

3244 ], 

3245 ) 

3246 

3247 

3248class _OnErrorOmit: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3249 @classmethod 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3250 def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3251 # there is no actual default value here but we use with_default_schema since it already has the on_error 

3252 # behavior implemented and it would be no more efficient to implement it on every other validator 

3253 # or as a standalone validator 

3254 return core_schema.with_default_schema(schema=handler(source_type), on_error='omit') 1CDabcdefghiEjFGklmnopqrsHItuvwxyzAB

3255 

3256 

3257OnErrorOmit = Annotated[T, _OnErrorOmit] 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3258""" 1abcdefghijklmnopqrsJtuvwxyzAB

3259When used as an item in a list, the key type in a dict, optional values of a TypedDict, etc. 

3260this annotation omits the item from the iteration if there is any error validating it. 

3261That is, instead of a [`ValidationError`][pydantic_core.ValidationError] being propagated up and the entire iterable being discarded 

3262any invalid items are discarded and the valid ones are returned. 

3263""" 

3264 

3265 

3266@_dataclasses.dataclass 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3267class FailFast(_fields.PydanticMetadata, BaseMetadata): 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB

3268 """A `FailFast` annotation can be used to specify that validation should stop at the first error. 

3269 

3270 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. 

3271 

3272 You might want to enable this setting if you want to validate your data faster (basically, if you use this, 

3273 validation will be more performant with the caveat that you get less information). 

3274 

3275 ```python 

3276 from typing import Annotated 

3277 

3278 from pydantic import BaseModel, FailFast, ValidationError 

3279 

3280 class Model(BaseModel): 

3281 x: Annotated[list[int], FailFast()] 

3282 

3283 # This will raise a single error for the first invalid value and stop validation 

3284 try: 

3285 obj = Model(x=[1, 2, 'a', 4, 5, 'b', 7, 8, 9, 'c']) 

3286 except ValidationError as e: 

3287 print(e) 

3288 ''' 

3289 1 validation error for Model 

3290 x.2 

3291 Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str] 

3292 ''' 

3293 ``` 

3294 """ 

3295 

3296 fail_fast: bool = True 1CDabcdefghiEjFGklmnopqrsJHItuvwxyzAB