Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pydantic/functional_validators.py: 57%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

201 statements  

1"""This module contains related classes and functions for validation.""" 

2 

3from __future__ import annotations as _annotations 

4 

5import dataclasses 

6import sys 

7import warnings 

8from functools import partialmethod 

9from types import FunctionType 

10from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, Union, cast, overload 

11 

12from pydantic_core import PydanticUndefined, core_schema 

13from typing_extensions import Self, TypeAlias 

14 

15from ._internal import _decorators, _generics, _internal_dataclass 

16from .annotated_handlers import GetCoreSchemaHandler 

17from .errors import PydanticUserError 

18from .version import version_short 

19from .warnings import ArbitraryTypeWarning, PydanticDeprecatedSince212 

20 

21if sys.version_info < (3, 11): 

22 from typing_extensions import Protocol 

23else: 

24 from typing import Protocol 

25 

26_inspect_validator = _decorators.inspect_validator 

27 

28 

29@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) 

30class AfterValidator: 

31 """!!! abstract "Usage Documentation" 

32 [field *after* validators](../concepts/validators.md#field-after-validator) 

33 

34 A metadata class that indicates that a validation should be applied **after** the inner validation logic. 

35 

36 Attributes: 

37 func: The validator function. 

38 

39 Example: 

40 ```python 

41 from typing import Annotated 

42 

43 from pydantic import AfterValidator, BaseModel, ValidationError 

44 

45 MyInt = Annotated[int, AfterValidator(lambda v: v + 1)] 

46 

47 class Model(BaseModel): 

48 a: MyInt 

49 

50 print(Model(a=1).a) 

51 #> 2 

52 

53 try: 

54 Model(a='a') 

55 except ValidationError as e: 

56 print(e.json(indent=2)) 

57 ''' 

58 [ 

59 { 

60 "type": "int_parsing", 

61 "loc": [ 

62 "a" 

63 ], 

64 "msg": "Input should be a valid integer, unable to parse string as an integer", 

65 "input": "a", 

66 "url": "https://errors.pydantic.dev/2/v/int_parsing" 

67 } 

68 ] 

69 ''' 

70 ``` 

71 """ 

72 

73 func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction 

74 

75 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 

76 schema = handler(source_type) 

77 info_arg = _inspect_validator(self.func, mode='after', type='field') 

78 if info_arg: 

79 func = cast(core_schema.WithInfoValidatorFunction, self.func) 

80 return core_schema.with_info_after_validator_function(func, schema=schema) 

81 else: 

82 func = cast(core_schema.NoInfoValidatorFunction, self.func) 

83 return core_schema.no_info_after_validator_function(func, schema=schema) 

84 

85 @classmethod 

86 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: 

87 return cls(func=decorator.func) 

88 

89 

90@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) 

91class BeforeValidator: 

92 """!!! abstract "Usage Documentation" 

93 [field *before* validators](../concepts/validators.md#field-before-validator) 

94 

95 A metadata class that indicates that a validation should be applied **before** the inner validation logic. 

96 

97 Attributes: 

98 func: The validator function. 

99 json_schema_input_type: The input type used to generate the appropriate 

100 JSON Schema (in validation mode). The actual input type is `Any`. 

101 

102 Example: 

103 ```python 

104 from typing import Annotated 

105 

106 from pydantic import BaseModel, BeforeValidator 

107 

108 MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)] 

109 

110 class Model(BaseModel): 

111 a: MyInt 

112 

113 print(Model(a=1).a) 

114 #> 2 

115 

116 try: 

117 Model(a='a') 

118 except TypeError as e: 

119 print(e) 

120 #> can only concatenate str (not "int") to str 

121 ``` 

122 """ 

123 

124 func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction 

125 json_schema_input_type: Any = PydanticUndefined 

126 

127 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 

128 schema = handler(source_type) 

129 input_schema = ( 

130 None 

131 if self.json_schema_input_type is PydanticUndefined 

132 else handler.generate_schema(self.json_schema_input_type) 

133 ) 

134 

135 info_arg = _inspect_validator(self.func, mode='before', type='field') 

136 if info_arg: 

137 func = cast(core_schema.WithInfoValidatorFunction, self.func) 

138 return core_schema.with_info_before_validator_function( 

139 func, 

140 schema=schema, 

141 json_schema_input_schema=input_schema, 

142 ) 

143 else: 

144 func = cast(core_schema.NoInfoValidatorFunction, self.func) 

145 return core_schema.no_info_before_validator_function( 

146 func, schema=schema, json_schema_input_schema=input_schema 

147 ) 

148 

149 @classmethod 

150 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: 

151 return cls( 

152 func=decorator.func, 

153 json_schema_input_type=decorator.info.json_schema_input_type, 

154 ) 

155 

156 

157@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) 

158class PlainValidator: 

159 """!!! abstract "Usage Documentation" 

160 [field *plain* validators](../concepts/validators.md#field-plain-validator) 

161 

162 A metadata class that indicates that a validation should be applied **instead** of the inner validation logic. 

163 

164 !!! note 

165 Before v2.9, `PlainValidator` wasn't always compatible with JSON Schema generation for `mode='validation'`. 

166 You can now use the `json_schema_input_type` argument to specify the input type of the function 

167 to be used in the JSON schema when `mode='validation'` (the default). See the example below for more details. 

168 

169 Attributes: 

170 func: The validator function. 

171 json_schema_input_type: The input type used to generate the appropriate 

172 JSON Schema (in validation mode). The actual input type is `Any`. 

173 

174 Example: 

175 ```python 

176 from typing import Annotated, Union 

177 

178 from pydantic import BaseModel, PlainValidator 

179 

180 def validate(v: object) -> int: 

181 if not isinstance(v, (int, str)): 

182 raise ValueError(f'Expected int or str, go {type(v)}') 

183 

184 return int(v) + 1 

185 

186 MyInt = Annotated[ 

187 int, 

188 PlainValidator(validate, json_schema_input_type=Union[str, int]), # (1)! 

189 ] 

190 

191 class Model(BaseModel): 

192 a: MyInt 

193 

194 print(Model(a='1').a) 

195 #> 2 

196 

197 print(Model(a=1).a) 

198 #> 2 

199 ``` 

200 

201 1. In this example, we've specified the `json_schema_input_type` as `Union[str, int]` which indicates to the JSON schema 

202 generator that in validation mode, the input type for the `a` field can be either a [`str`][] or an [`int`][]. 

203 """ 

204 

205 func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction 

206 json_schema_input_type: Any = Any 

207 

208 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 

209 # Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the 

210 # source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper 

211 # serialization schema. To work around this for use cases that will not involve serialization, we simply 

212 # catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema 

213 # and abort any attempts to handle special serialization. 

214 from pydantic import PydanticSchemaGenerationError 

215 

216 try: 

217 schema = handler(source_type) 

218 # TODO if `schema['serialization']` is one of `'include-exclude-dict/sequence', 

219 # schema validation will fail. That's why we use 'type ignore' comments below. 

220 serialization = schema.get( 

221 'serialization', 

222 core_schema.wrap_serializer_function_ser_schema( 

223 function=lambda v, h: h(v), 

224 schema=schema, 

225 return_schema=handler.generate_schema(source_type), 

226 ), 

227 ) 

228 except PydanticSchemaGenerationError: 

229 serialization = None 

230 

231 input_schema = handler.generate_schema(self.json_schema_input_type) 

232 

233 info_arg = _inspect_validator(self.func, mode='plain', type='field') 

234 if info_arg: 

235 func = cast(core_schema.WithInfoValidatorFunction, self.func) 

236 return core_schema.with_info_plain_validator_function( 

237 func, 

238 serialization=serialization, # pyright: ignore[reportArgumentType] 

239 json_schema_input_schema=input_schema, 

240 ) 

241 else: 

242 func = cast(core_schema.NoInfoValidatorFunction, self.func) 

243 return core_schema.no_info_plain_validator_function( 

244 func, 

245 serialization=serialization, # pyright: ignore[reportArgumentType] 

246 json_schema_input_schema=input_schema, 

247 ) 

248 

249 @classmethod 

250 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: 

251 return cls( 

252 func=decorator.func, 

253 json_schema_input_type=decorator.info.json_schema_input_type, 

254 ) 

255 

256 

257@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) 

258class WrapValidator: 

259 """!!! abstract "Usage Documentation" 

260 [field *wrap* validators](../concepts/validators.md#field-wrap-validator) 

261 

262 A metadata class that indicates that a validation should be applied **around** the inner validation logic. 

263 

264 Attributes: 

265 func: The validator function. 

266 json_schema_input_type: The input type used to generate the appropriate 

267 JSON Schema (in validation mode). The actual input type is `Any`. 

268 

269 ```python 

270 from datetime import datetime 

271 from typing import Annotated 

272 

273 from pydantic import BaseModel, ValidationError, WrapValidator 

274 

275 def validate_timestamp(v, handler): 

276 if v == 'now': 

277 # we don't want to bother with further validation, just return the new value 

278 return datetime.now() 

279 try: 

280 return handler(v) 

281 except ValidationError: 

282 # validation failed, in this case we want to return a default value 

283 return datetime(2000, 1, 1) 

284 

285 MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)] 

286 

287 class Model(BaseModel): 

288 a: MyTimestamp 

289 

290 print(Model(a='now').a) 

291 #> 2032-01-02 03:04:05.000006 

292 print(Model(a='invalid').a) 

293 #> 2000-01-01 00:00:00 

294 ``` 

295 """ 

296 

297 func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction 

298 json_schema_input_type: Any = PydanticUndefined 

299 

300 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: 

301 schema = handler(source_type) 

302 input_schema = ( 

303 None 

304 if self.json_schema_input_type is PydanticUndefined 

305 else handler.generate_schema(self.json_schema_input_type) 

306 ) 

307 

308 info_arg = _inspect_validator(self.func, mode='wrap', type='field') 

309 if info_arg: 

310 func = cast(core_schema.WithInfoWrapValidatorFunction, self.func) 

311 return core_schema.with_info_wrap_validator_function( 

312 func, 

313 schema=schema, 

314 json_schema_input_schema=input_schema, 

315 ) 

316 else: 

317 func = cast(core_schema.NoInfoWrapValidatorFunction, self.func) 

318 return core_schema.no_info_wrap_validator_function( 

319 func, 

320 schema=schema, 

321 json_schema_input_schema=input_schema, 

322 ) 

323 

324 @classmethod 

325 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: 

326 return cls( 

327 func=decorator.func, 

328 json_schema_input_type=decorator.info.json_schema_input_type, 

329 ) 

330 

331 

332if TYPE_CHECKING: 

333 

334 class _OnlyValueValidatorClsMethod(Protocol): 

335 def __call__(self, cls: Any, value: Any, /) -> Any: ... 

336 

337 class _V2ValidatorClsMethod(Protocol): 

338 def __call__(self, cls: Any, value: Any, info: core_schema.ValidationInfo[Any], /) -> Any: ... 

339 

340 class _OnlyValueWrapValidatorClsMethod(Protocol): 

341 def __call__(self, cls: Any, value: Any, handler: core_schema.ValidatorFunctionWrapHandler, /) -> Any: ... 

342 

343 class _V2WrapValidatorClsMethod(Protocol): 

344 def __call__( 

345 self, 

346 cls: Any, 

347 value: Any, 

348 handler: core_schema.ValidatorFunctionWrapHandler, 

349 info: core_schema.ValidationInfo[Any], 

350 /, 

351 ) -> Any: ... 

352 

353 _V2Validator = Union[ 

354 _V2ValidatorClsMethod, 

355 core_schema.WithInfoValidatorFunction, 

356 _OnlyValueValidatorClsMethod, 

357 core_schema.NoInfoValidatorFunction, 

358 ] 

359 

360 _V2WrapValidator = Union[ 

361 _V2WrapValidatorClsMethod, 

362 core_schema.WithInfoWrapValidatorFunction, 

363 _OnlyValueWrapValidatorClsMethod, 

364 core_schema.NoInfoWrapValidatorFunction, 

365 ] 

366 

367 _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]] 

368 

369 _V2BeforeAfterOrPlainValidatorType = TypeVar( 

370 '_V2BeforeAfterOrPlainValidatorType', 

371 bound=Union[_V2Validator, _PartialClsOrStaticMethod], 

372 ) 

373 _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=Union[_V2WrapValidator, _PartialClsOrStaticMethod]) 

374 

375FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain'] 

376 

377 

378@overload 

379def field_validator( 

380 field: str, 

381 /, 

382 *fields: str, 

383 mode: Literal['wrap'], 

384 check_fields: bool | None = ..., 

385 json_schema_input_type: Any = ..., 

386) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: ... 

387 

388 

389@overload 

390def field_validator( 

391 field: str, 

392 /, 

393 *fields: str, 

394 mode: Literal['before', 'plain'], 

395 check_fields: bool | None = ..., 

396 json_schema_input_type: Any = ..., 

397) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ... 

398 

399 

400@overload 

401def field_validator( 

402 field: str, 

403 /, 

404 *fields: str, 

405 mode: Literal['after'] = ..., 

406 check_fields: bool | None = ..., 

407) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ... 

408 

409 

410def field_validator( 

411 field: str, 

412 /, 

413 *fields: str, 

414 mode: FieldValidatorModes = 'after', 

415 check_fields: bool | None = None, 

416 json_schema_input_type: Any = PydanticUndefined, 

417) -> Callable[[Any], Any]: 

418 """!!! abstract "Usage Documentation" 

419 [field validators](../concepts/validators.md#field-validators) 

420 

421 Decorate methods on the class indicating that they should be used to validate fields. 

422 

423 Example usage: 

424 ```python 

425 from typing import Any 

426 

427 from pydantic import ( 

428 BaseModel, 

429 ValidationError, 

430 field_validator, 

431 ) 

432 

433 class Model(BaseModel): 

434 a: str 

435 

436 @field_validator('a') 

437 @classmethod 

438 def ensure_foobar(cls, v: Any): 

439 if 'foobar' not in v: 

440 raise ValueError('"foobar" not found in a') 

441 return v 

442 

443 print(repr(Model(a='this is foobar good'))) 

444 #> Model(a='this is foobar good') 

445 

446 try: 

447 Model(a='snap') 

448 except ValidationError as exc_info: 

449 print(exc_info) 

450 ''' 

451 1 validation error for Model 

452 a 

453 Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str] 

454 ''' 

455 ``` 

456 

457 For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators). 

458 

459 Args: 

460 field: The first field the `field_validator` should be called on; this is separate 

461 from `fields` to ensure an error is raised if you don't pass at least one. 

462 *fields: Additional field(s) the `field_validator` should be called on. 

463 mode: Specifies whether to validate the fields before or after validation. 

464 check_fields: Whether to check that the fields actually exist on the model. 

465 json_schema_input_type: The input type of the function. This is only used to generate 

466 the appropriate JSON Schema (in validation mode) and can only specified 

467 when `mode` is either `'before'`, `'plain'` or `'wrap'`. 

468 

469 Returns: 

470 A decorator that can be used to decorate a function to be used as a field_validator. 

471 

472 Raises: 

473 PydanticUserError: 

474 - If `@field_validator` is used bare (with no fields). 

475 - If the args passed to `@field_validator` as fields are not strings. 

476 - If `@field_validator` applied to instance methods. 

477 """ 

478 if isinstance(field, FunctionType): 

479 raise PydanticUserError( 

480 '`@field_validator` should be used with fields and keyword arguments, not bare. ' 

481 "E.g. usage should be `@validator('<field_name>', ...)`", 

482 code='validator-no-fields', 

483 ) 

484 

485 if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined: 

486 raise PydanticUserError( 

487 f"`json_schema_input_type` can't be used when mode is set to {mode!r}", 

488 code='validator-input-type', 

489 ) 

490 

491 if json_schema_input_type is PydanticUndefined and mode == 'plain': 

492 json_schema_input_type = Any 

493 

494 fields = field, *fields 

495 if not all(isinstance(field, str) for field in fields): 

496 raise PydanticUserError( 

497 '`@field_validator` fields should be passed as separate string args. ' 

498 "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`", 

499 code='validator-invalid-fields', 

500 ) 

501 

502 def dec( 

503 f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any], 

504 ) -> _decorators.PydanticDescriptorProxy[Any]: 

505 if _decorators.is_instance_method_from_sig(f): 

506 raise PydanticUserError( 

507 '`@field_validator` cannot be applied to instance methods', code='validator-instance-method' 

508 ) 

509 

510 # auto apply the @classmethod decorator 

511 f = _decorators.ensure_classmethod_based_on_signature(f) 

512 

513 dec_info = _decorators.FieldValidatorDecoratorInfo( 

514 fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type 

515 ) 

516 return _decorators.PydanticDescriptorProxy(f, dec_info) 

517 

518 return dec 

519 

520 

521_ModelType = TypeVar('_ModelType') 

522_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True) 

523 

524 

525class ModelWrapValidatorHandler(core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]): 

526 """`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`.""" 

527 

528 def __call__( # noqa: D102 

529 self, 

530 value: Any, 

531 outer_location: str | int | None = None, 

532 /, 

533 ) -> _ModelTypeCo: # pragma: no cover 

534 ... 

535 

536 

537class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]): 

538 """A `@model_validator` decorated function signature. 

539 This is used when `mode='wrap'` and the function does not have info argument. 

540 """ 

541 

542 def __call__( # noqa: D102 

543 self, 

544 cls: type[_ModelType], 

545 # this can be a dict, a model instance 

546 # or anything else that gets passed to validate_python 

547 # thus validators _must_ handle all cases 

548 value: Any, 

549 handler: ModelWrapValidatorHandler[_ModelType], 

550 /, 

551 ) -> _ModelType: ... 

552 

553 

554class ModelWrapValidator(Protocol[_ModelType]): 

555 """A `@model_validator` decorated function signature. This is used when `mode='wrap'`.""" 

556 

557 def __call__( # noqa: D102 

558 self, 

559 cls: type[_ModelType], 

560 # this can be a dict, a model instance 

561 # or anything else that gets passed to validate_python 

562 # thus validators _must_ handle all cases 

563 value: Any, 

564 handler: ModelWrapValidatorHandler[_ModelType], 

565 info: core_schema.ValidationInfo, 

566 /, 

567 ) -> _ModelType: ... 

568 

569 

570class FreeModelBeforeValidatorWithoutInfo(Protocol): 

571 """A `@model_validator` decorated function signature. 

572 This is used when `mode='before'` and the function does not have info argument. 

573 """ 

574 

575 def __call__( # noqa: D102 

576 self, 

577 # this can be a dict, a model instance 

578 # or anything else that gets passed to validate_python 

579 # thus validators _must_ handle all cases 

580 value: Any, 

581 /, 

582 ) -> Any: ... 

583 

584 

585class ModelBeforeValidatorWithoutInfo(Protocol): 

586 """A `@model_validator` decorated function signature. 

587 This is used when `mode='before'` and the function does not have info argument. 

588 """ 

589 

590 def __call__( # noqa: D102 

591 self, 

592 cls: Any, 

593 # this can be a dict, a model instance 

594 # or anything else that gets passed to validate_python 

595 # thus validators _must_ handle all cases 

596 value: Any, 

597 /, 

598 ) -> Any: ... 

599 

600 

601class FreeModelBeforeValidator(Protocol): 

602 """A `@model_validator` decorated function signature. This is used when `mode='before'`.""" 

603 

604 def __call__( # noqa: D102 

605 self, 

606 # this can be a dict, a model instance 

607 # or anything else that gets passed to validate_python 

608 # thus validators _must_ handle all cases 

609 value: Any, 

610 info: core_schema.ValidationInfo[Any], 

611 /, 

612 ) -> Any: ... 

613 

614 

615class ModelBeforeValidator(Protocol): 

616 """A `@model_validator` decorated function signature. This is used when `mode='before'`.""" 

617 

618 def __call__( # noqa: D102 

619 self, 

620 cls: Any, 

621 # this can be a dict, a model instance 

622 # or anything else that gets passed to validate_python 

623 # thus validators _must_ handle all cases 

624 value: Any, 

625 info: core_schema.ValidationInfo[Any], 

626 /, 

627 ) -> Any: ... 

628 

629 

630ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType] 

631"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not 

632have info argument. 

633""" 

634 

635ModelAfterValidator = Callable[[_ModelType, core_schema.ValidationInfo[Any]], _ModelType] 

636"""A `@model_validator` decorated function signature. This is used when `mode='after'`.""" 

637 

638_AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]] 

639_AnyModelBeforeValidator = Union[ 

640 FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo 

641] 

642_AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]] 

643 

644 

645@overload 

646def model_validator( 

647 *, 

648 mode: Literal['wrap'], 

649) -> Callable[ 

650 [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] 

651]: ... 

652 

653 

654@overload 

655def model_validator( 

656 *, 

657 mode: Literal['before'], 

658) -> Callable[ 

659 [_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] 

660]: ... 

661 

662 

663@overload 

664def model_validator( 

665 *, 

666 mode: Literal['after'], 

667) -> Callable[ 

668 [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] 

669]: ... 

670 

671 

672def model_validator( 

673 *, 

674 mode: Literal['wrap', 'before', 'after'], 

675) -> Any: 

676 """!!! abstract "Usage Documentation" 

677 [Model Validators](../concepts/validators.md#model-validators) 

678 

679 Decorate model methods for validation purposes. 

680 

681 Example usage: 

682 ```python 

683 from typing_extensions import Self 

684 

685 from pydantic import BaseModel, ValidationError, model_validator 

686 

687 class Square(BaseModel): 

688 width: float 

689 height: float 

690 

691 @model_validator(mode='after') 

692 def verify_square(self) -> Self: 

693 if self.width != self.height: 

694 raise ValueError('width and height do not match') 

695 return self 

696 

697 s = Square(width=1, height=1) 

698 print(repr(s)) 

699 #> Square(width=1.0, height=1.0) 

700 

701 try: 

702 Square(width=1, height=2) 

703 except ValidationError as e: 

704 print(e) 

705 ''' 

706 1 validation error for Square 

707 Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict] 

708 ''' 

709 ``` 

710 

711 For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators). 

712 

713 Args: 

714 mode: A required string literal that specifies the validation mode. 

715 It can be one of the following: 'wrap', 'before', or 'after'. 

716 

717 Returns: 

718 A decorator that can be used to decorate a function to be used as a model validator. 

719 """ 

720 

721 def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]: 

722 # auto apply the @classmethod decorator. NOTE: in V3, do not apply the conversion for 'after' validators: 

723 f = _decorators.ensure_classmethod_based_on_signature(f) 

724 if mode == 'after' and isinstance(f, classmethod): 

725 warnings.warn( 

726 category=PydanticDeprecatedSince212, 

727 message=( 

728 "Using `@model_validator` with mode='after' on a classmethod is deprecated. Instead, use an instance method. " 

729 f'See the documentation at https://docs.pydantic.dev/{version_short()}/concepts/validators/#model-after-validator.' 

730 ), 

731 stacklevel=2, 

732 ) 

733 

734 dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode) 

735 return _decorators.PydanticDescriptorProxy(f, dec_info) 

736 

737 return dec 

738 

739 

740AnyType = TypeVar('AnyType') 

741 

742 

743if TYPE_CHECKING: 

744 # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this 

745 InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence` 

746 

747else: 

748 

749 @dataclasses.dataclass(**_internal_dataclass.slots_true) 

750 class InstanceOf: 

751 '''Generic type for annotating a type that is an instance of a given class. 

752 

753 Example: 

754 ```python 

755 from pydantic import BaseModel, InstanceOf 

756 

757 class Foo: 

758 ... 

759 

760 class Bar(BaseModel): 

761 foo: InstanceOf[Foo] 

762 

763 Bar(foo=Foo()) 

764 try: 

765 Bar(foo=42) 

766 except ValidationError as e: 

767 print(e) 

768 """ 

769 [ 

770 │ { 

771 │ │ 'type': 'is_instance_of', 

772 │ │ 'loc': ('foo',), 

773 │ │ 'msg': 'Input should be an instance of Foo', 

774 │ │ 'input': 42, 

775 │ │ 'ctx': {'class': 'Foo'}, 

776 │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of' 

777 │ } 

778 ] 

779 """ 

780 ``` 

781 ''' 

782 

783 @classmethod 

784 def __class_getitem__(cls, item: AnyType) -> AnyType: 

785 return Annotated[item, cls()] 

786 

787 @classmethod 

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

789 from pydantic import PydanticSchemaGenerationError 

790 

791 # use the generic _origin_ as the second argument to isinstance when appropriate 

792 instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source) 

793 

794 try: 

795 # Try to generate the "standard" schema, which will be used when loading from JSON 

796 original_schema = handler(source) 

797 except PydanticSchemaGenerationError: 

798 # If that fails, just produce a schema that can validate from python 

799 return instance_of_schema 

800 else: 

801 # Use the "original" approach to serialization 

802 instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( 

803 function=lambda v, h: h(v), schema=original_schema 

804 ) 

805 return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema) 

806 

807 __hash__ = object.__hash__ 

808 

809 

810if TYPE_CHECKING: 

811 SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str] 

812else: 

813 

814 @dataclasses.dataclass(**_internal_dataclass.slots_true) 

815 class SkipValidation: 

816 """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be 

817 skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`. 

818 

819 This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes, 

820 and know that it is safe to skip validation for one or more of the fields. 

821 

822 Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations 

823 may not have the expected effects. Therefore, when used, this annotation should generally be the final 

824 annotation applied to a type. 

825 """ 

826 

827 def __class_getitem__(cls, item: Any) -> Any: 

828 return Annotated[item, SkipValidation()] 

829 

830 @classmethod 

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

832 with warnings.catch_warnings(): 

833 warnings.simplefilter('ignore', ArbitraryTypeWarning) 

834 original_schema = handler(source) 

835 metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]} 

836 return core_schema.any_schema( 

837 metadata=metadata, 

838 serialization=core_schema.wrap_serializer_function_ser_schema( 

839 function=lambda v, h: h(v), schema=original_schema 

840 ), 

841 ) 

842 

843 __hash__ = object.__hash__ 

844 

845 

846_FromTypeT = TypeVar('_FromTypeT') 

847 

848 

849class ValidateAs: 

850 """A helper class to validate a custom type from a type that is natively supported by Pydantic. 

851 

852 Args: 

853 from_type: The type natively supported by Pydantic to use to perform validation. 

854 instantiation_hook: A callable taking the validated type as an argument, and returning 

855 the populated custom type. 

856 

857 Example: 

858 ```python {lint="skip"} 

859 from typing import Annotated 

860 

861 from pydantic import BaseModel, TypeAdapter, ValidateAs 

862 

863 class MyCls: 

864 def __init__(self, a: int) -> None: 

865 self.a = a 

866 

867 def __repr__(self) -> str: 

868 return f"MyCls(a={self.a})" 

869 

870 class Model(BaseModel): 

871 a: int 

872 

873 

874 ta = TypeAdapter( 

875 Annotated[MyCls, ValidateAs(Model, lambda v: MyCls(a=v.a))] 

876 ) 

877 

878 print(ta.validate_python({'a': 1})) 

879 #> MyCls(a=1) 

880 ``` 

881 """ 

882 

883 # TODO: make use of PEP 747 

884 def __init__(self, from_type: type[_FromTypeT], /, instantiation_hook: Callable[[_FromTypeT], Any]) -> None: 

885 self.from_type = from_type 

886 self.instantiation_hook = instantiation_hook 

887 

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

889 schema = handler(self.from_type) 

890 return core_schema.no_info_after_validator_function( 

891 self.instantiation_hook, 

892 schema=schema, 

893 )