Spaces:
Configuration error
Configuration error
Upload folder using huggingface_hub (part 5)
Browse filesThis view is limited to 50 files because it contains too many changes. Β See raw diff
- .gitattributes +6 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/INSTALLER +1 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/METADATA +0 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/RECORD +217 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/WHEEL +4 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/licenses/LICENSE +21 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py +403 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py +228 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py +868 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py +293 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py +124 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py +212 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py +125 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py +53 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py +189 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py +785 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py +446 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py +141 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py +534 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/_migration.py +316 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/alias_generators.py +62 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/aliases.py +135 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py +122 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/class_validators.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/color.py +604 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/config.py +1296 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/dataclasses.py +413 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/datetime_parse.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/decorator.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/__init__.py +0 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py +256 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/config.py +72 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py +224 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py +284 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/json.py +141 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py +80 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py +103 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/env_settings.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/error_wrappers.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/errors.py +189 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py +1 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py +44 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py +663 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/fields.py +1892 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/functional_serializers.py +470 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/functional_validators.py +889 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/generics.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/json.py +5 -0
- .mcp-venv/lib/python3.13/site-packages/pydantic/json_schema.py +0 -0
.gitattributes
CHANGED
|
@@ -38,3 +38,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 38 |
.mcp-venv/bin/python3.13 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
.mcp-venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 40 |
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
.mcp-venv/bin/python3.13 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
.mcp-venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 40 |
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
.mcp-venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
.mcp-venv/lib/python3.13/site-packages/rpds/rpds.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
.venv/bin/python filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
.venv/bin/python3 filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
.venv/bin/python3.13 filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
.venv/lib/python3.13/site-packages/PIL/_imaging.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
.mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/METADATA
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
.mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/RECORD
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pydantic-2.13.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 2 |
+
pydantic-2.13.4.dist-info/METADATA,sha256=0eVsNBJJJRL7a1mp38Jat68qggRYXNe8oABRMCMTM5Q,109397
|
| 3 |
+
pydantic-2.13.4.dist-info/RECORD,,
|
| 4 |
+
pydantic-2.13.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
| 5 |
+
pydantic-2.13.4.dist-info/licenses/LICENSE,sha256=qeGG88oWte74QxjnpwFyE1GgDLe4rjpDlLZ7SeNSnvM,1129
|
| 6 |
+
pydantic/__init__.py,sha256=5iEnJ4wHv1OEzdKQPzaKaZKfO4pSQAC65ODrYI6_S8Y,15812
|
| 7 |
+
pydantic/__pycache__/__init__.cpython-313.pyc,,
|
| 8 |
+
pydantic/__pycache__/_migration.cpython-313.pyc,,
|
| 9 |
+
pydantic/__pycache__/alias_generators.cpython-313.pyc,,
|
| 10 |
+
pydantic/__pycache__/aliases.cpython-313.pyc,,
|
| 11 |
+
pydantic/__pycache__/annotated_handlers.cpython-313.pyc,,
|
| 12 |
+
pydantic/__pycache__/class_validators.cpython-313.pyc,,
|
| 13 |
+
pydantic/__pycache__/color.cpython-313.pyc,,
|
| 14 |
+
pydantic/__pycache__/config.cpython-313.pyc,,
|
| 15 |
+
pydantic/__pycache__/dataclasses.cpython-313.pyc,,
|
| 16 |
+
pydantic/__pycache__/datetime_parse.cpython-313.pyc,,
|
| 17 |
+
pydantic/__pycache__/decorator.cpython-313.pyc,,
|
| 18 |
+
pydantic/__pycache__/env_settings.cpython-313.pyc,,
|
| 19 |
+
pydantic/__pycache__/error_wrappers.cpython-313.pyc,,
|
| 20 |
+
pydantic/__pycache__/errors.cpython-313.pyc,,
|
| 21 |
+
pydantic/__pycache__/fields.cpython-313.pyc,,
|
| 22 |
+
pydantic/__pycache__/functional_serializers.cpython-313.pyc,,
|
| 23 |
+
pydantic/__pycache__/functional_validators.cpython-313.pyc,,
|
| 24 |
+
pydantic/__pycache__/generics.cpython-313.pyc,,
|
| 25 |
+
pydantic/__pycache__/json.cpython-313.pyc,,
|
| 26 |
+
pydantic/__pycache__/json_schema.cpython-313.pyc,,
|
| 27 |
+
pydantic/__pycache__/main.cpython-313.pyc,,
|
| 28 |
+
pydantic/__pycache__/mypy.cpython-313.pyc,,
|
| 29 |
+
pydantic/__pycache__/networks.cpython-313.pyc,,
|
| 30 |
+
pydantic/__pycache__/parse.cpython-313.pyc,,
|
| 31 |
+
pydantic/__pycache__/root_model.cpython-313.pyc,,
|
| 32 |
+
pydantic/__pycache__/schema.cpython-313.pyc,,
|
| 33 |
+
pydantic/__pycache__/tools.cpython-313.pyc,,
|
| 34 |
+
pydantic/__pycache__/type_adapter.cpython-313.pyc,,
|
| 35 |
+
pydantic/__pycache__/types.cpython-313.pyc,,
|
| 36 |
+
pydantic/__pycache__/typing.cpython-313.pyc,,
|
| 37 |
+
pydantic/__pycache__/utils.cpython-313.pyc,,
|
| 38 |
+
pydantic/__pycache__/validate_call_decorator.cpython-313.pyc,,
|
| 39 |
+
pydantic/__pycache__/validators.cpython-313.pyc,,
|
| 40 |
+
pydantic/__pycache__/version.cpython-313.pyc,,
|
| 41 |
+
pydantic/__pycache__/warnings.cpython-313.pyc,,
|
| 42 |
+
pydantic/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 43 |
+
pydantic/_internal/__pycache__/__init__.cpython-313.pyc,,
|
| 44 |
+
pydantic/_internal/__pycache__/_config.cpython-313.pyc,,
|
| 45 |
+
pydantic/_internal/__pycache__/_core_metadata.cpython-313.pyc,,
|
| 46 |
+
pydantic/_internal/__pycache__/_core_utils.cpython-313.pyc,,
|
| 47 |
+
pydantic/_internal/__pycache__/_dataclasses.cpython-313.pyc,,
|
| 48 |
+
pydantic/_internal/__pycache__/_decorators.cpython-313.pyc,,
|
| 49 |
+
pydantic/_internal/__pycache__/_decorators_v1.cpython-313.pyc,,
|
| 50 |
+
pydantic/_internal/__pycache__/_discriminated_union.cpython-313.pyc,,
|
| 51 |
+
pydantic/_internal/__pycache__/_docs_extraction.cpython-313.pyc,,
|
| 52 |
+
pydantic/_internal/__pycache__/_fields.cpython-313.pyc,,
|
| 53 |
+
pydantic/_internal/__pycache__/_forward_ref.cpython-313.pyc,,
|
| 54 |
+
pydantic/_internal/__pycache__/_generate_schema.cpython-313.pyc,,
|
| 55 |
+
pydantic/_internal/__pycache__/_generics.cpython-313.pyc,,
|
| 56 |
+
pydantic/_internal/__pycache__/_git.cpython-313.pyc,,
|
| 57 |
+
pydantic/_internal/__pycache__/_import_utils.cpython-313.pyc,,
|
| 58 |
+
pydantic/_internal/__pycache__/_internal_dataclass.cpython-313.pyc,,
|
| 59 |
+
pydantic/_internal/__pycache__/_known_annotated_metadata.cpython-313.pyc,,
|
| 60 |
+
pydantic/_internal/__pycache__/_mock_val_ser.cpython-313.pyc,,
|
| 61 |
+
pydantic/_internal/__pycache__/_model_construction.cpython-313.pyc,,
|
| 62 |
+
pydantic/_internal/__pycache__/_namespace_utils.cpython-313.pyc,,
|
| 63 |
+
pydantic/_internal/__pycache__/_repr.cpython-313.pyc,,
|
| 64 |
+
pydantic/_internal/__pycache__/_schema_gather.cpython-313.pyc,,
|
| 65 |
+
pydantic/_internal/__pycache__/_schema_generation_shared.cpython-313.pyc,,
|
| 66 |
+
pydantic/_internal/__pycache__/_serializers.cpython-313.pyc,,
|
| 67 |
+
pydantic/_internal/__pycache__/_signature.cpython-313.pyc,,
|
| 68 |
+
pydantic/_internal/__pycache__/_typing_extra.cpython-313.pyc,,
|
| 69 |
+
pydantic/_internal/__pycache__/_utils.cpython-313.pyc,,
|
| 70 |
+
pydantic/_internal/__pycache__/_validate_call.cpython-313.pyc,,
|
| 71 |
+
pydantic/_internal/__pycache__/_validators.cpython-313.pyc,,
|
| 72 |
+
pydantic/_internal/_config.py,sha256=Rzys1Joffn4JczElcYDqsZLRgBgHn2lYWqDR55oASPA,14839
|
| 73 |
+
pydantic/_internal/_core_metadata.py,sha256=Y_g2t3i7uluK-wXCZvzJfRFMPUM23aBYLfae4FzBPy0,5162
|
| 74 |
+
pydantic/_internal/_core_utils.py,sha256=1jru4VbJ0x63R6dtVcuOI-dKQTC_d_lSnJWEBQzGNEQ,6487
|
| 75 |
+
pydantic/_internal/_dataclasses.py,sha256=Zgqcm1WaJLBwTQQC5mGKNowjlTgX3mfX_J5e2vd24lM,13188
|
| 76 |
+
pydantic/_internal/_decorators.py,sha256=RDEG_Jau5NiJcfO0xgdT7EOgsU1LgWIYlX7wN5rYtVs,33620
|
| 77 |
+
pydantic/_internal/_decorators_v1.py,sha256=tfdfdpQKY4R2XCOwqHbZeoQMur6VNigRrfhudXBHx38,6185
|
| 78 |
+
pydantic/_internal/_discriminated_union.py,sha256=JLx_MVLep7Mxl1zbpdNZjvHDcz-J3OEW6WcdV184dcM,26255
|
| 79 |
+
pydantic/_internal/_docs_extraction.py,sha256=fyznSAHh5AzohnXZStV0HvH-nRbavNHPyg-knx-S_EE,4127
|
| 80 |
+
pydantic/_internal/_fields.py,sha256=hXeb-zodGwTDvG9OK1um18P64b7cqHb0GOngP4jrgcY,31557
|
| 81 |
+
pydantic/_internal/_forward_ref.py,sha256=5n3Y7-3AKLn8_FS3Yc7KutLiPUhyXmAtkEZOaFnonwM,611
|
| 82 |
+
pydantic/_internal/_generate_schema.py,sha256=PjogUawIXmf8LuLNe9seJph7WLW4MJ7-GBsXwCsQC9Q,136348
|
| 83 |
+
pydantic/_internal/_generics.py,sha256=CXjcInlvci8VejaWn1f39kv0AcfJL0R523qRLFZVD-s,23393
|
| 84 |
+
pydantic/_internal/_git.py,sha256=IwPh3DPfa2Xq3rBuB9Nx8luR2A1i69QdeTfWWXIuCVg,809
|
| 85 |
+
pydantic/_internal/_import_utils.py,sha256=TRhxD5OuY6CUosioBdBcJUs0om7IIONiZdYAV7zQ8jM,402
|
| 86 |
+
pydantic/_internal/_internal_dataclass.py,sha256=_bedc1XbuuygRGiLZqkUkwwFpQaoR1hKLlR501nyySY,144
|
| 87 |
+
pydantic/_internal/_known_annotated_metadata.py,sha256=PynQIFQ61__4Gcrzn0D5ENllg7jPq_cxoLTmuFQBY88,16805
|
| 88 |
+
pydantic/_internal/_mock_val_ser.py,sha256=wmRRFSBvqfcLbI41PsFliB4u2AZ3mJpZeiERbD3xKTo,8885
|
| 89 |
+
pydantic/_internal/_model_construction.py,sha256=JoKmY4JrDBu3nG_tCIrJgtJJE1uq6v29TvTz5ElHE5g,38928
|
| 90 |
+
pydantic/_internal/_namespace_utils.py,sha256=hl3-TRAr82U2jTyPP3t-QqsvKLirxtkLfNfrN-fp0x8,12878
|
| 91 |
+
pydantic/_internal/_repr.py,sha256=jQfnJuyDxQpSRNhG29II9PX8e4Nv2qWZrEw2lqih3UE,5172
|
| 92 |
+
pydantic/_internal/_schema_gather.py,sha256=8nJ-uM6Y4z6xpasnGonEMubtNVX_mxeeRDFmd_qMVLA,9052
|
| 93 |
+
pydantic/_internal/_schema_generation_shared.py,sha256=F_rbQbrkoomgxsskdHpP0jUJ7TCfe0BADAEkq6CJ4nM,4842
|
| 94 |
+
pydantic/_internal/_serializers.py,sha256=YIWvSmAR5fnbGSWCOQduWt1yB4ZQY42eAruc-enrb6c,1491
|
| 95 |
+
pydantic/_internal/_signature.py,sha256=i_b6wtluiVWZRh1ZY8UvB2UZziP1KjqSXZgC-HxwOT0,6808
|
| 96 |
+
pydantic/_internal/_typing_extra.py,sha256=dDxqF46lzuqCoKLrAH_k95EDbayEeKb2lHjuTJ5OBoY,31574
|
| 97 |
+
pydantic/_internal/_utils.py,sha256=gN48BsR-FDrJDibCmo69ttQg67WbuFrdy_1NQL3cvLI,15959
|
| 98 |
+
pydantic/_internal/_validate_call.py,sha256=OD_BspHaL9FKzZ9XrndhiEuMnjF3SRIJUHtwv6yUffU,5366
|
| 99 |
+
pydantic/_internal/_validators.py,sha256=7GTjXXWFMLib4dxQ-HeaiHlAZiR2B2G8byCYMGrmQ48,20563
|
| 100 |
+
pydantic/_migration.py,sha256=VF73LRCUz3Irb5xVt13jb3NAcXVnEF6T1-J0OLfeZ5A,12160
|
| 101 |
+
pydantic/alias_generators.py,sha256=KM1n3u4JfLSBl1UuYg3hoYHzXJD-yvgrnq8u1ccwh_A,2124
|
| 102 |
+
pydantic/aliases.py,sha256=vhCHyoSWnX-EJ-wWb5qj4xyRssgGWnTQfzQp4GSZ9ug,4937
|
| 103 |
+
pydantic/annotated_handlers.py,sha256=WfyFSqwoEIFXBh7T73PycKloI1DiX45GWi0-JOsCR4Y,4407
|
| 104 |
+
pydantic/class_validators.py,sha256=i_V3j-PYdGLSLmj_IJZekTRjunO8SIVz8LMlquPyP7E,148
|
| 105 |
+
pydantic/color.py,sha256=AzqGfVQHF92_ZctDcue0DM4yTp2P6tekkwRINTWrLIo,21481
|
| 106 |
+
pydantic/config.py,sha256=o1P67FMWIQG-_RfGtKislkHwo4pXm_6jfsR41P88v78,44533
|
| 107 |
+
pydantic/dataclasses.py,sha256=4X9We0jj1KLwBtvYSkAFXNon46zrpBmRZANf4LfwbXg,18963
|
| 108 |
+
pydantic/datetime_parse.py,sha256=QC-WgMxMr_wQ_mNXUS7AVf-2hLEhvvsPY1PQyhSGOdk,150
|
| 109 |
+
pydantic/decorator.py,sha256=YX-jUApu5AKaVWKPoaV-n-4l7UbS69GEt9Ra3hszmKI,145
|
| 110 |
+
pydantic/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 111 |
+
pydantic/deprecated/__pycache__/__init__.cpython-313.pyc,,
|
| 112 |
+
pydantic/deprecated/__pycache__/class_validators.cpython-313.pyc,,
|
| 113 |
+
pydantic/deprecated/__pycache__/config.cpython-313.pyc,,
|
| 114 |
+
pydantic/deprecated/__pycache__/copy_internals.cpython-313.pyc,,
|
| 115 |
+
pydantic/deprecated/__pycache__/decorator.cpython-313.pyc,,
|
| 116 |
+
pydantic/deprecated/__pycache__/json.cpython-313.pyc,,
|
| 117 |
+
pydantic/deprecated/__pycache__/parse.cpython-313.pyc,,
|
| 118 |
+
pydantic/deprecated/__pycache__/tools.cpython-313.pyc,,
|
| 119 |
+
pydantic/deprecated/class_validators.py,sha256=n0jYQOcb5YQiw0b7YXyi7NPiYdV7ujWR4KyZumjTPok,10281
|
| 120 |
+
pydantic/deprecated/config.py,sha256=k_lsVk57paxLJOcBueH07cu1OgEgWdVBxm6lfaC3CCU,2663
|
| 121 |
+
pydantic/deprecated/copy_internals.py,sha256=Ghd-vkMd5EYCCgyCGtPKO58np9cEKBQC6qkBeIEFI2g,7618
|
| 122 |
+
pydantic/deprecated/decorator.py,sha256=TBm6bJ7wJsNih_8Wq5IzDcwP32m9_vfxs96desLuk00,10845
|
| 123 |
+
pydantic/deprecated/json.py,sha256=HlWCG35RRrxyzuTS6LTQiZBwRhmDZWmeqQH8rLW6wA8,4657
|
| 124 |
+
pydantic/deprecated/parse.py,sha256=Gzd6b_g8zJXcuE7QRq5adhx_EMJahXfcpXCF0RgrqqI,2511
|
| 125 |
+
pydantic/deprecated/tools.py,sha256=Nrm9oFRZWp8-jlfvPgJILEsywp4YzZD52XIGPDLxHcI,3330
|
| 126 |
+
pydantic/env_settings.py,sha256=6IHeeWEqlUPRUv3V-AXiF_W91fg2Jw_M3O0l34J_eyA,148
|
| 127 |
+
pydantic/error_wrappers.py,sha256=RK6mqATc9yMD-KBD9IJS9HpKCprWHd8wo84Bnm-3fR8,150
|
| 128 |
+
pydantic/errors.py,sha256=DrECPCWhSYrQ8Ba4O8hKzIAM2i9GBHTXWALzaFDpLf4,6013
|
| 129 |
+
pydantic/experimental/__init__.py,sha256=QT7rKYdDsCiTJ9GEjmsQdWHScwpKrrNkGq6vqONP6RQ,104
|
| 130 |
+
pydantic/experimental/__pycache__/__init__.cpython-313.pyc,,
|
| 131 |
+
pydantic/experimental/__pycache__/arguments_schema.cpython-313.pyc,,
|
| 132 |
+
pydantic/experimental/__pycache__/missing_sentinel.cpython-313.pyc,,
|
| 133 |
+
pydantic/experimental/__pycache__/pipeline.cpython-313.pyc,,
|
| 134 |
+
pydantic/experimental/arguments_schema.py,sha256=EFnjX_ulp-tPyUjQX5pmQtug1OFL_Acc8bcMbLd-fVY,1866
|
| 135 |
+
pydantic/experimental/missing_sentinel.py,sha256=hQejgtF00wUuQMni9429evg-eXyIwpKvjsD8ofqfj-w,127
|
| 136 |
+
pydantic/experimental/pipeline.py,sha256=auoW6l6g1FC41LciPmsI1M6ncf00Szde9B3C-yvb9mI,23956
|
| 137 |
+
pydantic/fields.py,sha256=a8ZhJfI8FD6TQDD7-cWMm1ZXlQva0wMdT2Ey5VvVe-M,82023
|
| 138 |
+
pydantic/functional_serializers.py,sha256=zwRAjZusORtEbtxpVU20kg8FpemyZz4Fq6wJk1mpwYQ,18117
|
| 139 |
+
pydantic/functional_validators.py,sha256=7p-4jvP__9jZyvcdXiide5pAa_JlM7fJ1BgkqbjxSWM,31724
|
| 140 |
+
pydantic/generics.py,sha256=0ZqZ9O9annIj_3mGBRqps4htey3b5lV1-d2tUxPMMnA,144
|
| 141 |
+
pydantic/json.py,sha256=ZH8RkI7h4Bz-zp8OdTAxbJUoVvcoU-jhMdRZ0B-k0xc,140
|
| 142 |
+
pydantic/json_schema.py,sha256=da3hQ9vQPLEhPsrDnUlijfQ1fuCCViGVnUzOrAZLgDs,125955
|
| 143 |
+
pydantic/main.py,sha256=NbhCz-ku8wDgYLQMBi75Ov7hywdc8LewA3oUNIZ8JVQ,85334
|
| 144 |
+
pydantic/mypy.py,sha256=sYmmZrL_GvoYSLcBuPRRDfSfgQWehkzw_ZvNkwDY2ME,60971
|
| 145 |
+
pydantic/networks.py,sha256=gCB96gt0G7tiVDhVnJfpKr1ARL5qkH-SPZkuNkmG2O4,42102
|
| 146 |
+
pydantic/parse.py,sha256=wkd82dgtvWtD895U_I6E1htqMlGhBSYEV39cuBSeo3A,141
|
| 147 |
+
pydantic/plugin/__init__.py,sha256=a7Tw366U6K3kltCCNZY76nc9ss-7uGGQ40TXad9OypQ,7333
|
| 148 |
+
pydantic/plugin/__pycache__/__init__.cpython-313.pyc,,
|
| 149 |
+
pydantic/plugin/__pycache__/_loader.cpython-313.pyc,,
|
| 150 |
+
pydantic/plugin/__pycache__/_schema_validator.cpython-313.pyc,,
|
| 151 |
+
pydantic/plugin/_loader.py,sha256=hAjgSljoIhGx3AVpIpuqw5SPttBNNeGBSTrqSMnNiJk,2213
|
| 152 |
+
pydantic/plugin/_schema_validator.py,sha256=5M5Ic1bZnjhNDxtRDVKbRPTQ6po6QuKMY7MguMkHeW0,5445
|
| 153 |
+
pydantic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 154 |
+
pydantic/root_model.py,sha256=cOMoeWdp536KF85uRIcW-oz1O7d5GRCpx9HTS4-1uf8,6394
|
| 155 |
+
pydantic/schema.py,sha256=Vqqjvq_LnapVknebUd3Bp_J1p2gXZZnZRgL48bVEG7o,142
|
| 156 |
+
pydantic/tools.py,sha256=iHQpd8SJ5DCTtPV5atAV06T89bjSaMFeZZ2LX9lasZY,141
|
| 157 |
+
pydantic/type_adapter.py,sha256=T05g8WQczBsVU_35RdKxgjIi7Y7LIip67pF3NI7X4GE,36123
|
| 158 |
+
pydantic/types.py,sha256=fpYcGnAncK4QjaFm3jZtqYwxJYzB7rJJPfAl89emzyQ,105961
|
| 159 |
+
pydantic/typing.py,sha256=P7feA35MwTcLsR1uL7db0S-oydBxobmXa55YDoBgajQ,138
|
| 160 |
+
pydantic/utils.py,sha256=15nR2QpqTBFlQV4TNtTItMyTJx_fbyV-gPmIEY1Gooc,141
|
| 161 |
+
pydantic/v1/__init__.py,sha256=SxQPklgBs4XHJwE6BZ9qoewYoGiNyYUnmHzEFCZbfnI,2946
|
| 162 |
+
pydantic/v1/__pycache__/__init__.cpython-313.pyc,,
|
| 163 |
+
pydantic/v1/__pycache__/_hypothesis_plugin.cpython-313.pyc,,
|
| 164 |
+
pydantic/v1/__pycache__/annotated_types.cpython-313.pyc,,
|
| 165 |
+
pydantic/v1/__pycache__/class_validators.cpython-313.pyc,,
|
| 166 |
+
pydantic/v1/__pycache__/color.cpython-313.pyc,,
|
| 167 |
+
pydantic/v1/__pycache__/config.cpython-313.pyc,,
|
| 168 |
+
pydantic/v1/__pycache__/dataclasses.cpython-313.pyc,,
|
| 169 |
+
pydantic/v1/__pycache__/datetime_parse.cpython-313.pyc,,
|
| 170 |
+
pydantic/v1/__pycache__/decorator.cpython-313.pyc,,
|
| 171 |
+
pydantic/v1/__pycache__/env_settings.cpython-313.pyc,,
|
| 172 |
+
pydantic/v1/__pycache__/error_wrappers.cpython-313.pyc,,
|
| 173 |
+
pydantic/v1/__pycache__/errors.cpython-313.pyc,,
|
| 174 |
+
pydantic/v1/__pycache__/fields.cpython-313.pyc,,
|
| 175 |
+
pydantic/v1/__pycache__/generics.cpython-313.pyc,,
|
| 176 |
+
pydantic/v1/__pycache__/json.cpython-313.pyc,,
|
| 177 |
+
pydantic/v1/__pycache__/main.cpython-313.pyc,,
|
| 178 |
+
pydantic/v1/__pycache__/mypy.cpython-313.pyc,,
|
| 179 |
+
pydantic/v1/__pycache__/networks.cpython-313.pyc,,
|
| 180 |
+
pydantic/v1/__pycache__/parse.cpython-313.pyc,,
|
| 181 |
+
pydantic/v1/__pycache__/schema.cpython-313.pyc,,
|
| 182 |
+
pydantic/v1/__pycache__/tools.cpython-313.pyc,,
|
| 183 |
+
pydantic/v1/__pycache__/types.cpython-313.pyc,,
|
| 184 |
+
pydantic/v1/__pycache__/typing.cpython-313.pyc,,
|
| 185 |
+
pydantic/v1/__pycache__/utils.cpython-313.pyc,,
|
| 186 |
+
pydantic/v1/__pycache__/validators.cpython-313.pyc,,
|
| 187 |
+
pydantic/v1/__pycache__/version.cpython-313.pyc,,
|
| 188 |
+
pydantic/v1/_hypothesis_plugin.py,sha256=5ES5xWuw1FQAsymLezy8QgnVz0ZpVfU3jkmT74H27VQ,14847
|
| 189 |
+
pydantic/v1/annotated_types.py,sha256=uk2NAAxqiNELKjiHhyhxKaIOh8F1lYW_LzrW3X7oZBc,3157
|
| 190 |
+
pydantic/v1/class_validators.py,sha256=ULOaIUgYUDBsHL7EEVEarcM-UubKUggoN8hSbDonsFE,14672
|
| 191 |
+
pydantic/v1/color.py,sha256=iZABLYp6OVoo2AFkP9Ipri_wSc6-Kklu8YuhSartd5g,16844
|
| 192 |
+
pydantic/v1/config.py,sha256=a6P0Wer9x4cbwKW7Xv8poSUqM4WP-RLWwX6YMpYq9AA,6532
|
| 193 |
+
pydantic/v1/dataclasses.py,sha256=784cqvInbwIPWr9usfpX3ch7z4t3J2tTK6N067_wk1o,18172
|
| 194 |
+
pydantic/v1/datetime_parse.py,sha256=4Qy1kQpq3rNVZJeIHeSPDpuS2Bvhp1KPtzJG1xu-H00,7724
|
| 195 |
+
pydantic/v1/decorator.py,sha256=zaaxxxoWPCm818D1bs0yhapRjXm32V8G0ZHWCdM1uXA,10339
|
| 196 |
+
pydantic/v1/env_settings.py,sha256=A9VXwtRl02AY-jH0C0ouy5VNw3fi6F_pkzuHDjgAAOM,14105
|
| 197 |
+
pydantic/v1/error_wrappers.py,sha256=6625Mfw9qkC2NwitB_JFAWe8B-Xv6zBU7rL9k28tfyo,5196
|
| 198 |
+
pydantic/v1/errors.py,sha256=mIwPED5vGM5Q5v4C4Z1JPldTRH-omvEylH6ksMhOmPw,17726
|
| 199 |
+
pydantic/v1/fields.py,sha256=VqWJCriUNiEyptXroDVJ501JpVA0en2VANcksqXL2b8,50649
|
| 200 |
+
pydantic/v1/generics.py,sha256=YzyKTZN6x5Q1RGJ3WQ9jN-uwHJxL3W4qoZqwcZXqxWg,17829
|
| 201 |
+
pydantic/v1/json.py,sha256=WQ5Hy_hIpfdR3YS8k6N2E6KMJzsdbBi_ldWOPJaV81M,3390
|
| 202 |
+
pydantic/v1/main.py,sha256=vRB1TbpkzPN3P5ijJlc-cjNuO-HciNOpC4b8K3zZnfc,45697
|
| 203 |
+
pydantic/v1/mypy.py,sha256=Cl8XRfCmIcVE3j5AEU52C8iDh8lcX__D3hz2jIWxMAs,38860
|
| 204 |
+
pydantic/v1/networks.py,sha256=HYNtKAfOmOnKJpsDg1g6SIkj9WPhU_-i8l5e2JKBpG4,22124
|
| 205 |
+
pydantic/v1/parse.py,sha256=BJtdqiZRtav9VRFCmOxoY-KImQmjPy-A_NoojiFUZxY,1821
|
| 206 |
+
pydantic/v1/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 207 |
+
pydantic/v1/schema.py,sha256=aqBuA--cq8gAVkim5BJPFASHzOZ8dFtmFX_fNGr6ip4,47801
|
| 208 |
+
pydantic/v1/tools.py,sha256=1lDdXHk0jL5uP3u5RCYAvUAlGClgAO-45lkq9j7fyBA,2881
|
| 209 |
+
pydantic/v1/types.py,sha256=Bzl-RcnitPBHnqwwj9iv7JjHuN1GpnWH24dKkF3l9e8,35455
|
| 210 |
+
pydantic/v1/typing.py,sha256=ovwtLpEZCbnghZaHfSNJupzetzHNkLXjn_66kgTnIV4,20102
|
| 211 |
+
pydantic/v1/utils.py,sha256=1PqOIlz6OVWwGds3HBKlw4Et6asFou0UUpAto7jFOCs,26014
|
| 212 |
+
pydantic/v1/validators.py,sha256=lyUkn1MWhHxlCX5ZfEgFj_CAHojoiPcaQeMdEM9XviU,22187
|
| 213 |
+
pydantic/v1/version.py,sha256=YpHWOQKtGoxfyikzGrcmXJVKUVYB9EBdoCR994QxSnE,1039
|
| 214 |
+
pydantic/validate_call_decorator.py,sha256=VLAi4hoFpjC-1eL0HixYaaWaEFO6htGcDctgsqa5VII,4416
|
| 215 |
+
pydantic/validators.py,sha256=pwbIJXVb1CV2mAE4w_EGfNj7DwzsKaWw_tTL6cviTus,146
|
| 216 |
+
pydantic/version.py,sha256=T5rziwDPrMjfaU0X--5fBdGVvh94QznNQ7QjD4LfgmA,3985
|
| 217 |
+
pydantic/warnings.py,sha256=3QyQo6lN35cO7OXDbFEXWcNaPlGCRaregVZA-G-lZwI,4822
|
.mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: hatchling 1.29.0
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
.mcp-venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2017 to present Pydantic Services Inc. and individual contributors.
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from collections import defaultdict
|
| 4 |
+
from collections.abc import Iterable
|
| 5 |
+
from copy import copy
|
| 6 |
+
from functools import lru_cache, partial
|
| 7 |
+
from typing import TYPE_CHECKING, Any
|
| 8 |
+
|
| 9 |
+
from pydantic_core import CoreSchema, PydanticCustomError, ValidationError, to_jsonable_python
|
| 10 |
+
from pydantic_core import core_schema as cs
|
| 11 |
+
|
| 12 |
+
from ._fields import PydanticMetadata
|
| 13 |
+
from ._import_utils import import_cached_field_info
|
| 14 |
+
|
| 15 |
+
if TYPE_CHECKING:
|
| 16 |
+
pass
|
| 17 |
+
|
| 18 |
+
STRICT = {'strict'}
|
| 19 |
+
FAIL_FAST = {'fail_fast'}
|
| 20 |
+
LENGTH_CONSTRAINTS = {'min_length', 'max_length'}
|
| 21 |
+
INEQUALITY = {'le', 'ge', 'lt', 'gt'}
|
| 22 |
+
NUMERIC_CONSTRAINTS = {'multiple_of', *INEQUALITY}
|
| 23 |
+
ALLOW_INF_NAN = {'allow_inf_nan'}
|
| 24 |
+
|
| 25 |
+
STR_CONSTRAINTS = {
|
| 26 |
+
*LENGTH_CONSTRAINTS,
|
| 27 |
+
*STRICT,
|
| 28 |
+
'strip_whitespace',
|
| 29 |
+
'to_lower',
|
| 30 |
+
'to_upper',
|
| 31 |
+
'pattern',
|
| 32 |
+
'coerce_numbers_to_str',
|
| 33 |
+
'ascii_only',
|
| 34 |
+
}
|
| 35 |
+
BYTES_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT}
|
| 36 |
+
|
| 37 |
+
LIST_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT, *FAIL_FAST}
|
| 38 |
+
TUPLE_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT, *FAIL_FAST}
|
| 39 |
+
SET_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT, *FAIL_FAST}
|
| 40 |
+
DICT_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT}
|
| 41 |
+
GENERATOR_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT}
|
| 42 |
+
SEQUENCE_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *FAIL_FAST}
|
| 43 |
+
|
| 44 |
+
FLOAT_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *ALLOW_INF_NAN, *STRICT}
|
| 45 |
+
DECIMAL_CONSTRAINTS = {'max_digits', 'decimal_places', *FLOAT_CONSTRAINTS}
|
| 46 |
+
INT_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *ALLOW_INF_NAN, *STRICT}
|
| 47 |
+
BOOL_CONSTRAINTS = STRICT
|
| 48 |
+
UUID_CONSTRAINTS = STRICT
|
| 49 |
+
|
| 50 |
+
DATE_TIME_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT}
|
| 51 |
+
TIMEDELTA_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT}
|
| 52 |
+
TIME_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT}
|
| 53 |
+
LAX_OR_STRICT_CONSTRAINTS = STRICT
|
| 54 |
+
ENUM_CONSTRAINTS = STRICT
|
| 55 |
+
COMPLEX_CONSTRAINTS = STRICT
|
| 56 |
+
|
| 57 |
+
UNION_CONSTRAINTS = {'union_mode'}
|
| 58 |
+
URL_CONSTRAINTS = {
|
| 59 |
+
'max_length',
|
| 60 |
+
'allowed_schemes',
|
| 61 |
+
'host_required',
|
| 62 |
+
'default_host',
|
| 63 |
+
'default_port',
|
| 64 |
+
'default_path',
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
TEXT_SCHEMA_TYPES = ('str', 'bytes', 'url', 'multi-host-url')
|
| 68 |
+
SEQUENCE_SCHEMA_TYPES = ('list', 'tuple', 'set', 'frozenset', 'generator', *TEXT_SCHEMA_TYPES)
|
| 69 |
+
NUMERIC_SCHEMA_TYPES = ('float', 'int', 'date', 'time', 'timedelta', 'datetime')
|
| 70 |
+
|
| 71 |
+
CONSTRAINTS_TO_ALLOWED_SCHEMAS: dict[str, set[str]] = defaultdict(set)
|
| 72 |
+
|
| 73 |
+
constraint_schema_pairings: list[tuple[set[str], tuple[str, ...]]] = [
|
| 74 |
+
(STR_CONSTRAINTS, TEXT_SCHEMA_TYPES),
|
| 75 |
+
(BYTES_CONSTRAINTS, ('bytes',)),
|
| 76 |
+
(LIST_CONSTRAINTS, ('list',)),
|
| 77 |
+
(TUPLE_CONSTRAINTS, ('tuple',)),
|
| 78 |
+
(SET_CONSTRAINTS, ('set', 'frozenset')),
|
| 79 |
+
(DICT_CONSTRAINTS, ('dict',)),
|
| 80 |
+
(GENERATOR_CONSTRAINTS, ('generator',)),
|
| 81 |
+
(FLOAT_CONSTRAINTS, ('float',)),
|
| 82 |
+
(INT_CONSTRAINTS, ('int',)),
|
| 83 |
+
(DATE_TIME_CONSTRAINTS, ('date', 'time', 'datetime', 'timedelta')),
|
| 84 |
+
# TODO: this is a bit redundant, we could probably avoid some of these
|
| 85 |
+
(STRICT, (*TEXT_SCHEMA_TYPES, *SEQUENCE_SCHEMA_TYPES, *NUMERIC_SCHEMA_TYPES, 'typed-dict', 'model')),
|
| 86 |
+
(UNION_CONSTRAINTS, ('union',)),
|
| 87 |
+
(URL_CONSTRAINTS, ('url', 'multi-host-url')),
|
| 88 |
+
(BOOL_CONSTRAINTS, ('bool',)),
|
| 89 |
+
(UUID_CONSTRAINTS, ('uuid',)),
|
| 90 |
+
(LAX_OR_STRICT_CONSTRAINTS, ('lax-or-strict',)),
|
| 91 |
+
(ENUM_CONSTRAINTS, ('enum',)),
|
| 92 |
+
(DECIMAL_CONSTRAINTS, ('decimal',)),
|
| 93 |
+
(COMPLEX_CONSTRAINTS, ('complex',)),
|
| 94 |
+
]
|
| 95 |
+
|
| 96 |
+
for constraints, schemas in constraint_schema_pairings:
|
| 97 |
+
for c in constraints:
|
| 98 |
+
CONSTRAINTS_TO_ALLOWED_SCHEMAS[c].update(schemas)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def as_jsonable_value(v: Any) -> Any:
|
| 102 |
+
if type(v) not in (int, str, float, bytes, bool, type(None)):
|
| 103 |
+
return to_jsonable_python(v)
|
| 104 |
+
return v
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def expand_grouped_metadata(annotations: Iterable[Any]) -> Iterable[Any]:
|
| 108 |
+
"""Expand the annotations.
|
| 109 |
+
|
| 110 |
+
Args:
|
| 111 |
+
annotations: An iterable of annotations.
|
| 112 |
+
|
| 113 |
+
Returns:
|
| 114 |
+
An iterable of expanded annotations.
|
| 115 |
+
|
| 116 |
+
Example:
|
| 117 |
+
```python
|
| 118 |
+
from annotated_types import Ge, Len
|
| 119 |
+
|
| 120 |
+
from pydantic._internal._known_annotated_metadata import expand_grouped_metadata
|
| 121 |
+
|
| 122 |
+
print(list(expand_grouped_metadata([Ge(4), Len(5)])))
|
| 123 |
+
#> [Ge(ge=4), MinLen(min_length=5)]
|
| 124 |
+
```
|
| 125 |
+
"""
|
| 126 |
+
import annotated_types as at
|
| 127 |
+
|
| 128 |
+
FieldInfo = import_cached_field_info()
|
| 129 |
+
|
| 130 |
+
for annotation in annotations:
|
| 131 |
+
if isinstance(annotation, at.GroupedMetadata):
|
| 132 |
+
yield from annotation
|
| 133 |
+
elif isinstance(annotation, FieldInfo):
|
| 134 |
+
yield from annotation.metadata
|
| 135 |
+
# this is a bit problematic in that it results in duplicate metadata
|
| 136 |
+
# all of our "consumers" can handle it, but it is not ideal
|
| 137 |
+
# we probably should split up FieldInfo into:
|
| 138 |
+
# - annotated types metadata
|
| 139 |
+
# - individual metadata known only to Pydantic
|
| 140 |
+
annotation = copy(annotation)
|
| 141 |
+
annotation.metadata = []
|
| 142 |
+
yield annotation
|
| 143 |
+
else:
|
| 144 |
+
yield annotation
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
@lru_cache
|
| 148 |
+
def _get_at_to_constraint_map() -> dict[type, str]:
|
| 149 |
+
"""Return a mapping of annotated types to constraints.
|
| 150 |
+
|
| 151 |
+
Normally, we would define a mapping like this in the module scope, but we can't do that
|
| 152 |
+
because we don't permit module level imports of `annotated_types`, in an attempt to speed up
|
| 153 |
+
the import time of `pydantic`. We still only want to have this dictionary defined in one place,
|
| 154 |
+
so we use this function to cache the result.
|
| 155 |
+
"""
|
| 156 |
+
import annotated_types as at
|
| 157 |
+
|
| 158 |
+
return {
|
| 159 |
+
at.Gt: 'gt',
|
| 160 |
+
at.Ge: 'ge',
|
| 161 |
+
at.Lt: 'lt',
|
| 162 |
+
at.Le: 'le',
|
| 163 |
+
at.MultipleOf: 'multiple_of',
|
| 164 |
+
at.MinLen: 'min_length',
|
| 165 |
+
at.MaxLen: 'max_length',
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def apply_known_metadata(annotation: Any, schema: CoreSchema) -> CoreSchema | None: # noqa: C901
|
| 170 |
+
"""Apply `annotation` to `schema` if it is an annotation we know about (Gt, Le, etc.).
|
| 171 |
+
Otherwise return `None`.
|
| 172 |
+
|
| 173 |
+
This does not handle all known annotations. If / when it does, it can always
|
| 174 |
+
return a CoreSchema and return the unmodified schema if the annotation should be ignored.
|
| 175 |
+
|
| 176 |
+
Assumes that GroupedMetadata has already been expanded via `expand_grouped_metadata`.
|
| 177 |
+
|
| 178 |
+
Args:
|
| 179 |
+
annotation: The annotation.
|
| 180 |
+
schema: The schema.
|
| 181 |
+
|
| 182 |
+
Returns:
|
| 183 |
+
An updated schema with annotation if it is an annotation we know about, `None` otherwise.
|
| 184 |
+
|
| 185 |
+
Raises:
|
| 186 |
+
RuntimeError: If a constraint can't be applied to a specific schema type.
|
| 187 |
+
ValueError: If an unknown constraint is encountered.
|
| 188 |
+
"""
|
| 189 |
+
import annotated_types as at
|
| 190 |
+
|
| 191 |
+
from ._validators import NUMERIC_VALIDATOR_LOOKUP, forbid_inf_nan_check
|
| 192 |
+
|
| 193 |
+
schema = schema.copy()
|
| 194 |
+
schema_update, other_metadata = collect_known_metadata([annotation])
|
| 195 |
+
schema_type = schema['type']
|
| 196 |
+
|
| 197 |
+
chain_schema_constraints: set[str] = {
|
| 198 |
+
'pattern',
|
| 199 |
+
'strip_whitespace',
|
| 200 |
+
'to_lower',
|
| 201 |
+
'to_upper',
|
| 202 |
+
'coerce_numbers_to_str',
|
| 203 |
+
'ascii_only',
|
| 204 |
+
}
|
| 205 |
+
chain_schema_steps: list[CoreSchema] = []
|
| 206 |
+
|
| 207 |
+
for constraint, value in schema_update.items():
|
| 208 |
+
if constraint not in CONSTRAINTS_TO_ALLOWED_SCHEMAS:
|
| 209 |
+
raise ValueError(f'Unknown constraint {constraint}')
|
| 210 |
+
allowed_schemas = CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint]
|
| 211 |
+
|
| 212 |
+
# if it becomes necessary to handle more than one constraint
|
| 213 |
+
# in this recursive case with function-after or function-wrap, we should refactor
|
| 214 |
+
# this is a bit challenging because we sometimes want to apply constraints to the inner schema,
|
| 215 |
+
# whereas other times we want to wrap the existing schema with a new one that enforces a new constraint.
|
| 216 |
+
if schema_type in {'function-before', 'function-wrap', 'function-after'} and constraint == 'strict':
|
| 217 |
+
schema['schema'] = apply_known_metadata(annotation, schema['schema']) # type: ignore # schema is function schema
|
| 218 |
+
return schema
|
| 219 |
+
|
| 220 |
+
# if we're allowed to apply constraint directly to the schema, like le to int, do that
|
| 221 |
+
if schema_type in allowed_schemas:
|
| 222 |
+
if constraint == 'union_mode' and schema_type == 'union':
|
| 223 |
+
schema['mode'] = value # type: ignore # schema is UnionSchema
|
| 224 |
+
else:
|
| 225 |
+
schema[constraint] = value
|
| 226 |
+
continue
|
| 227 |
+
|
| 228 |
+
# else, apply a function after validator to the schema to enforce the corresponding constraint
|
| 229 |
+
if constraint in chain_schema_constraints:
|
| 230 |
+
|
| 231 |
+
def _apply_constraint_with_incompatibility_info(
|
| 232 |
+
value: Any, handler: cs.ValidatorFunctionWrapHandler
|
| 233 |
+
) -> Any:
|
| 234 |
+
try:
|
| 235 |
+
x = handler(value)
|
| 236 |
+
except ValidationError as ve:
|
| 237 |
+
# if the error is about the type, it's likely that the constraint is incompatible the type of the field
|
| 238 |
+
# for example, the following invalid schema wouldn't be caught during schema build, but rather at this point
|
| 239 |
+
# with a cryptic 'string_type' error coming from the string validator,
|
| 240 |
+
# that we'd rather express as a constraint incompatibility error (TypeError)
|
| 241 |
+
# Annotated[list[int], Field(pattern='abc')]
|
| 242 |
+
if 'type' in ve.errors()[0]['type']:
|
| 243 |
+
raise TypeError(
|
| 244 |
+
f"Unable to apply constraint '{constraint}' to supplied value {value} for schema of type '{schema_type}'" # noqa: B023
|
| 245 |
+
)
|
| 246 |
+
raise ve
|
| 247 |
+
return x
|
| 248 |
+
|
| 249 |
+
chain_schema_steps.append(
|
| 250 |
+
cs.no_info_wrap_validator_function(
|
| 251 |
+
_apply_constraint_with_incompatibility_info, cs.str_schema(**{constraint: value})
|
| 252 |
+
)
|
| 253 |
+
)
|
| 254 |
+
elif constraint in NUMERIC_VALIDATOR_LOOKUP:
|
| 255 |
+
if constraint in LENGTH_CONSTRAINTS:
|
| 256 |
+
inner_schema = schema
|
| 257 |
+
while inner_schema['type'] in {'function-before', 'function-wrap', 'function-after'}:
|
| 258 |
+
inner_schema = inner_schema['schema'] # type: ignore
|
| 259 |
+
inner_schema_type = inner_schema['type']
|
| 260 |
+
if inner_schema_type == 'list' or (
|
| 261 |
+
inner_schema_type == 'json-or-python' and inner_schema['json_schema']['type'] == 'list' # type: ignore
|
| 262 |
+
):
|
| 263 |
+
js_constraint_key = 'minItems' if constraint == 'min_length' else 'maxItems'
|
| 264 |
+
else:
|
| 265 |
+
js_constraint_key = 'minLength' if constraint == 'min_length' else 'maxLength'
|
| 266 |
+
else:
|
| 267 |
+
js_constraint_key = constraint
|
| 268 |
+
|
| 269 |
+
schema = cs.no_info_after_validator_function(
|
| 270 |
+
partial(NUMERIC_VALIDATOR_LOOKUP[constraint], **{constraint: value}), schema
|
| 271 |
+
)
|
| 272 |
+
metadata = schema.get('metadata', {})
|
| 273 |
+
if (existing_json_schema_updates := metadata.get('pydantic_js_updates')) is not None:
|
| 274 |
+
metadata['pydantic_js_updates'] = {
|
| 275 |
+
**existing_json_schema_updates,
|
| 276 |
+
**{js_constraint_key: as_jsonable_value(value)},
|
| 277 |
+
}
|
| 278 |
+
else:
|
| 279 |
+
metadata['pydantic_js_updates'] = {js_constraint_key: as_jsonable_value(value)}
|
| 280 |
+
schema['metadata'] = metadata
|
| 281 |
+
elif constraint == 'allow_inf_nan' and value is False:
|
| 282 |
+
schema = cs.no_info_after_validator_function(
|
| 283 |
+
forbid_inf_nan_check,
|
| 284 |
+
schema,
|
| 285 |
+
)
|
| 286 |
+
else:
|
| 287 |
+
# It's rare that we'd get here, but it's possible if we add a new constraint and forget to handle it
|
| 288 |
+
# Most constraint errors are caught at runtime during attempted application
|
| 289 |
+
raise RuntimeError(f"Unable to apply constraint '{constraint}' to schema of type '{schema_type}'")
|
| 290 |
+
|
| 291 |
+
for annotation in other_metadata:
|
| 292 |
+
if (annotation_type := type(annotation)) in (at_to_constraint_map := _get_at_to_constraint_map()):
|
| 293 |
+
constraint = at_to_constraint_map[annotation_type]
|
| 294 |
+
validator = NUMERIC_VALIDATOR_LOOKUP.get(constraint)
|
| 295 |
+
if validator is None:
|
| 296 |
+
raise ValueError(f'Unknown constraint {constraint}')
|
| 297 |
+
schema = cs.no_info_after_validator_function(
|
| 298 |
+
partial(validator, {constraint: getattr(annotation, constraint)}), schema
|
| 299 |
+
)
|
| 300 |
+
continue
|
| 301 |
+
elif isinstance(annotation, (at.Predicate, at.Not)):
|
| 302 |
+
predicate_name = f'{annotation.func.__qualname__!r} ' if hasattr(annotation.func, '__qualname__') else ''
|
| 303 |
+
|
| 304 |
+
# Note: B023 is ignored because even though we iterate over `other_metadata`, it is guaranteed
|
| 305 |
+
# to be of length 1. `apply_known_metadata()` is called from `GenerateSchema`, where annotations
|
| 306 |
+
# were already expanded via `expand_grouped_metadata()`. Confusing, but this falls into the annotations
|
| 307 |
+
# refactor.
|
| 308 |
+
if isinstance(annotation, at.Predicate):
|
| 309 |
+
|
| 310 |
+
def val_func(v: Any) -> Any:
|
| 311 |
+
predicate_satisfied = annotation.func(v) # noqa: B023
|
| 312 |
+
if not predicate_satisfied:
|
| 313 |
+
raise PydanticCustomError(
|
| 314 |
+
'predicate_failed',
|
| 315 |
+
f'Predicate {predicate_name}failed', # pyright: ignore[reportArgumentType] # noqa: B023
|
| 316 |
+
)
|
| 317 |
+
return v
|
| 318 |
+
|
| 319 |
+
else:
|
| 320 |
+
|
| 321 |
+
def val_func(v: Any) -> Any:
|
| 322 |
+
predicate_satisfied = annotation.func(v) # noqa: B023
|
| 323 |
+
if predicate_satisfied:
|
| 324 |
+
raise PydanticCustomError(
|
| 325 |
+
'not_operation_failed',
|
| 326 |
+
f'Not of {predicate_name}failed', # pyright: ignore[reportArgumentType] # noqa: B023
|
| 327 |
+
)
|
| 328 |
+
return v
|
| 329 |
+
|
| 330 |
+
schema = cs.no_info_after_validator_function(val_func, schema)
|
| 331 |
+
else:
|
| 332 |
+
# ignore any other unknown metadata
|
| 333 |
+
return None
|
| 334 |
+
|
| 335 |
+
if chain_schema_steps:
|
| 336 |
+
chain_schema_steps = [schema] + chain_schema_steps
|
| 337 |
+
return cs.chain_schema(chain_schema_steps)
|
| 338 |
+
|
| 339 |
+
return schema
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def collect_known_metadata(annotations: Iterable[Any]) -> tuple[dict[str, Any], list[Any]]:
|
| 343 |
+
"""Split `annotations` into known metadata and unknown annotations.
|
| 344 |
+
|
| 345 |
+
Args:
|
| 346 |
+
annotations: An iterable of annotations.
|
| 347 |
+
|
| 348 |
+
Returns:
|
| 349 |
+
A tuple contains a dict of known metadata and a list of unknown annotations.
|
| 350 |
+
|
| 351 |
+
Example:
|
| 352 |
+
```python
|
| 353 |
+
from annotated_types import Gt, Len
|
| 354 |
+
|
| 355 |
+
from pydantic._internal._known_annotated_metadata import collect_known_metadata
|
| 356 |
+
|
| 357 |
+
print(collect_known_metadata([Gt(1), Len(42), ...]))
|
| 358 |
+
#> ({'gt': 1, 'min_length': 42}, [Ellipsis])
|
| 359 |
+
```
|
| 360 |
+
"""
|
| 361 |
+
annotations = expand_grouped_metadata(annotations)
|
| 362 |
+
|
| 363 |
+
res: dict[str, Any] = {}
|
| 364 |
+
remaining: list[Any] = []
|
| 365 |
+
|
| 366 |
+
for annotation in annotations:
|
| 367 |
+
# isinstance(annotation, PydanticMetadata) also covers ._fields:_PydanticGeneralMetadata
|
| 368 |
+
if isinstance(annotation, PydanticMetadata):
|
| 369 |
+
res.update(annotation.__dict__)
|
| 370 |
+
# we don't use dataclasses.asdict because that recursively calls asdict on the field values
|
| 371 |
+
elif (annotation_type := type(annotation)) in (at_to_constraint_map := _get_at_to_constraint_map()):
|
| 372 |
+
constraint = at_to_constraint_map[annotation_type]
|
| 373 |
+
res[constraint] = getattr(annotation, constraint)
|
| 374 |
+
elif isinstance(annotation, type) and issubclass(annotation, PydanticMetadata):
|
| 375 |
+
# also support PydanticMetadata classes being used without initialisation,
|
| 376 |
+
# e.g. `Annotated[int, Strict]` as well as `Annotated[int, Strict()]`
|
| 377 |
+
res.update({k: v for k, v in vars(annotation).items() if not k.startswith('_')})
|
| 378 |
+
else:
|
| 379 |
+
remaining.append(annotation)
|
| 380 |
+
# Nones can sneak in but pydantic-core will reject them
|
| 381 |
+
# it'd be nice to clean things up so we don't put in None (we probably don't _need_ to, it was just easier)
|
| 382 |
+
# but this is simple enough to kick that can down the road
|
| 383 |
+
res = {k: v for k, v in res.items() if v is not None}
|
| 384 |
+
return res, remaining
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
def check_metadata(metadata: dict[str, Any], allowed: Iterable[str], source_type: Any) -> None:
|
| 388 |
+
"""A small utility function to validate that the given metadata can be applied to the target.
|
| 389 |
+
More than saving lines of code, this gives us a consistent error message for all of our internal implementations.
|
| 390 |
+
|
| 391 |
+
Args:
|
| 392 |
+
metadata: A dict of metadata.
|
| 393 |
+
allowed: An iterable of allowed metadata.
|
| 394 |
+
source_type: The source type.
|
| 395 |
+
|
| 396 |
+
Raises:
|
| 397 |
+
TypeError: If there is metadatas that can't be applied on source type.
|
| 398 |
+
"""
|
| 399 |
+
unknown = metadata.keys() - set(allowed)
|
| 400 |
+
if unknown:
|
| 401 |
+
raise TypeError(
|
| 402 |
+
f'The following constraints cannot be applied to {source_type!r}: {", ".join([f"{k!r}" for k in unknown])}'
|
| 403 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from collections.abc import Iterator, Mapping
|
| 4 |
+
from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, TypeVar, Union
|
| 5 |
+
|
| 6 |
+
from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator
|
| 7 |
+
|
| 8 |
+
from ..errors import PydanticErrorCodes, PydanticUserError
|
| 9 |
+
from ..plugin._schema_validator import PluggableSchemaValidator
|
| 10 |
+
|
| 11 |
+
if TYPE_CHECKING:
|
| 12 |
+
from ..dataclasses import PydanticDataclass
|
| 13 |
+
from ..main import BaseModel
|
| 14 |
+
from ..type_adapter import TypeAdapter
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
ValSer = TypeVar('ValSer', bound=Union[SchemaValidator, PluggableSchemaValidator, SchemaSerializer])
|
| 18 |
+
T = TypeVar('T')
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class MockCoreSchema(Mapping[str, Any]):
|
| 22 |
+
"""Mocker for `pydantic_core.CoreSchema` which optionally attempts to
|
| 23 |
+
rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails.
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
__slots__ = '_error_message', '_code', '_attempt_rebuild', '_built_memo'
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
error_message: str,
|
| 31 |
+
*,
|
| 32 |
+
code: PydanticErrorCodes,
|
| 33 |
+
attempt_rebuild: Callable[[], CoreSchema | None] | None = None,
|
| 34 |
+
) -> None:
|
| 35 |
+
self._error_message = error_message
|
| 36 |
+
self._code: PydanticErrorCodes = code
|
| 37 |
+
self._attempt_rebuild = attempt_rebuild
|
| 38 |
+
self._built_memo: CoreSchema | None = None
|
| 39 |
+
|
| 40 |
+
def __getitem__(self, key: str) -> Any:
|
| 41 |
+
return self._get_built().__getitem__(key)
|
| 42 |
+
|
| 43 |
+
def __len__(self) -> int:
|
| 44 |
+
return self._get_built().__len__()
|
| 45 |
+
|
| 46 |
+
def __iter__(self) -> Iterator[str]:
|
| 47 |
+
return self._get_built().__iter__()
|
| 48 |
+
|
| 49 |
+
def _get_built(self) -> CoreSchema:
|
| 50 |
+
if self._built_memo is not None:
|
| 51 |
+
return self._built_memo
|
| 52 |
+
|
| 53 |
+
if self._attempt_rebuild:
|
| 54 |
+
schema = self._attempt_rebuild()
|
| 55 |
+
if schema is not None:
|
| 56 |
+
self._built_memo = schema
|
| 57 |
+
return schema
|
| 58 |
+
raise PydanticUserError(self._error_message, code=self._code)
|
| 59 |
+
|
| 60 |
+
def rebuild(self) -> CoreSchema | None:
|
| 61 |
+
self._built_memo = None
|
| 62 |
+
if self._attempt_rebuild:
|
| 63 |
+
schema = self._attempt_rebuild()
|
| 64 |
+
if schema is not None:
|
| 65 |
+
return schema
|
| 66 |
+
else:
|
| 67 |
+
raise PydanticUserError(self._error_message, code=self._code)
|
| 68 |
+
return None
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class MockValSer(Generic[ValSer]):
|
| 72 |
+
"""Mocker for `pydantic_core.SchemaValidator` or `pydantic_core.SchemaSerializer` which optionally attempts to
|
| 73 |
+
rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails.
|
| 74 |
+
"""
|
| 75 |
+
|
| 76 |
+
__slots__ = '_error_message', '_code', '_val_or_ser', '_attempt_rebuild'
|
| 77 |
+
|
| 78 |
+
def __init__(
|
| 79 |
+
self,
|
| 80 |
+
error_message: str,
|
| 81 |
+
*,
|
| 82 |
+
code: PydanticErrorCodes,
|
| 83 |
+
val_or_ser: Literal['validator', 'serializer'],
|
| 84 |
+
attempt_rebuild: Callable[[], ValSer | None] | None = None,
|
| 85 |
+
) -> None:
|
| 86 |
+
self._error_message = error_message
|
| 87 |
+
self._val_or_ser = SchemaValidator if val_or_ser == 'validator' else SchemaSerializer
|
| 88 |
+
self._code: PydanticErrorCodes = code
|
| 89 |
+
self._attempt_rebuild = attempt_rebuild
|
| 90 |
+
|
| 91 |
+
def __getattr__(self, item: str) -> None:
|
| 92 |
+
__tracebackhide__ = True
|
| 93 |
+
if self._attempt_rebuild:
|
| 94 |
+
val_ser = self._attempt_rebuild()
|
| 95 |
+
if val_ser is not None:
|
| 96 |
+
return getattr(val_ser, item)
|
| 97 |
+
|
| 98 |
+
# raise an AttributeError if `item` doesn't exist
|
| 99 |
+
getattr(self._val_or_ser, item)
|
| 100 |
+
raise PydanticUserError(self._error_message, code=self._code)
|
| 101 |
+
|
| 102 |
+
def rebuild(self) -> ValSer | None:
|
| 103 |
+
if self._attempt_rebuild:
|
| 104 |
+
val_ser = self._attempt_rebuild()
|
| 105 |
+
if val_ser is not None:
|
| 106 |
+
return val_ser
|
| 107 |
+
else:
|
| 108 |
+
raise PydanticUserError(self._error_message, code=self._code)
|
| 109 |
+
return None
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def set_type_adapter_mocks(adapter: TypeAdapter) -> None:
|
| 113 |
+
"""Set `core_schema`, `validator` and `serializer` to mock core types on a type adapter instance.
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
adapter: The type adapter instance to set the mocks on
|
| 117 |
+
"""
|
| 118 |
+
type_repr = str(adapter._type)
|
| 119 |
+
undefined_type_error_message = (
|
| 120 |
+
f'`TypeAdapter[{type_repr}]` is not fully defined; you should define `{type_repr}` and all referenced types,'
|
| 121 |
+
f' then call `.rebuild()` on the instance.'
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
def attempt_rebuild_fn(attr_fn: Callable[[TypeAdapter], T]) -> Callable[[], T | None]:
|
| 125 |
+
def handler() -> T | None:
|
| 126 |
+
if adapter.rebuild(raise_errors=False, _parent_namespace_depth=5) is not False:
|
| 127 |
+
return attr_fn(adapter)
|
| 128 |
+
return None
|
| 129 |
+
|
| 130 |
+
return handler
|
| 131 |
+
|
| 132 |
+
adapter.core_schema = MockCoreSchema( # pyright: ignore[reportAttributeAccessIssue]
|
| 133 |
+
undefined_type_error_message,
|
| 134 |
+
code='class-not-fully-defined',
|
| 135 |
+
attempt_rebuild=attempt_rebuild_fn(lambda ta: ta.core_schema),
|
| 136 |
+
)
|
| 137 |
+
adapter.validator = MockValSer( # pyright: ignore[reportAttributeAccessIssue]
|
| 138 |
+
undefined_type_error_message,
|
| 139 |
+
code='class-not-fully-defined',
|
| 140 |
+
val_or_ser='validator',
|
| 141 |
+
attempt_rebuild=attempt_rebuild_fn(lambda ta: ta.validator),
|
| 142 |
+
)
|
| 143 |
+
adapter.serializer = MockValSer( # pyright: ignore[reportAttributeAccessIssue]
|
| 144 |
+
undefined_type_error_message,
|
| 145 |
+
code='class-not-fully-defined',
|
| 146 |
+
val_or_ser='serializer',
|
| 147 |
+
attempt_rebuild=attempt_rebuild_fn(lambda ta: ta.serializer),
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def set_model_mocks(cls: type[BaseModel], undefined_name: str = 'all referenced types') -> None:
|
| 152 |
+
"""Set `__pydantic_core_schema__`, `__pydantic_validator__` and `__pydantic_serializer__` to mock core types on a model.
|
| 153 |
+
|
| 154 |
+
Args:
|
| 155 |
+
cls: The model class to set the mocks on
|
| 156 |
+
undefined_name: Name of the undefined thing, used in error messages
|
| 157 |
+
"""
|
| 158 |
+
undefined_type_error_message = (
|
| 159 |
+
f'`{cls.__name__}` is not fully defined; you should define {undefined_name},'
|
| 160 |
+
f' then call `{cls.__name__}.model_rebuild()`.'
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
def attempt_rebuild_fn(attr_fn: Callable[[type[BaseModel]], T]) -> Callable[[], T | None]:
|
| 164 |
+
def handler() -> T | None:
|
| 165 |
+
if cls.model_rebuild(raise_errors=False, _parent_namespace_depth=5) is not False:
|
| 166 |
+
return attr_fn(cls)
|
| 167 |
+
return None
|
| 168 |
+
|
| 169 |
+
return handler
|
| 170 |
+
|
| 171 |
+
cls.__pydantic_core_schema__ = MockCoreSchema( # pyright: ignore[reportAttributeAccessIssue]
|
| 172 |
+
undefined_type_error_message,
|
| 173 |
+
code='class-not-fully-defined',
|
| 174 |
+
attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_core_schema__),
|
| 175 |
+
)
|
| 176 |
+
cls.__pydantic_validator__ = MockValSer( # pyright: ignore[reportAttributeAccessIssue]
|
| 177 |
+
undefined_type_error_message,
|
| 178 |
+
code='class-not-fully-defined',
|
| 179 |
+
val_or_ser='validator',
|
| 180 |
+
attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_validator__),
|
| 181 |
+
)
|
| 182 |
+
cls.__pydantic_serializer__ = MockValSer( # pyright: ignore[reportAttributeAccessIssue]
|
| 183 |
+
undefined_type_error_message,
|
| 184 |
+
code='class-not-fully-defined',
|
| 185 |
+
val_or_ser='serializer',
|
| 186 |
+
attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_serializer__),
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def set_dataclass_mocks(cls: type[PydanticDataclass], undefined_name: str = 'all referenced types') -> None:
|
| 191 |
+
"""Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a dataclass.
|
| 192 |
+
|
| 193 |
+
Args:
|
| 194 |
+
cls: The model class to set the mocks on
|
| 195 |
+
undefined_name: Name of the undefined thing, used in error messages
|
| 196 |
+
"""
|
| 197 |
+
from ..dataclasses import rebuild_dataclass
|
| 198 |
+
|
| 199 |
+
undefined_type_error_message = (
|
| 200 |
+
f'`{cls.__name__}` is not fully defined; you should define {undefined_name},'
|
| 201 |
+
f' then call `pydantic.dataclasses.rebuild_dataclass({cls.__name__})`.'
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
def attempt_rebuild_fn(attr_fn: Callable[[type[PydanticDataclass]], T]) -> Callable[[], T | None]:
|
| 205 |
+
def handler() -> T | None:
|
| 206 |
+
if rebuild_dataclass(cls, raise_errors=False, _parent_namespace_depth=5) is not False:
|
| 207 |
+
return attr_fn(cls)
|
| 208 |
+
return None
|
| 209 |
+
|
| 210 |
+
return handler
|
| 211 |
+
|
| 212 |
+
cls.__pydantic_core_schema__ = MockCoreSchema( # pyright: ignore[reportAttributeAccessIssue]
|
| 213 |
+
undefined_type_error_message,
|
| 214 |
+
code='class-not-fully-defined',
|
| 215 |
+
attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_core_schema__),
|
| 216 |
+
)
|
| 217 |
+
cls.__pydantic_validator__ = MockValSer( # pyright: ignore[reportAttributeAccessIssue]
|
| 218 |
+
undefined_type_error_message,
|
| 219 |
+
code='class-not-fully-defined',
|
| 220 |
+
val_or_ser='validator',
|
| 221 |
+
attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_validator__),
|
| 222 |
+
)
|
| 223 |
+
cls.__pydantic_serializer__ = MockValSer( # pyright: ignore[reportAttributeAccessIssue]
|
| 224 |
+
undefined_type_error_message,
|
| 225 |
+
code='class-not-fully-defined',
|
| 226 |
+
val_or_ser='serializer',
|
| 227 |
+
attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_serializer__),
|
| 228 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py
ADDED
|
@@ -0,0 +1,868 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Private logic for creating models."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import operator
|
| 6 |
+
import sys
|
| 7 |
+
import typing
|
| 8 |
+
import warnings
|
| 9 |
+
import weakref
|
| 10 |
+
from abc import ABCMeta
|
| 11 |
+
from functools import cache, partial, wraps
|
| 12 |
+
from types import FunctionType
|
| 13 |
+
from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, cast
|
| 14 |
+
|
| 15 |
+
from pydantic_core import PydanticUndefined, SchemaSerializer
|
| 16 |
+
from typing_extensions import TypeAliasType, dataclass_transform, deprecated, get_args, get_origin
|
| 17 |
+
from typing_inspection import typing_objects
|
| 18 |
+
|
| 19 |
+
from ..errors import PydanticUndefinedAnnotation, PydanticUserError
|
| 20 |
+
from ..plugin._schema_validator import create_schema_validator
|
| 21 |
+
from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20
|
| 22 |
+
from ._config import ConfigWrapper
|
| 23 |
+
from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function
|
| 24 |
+
from ._fields import collect_model_fields, is_valid_field_name, is_valid_privateattr_name, rebuild_model_fields
|
| 25 |
+
from ._generate_schema import GenerateSchema, InvalidSchemaError
|
| 26 |
+
from ._generics import PydanticGenericMetadata, get_model_typevars_map
|
| 27 |
+
from ._import_utils import import_cached_base_model, import_cached_field_info
|
| 28 |
+
from ._mock_val_ser import set_model_mocks
|
| 29 |
+
from ._namespace_utils import NsResolver
|
| 30 |
+
from ._signature import generate_pydantic_signature
|
| 31 |
+
from ._typing_extra import (
|
| 32 |
+
_make_forward_ref,
|
| 33 |
+
eval_type_backport,
|
| 34 |
+
is_classvar_annotation,
|
| 35 |
+
parent_frame_namespace,
|
| 36 |
+
)
|
| 37 |
+
from ._utils import LazyClassAttribute, SafeGetItemProxy
|
| 38 |
+
|
| 39 |
+
if TYPE_CHECKING:
|
| 40 |
+
from ..fields import Field as PydanticModelField
|
| 41 |
+
from ..fields import FieldInfo, ModelPrivateAttr
|
| 42 |
+
from ..fields import PrivateAttr as PydanticModelPrivateAttr
|
| 43 |
+
from ..main import BaseModel
|
| 44 |
+
from ._fields import PydanticExtraInfo
|
| 45 |
+
else:
|
| 46 |
+
PydanticModelField = object()
|
| 47 |
+
PydanticModelPrivateAttr = object()
|
| 48 |
+
|
| 49 |
+
object_setattr = object.__setattr__
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class _ModelNamespaceDict(dict):
|
| 53 |
+
"""A dictionary subclass that intercepts attribute setting on model classes and
|
| 54 |
+
warns about overriding of decorators.
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
def __setitem__(self, k: str, v: object) -> None:
|
| 58 |
+
existing: Any = self.get(k, None)
|
| 59 |
+
if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy):
|
| 60 |
+
warnings.warn(
|
| 61 |
+
f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator',
|
| 62 |
+
stacklevel=2,
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
return super().__setitem__(k, v)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def NoInitField(
|
| 69 |
+
*,
|
| 70 |
+
init: Literal[False] = False,
|
| 71 |
+
) -> Any:
|
| 72 |
+
"""Only for typing purposes. Used as default value of `__pydantic_fields_set__`,
|
| 73 |
+
`__pydantic_extra__`, `__pydantic_private__`, so they could be ignored when
|
| 74 |
+
synthesizing the `__init__` signature.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
# For ModelMetaclass.register():
|
| 79 |
+
_T = TypeVar('_T')
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr, NoInitField))
|
| 83 |
+
class ModelMetaclass(ABCMeta):
|
| 84 |
+
def __new__(
|
| 85 |
+
mcs,
|
| 86 |
+
cls_name: str,
|
| 87 |
+
bases: tuple[type[Any], ...],
|
| 88 |
+
namespace: dict[str, Any],
|
| 89 |
+
__pydantic_generic_metadata__: PydanticGenericMetadata | None = None,
|
| 90 |
+
__pydantic_reset_parent_namespace__: bool = True,
|
| 91 |
+
_create_model_module: str | None = None,
|
| 92 |
+
**kwargs: Any,
|
| 93 |
+
) -> type:
|
| 94 |
+
"""Metaclass for creating Pydantic models.
|
| 95 |
+
|
| 96 |
+
Args:
|
| 97 |
+
cls_name: The name of the class to be created.
|
| 98 |
+
bases: The base classes of the class to be created.
|
| 99 |
+
namespace: The attribute dictionary of the class to be created.
|
| 100 |
+
__pydantic_generic_metadata__: Metadata for generic models.
|
| 101 |
+
__pydantic_reset_parent_namespace__: Reset parent namespace.
|
| 102 |
+
_create_model_module: The module of the class to be created, if created by `create_model`.
|
| 103 |
+
**kwargs: Catch-all for any other keyword arguments.
|
| 104 |
+
|
| 105 |
+
Returns:
|
| 106 |
+
The new class created by the metaclass.
|
| 107 |
+
"""
|
| 108 |
+
# Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact
|
| 109 |
+
# that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__`
|
| 110 |
+
# call we're in the middle of is for the `BaseModel` class.
|
| 111 |
+
if bases:
|
| 112 |
+
raw_annotations: dict[str, Any]
|
| 113 |
+
if sys.version_info >= (3, 14):
|
| 114 |
+
if (
|
| 115 |
+
'__annotations__' in namespace
|
| 116 |
+
): # `from __future__ import annotations` was used in the model's module
|
| 117 |
+
raw_annotations = namespace['__annotations__']
|
| 118 |
+
else:
|
| 119 |
+
# See https://docs.python.org/3.14/library/annotationlib.html#using-annotations-in-a-metaclass:
|
| 120 |
+
from annotationlib import Format, call_annotate_function, get_annotate_from_class_namespace
|
| 121 |
+
|
| 122 |
+
if annotate := get_annotate_from_class_namespace(namespace):
|
| 123 |
+
raw_annotations = call_annotate_function(annotate, format=Format.FORWARDREF)
|
| 124 |
+
else:
|
| 125 |
+
raw_annotations = {}
|
| 126 |
+
else:
|
| 127 |
+
raw_annotations = namespace.get('__annotations__', {})
|
| 128 |
+
|
| 129 |
+
base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases)
|
| 130 |
+
|
| 131 |
+
config_wrapper = ConfigWrapper.for_model(bases, namespace, raw_annotations, kwargs)
|
| 132 |
+
namespace['model_config'] = config_wrapper.config_dict
|
| 133 |
+
private_attributes = inspect_namespace(
|
| 134 |
+
namespace, raw_annotations, config_wrapper.ignored_types, class_vars, base_field_names
|
| 135 |
+
)
|
| 136 |
+
if private_attributes or base_private_attributes:
|
| 137 |
+
original_model_post_init = get_model_post_init(namespace, bases)
|
| 138 |
+
if original_model_post_init is not None:
|
| 139 |
+
# if there are private attributes and a model_post_init function, we handle both
|
| 140 |
+
|
| 141 |
+
@wraps(original_model_post_init)
|
| 142 |
+
def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None:
|
| 143 |
+
"""We need to both initialize private attributes and call the user-defined model_post_init
|
| 144 |
+
method.
|
| 145 |
+
"""
|
| 146 |
+
init_private_attributes(self, context)
|
| 147 |
+
original_model_post_init(self, context)
|
| 148 |
+
|
| 149 |
+
namespace['model_post_init'] = wrapped_model_post_init
|
| 150 |
+
else:
|
| 151 |
+
namespace['model_post_init'] = init_private_attributes
|
| 152 |
+
|
| 153 |
+
namespace['__class_vars__'] = class_vars
|
| 154 |
+
namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes}
|
| 155 |
+
|
| 156 |
+
cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs))
|
| 157 |
+
BaseModel_ = import_cached_base_model()
|
| 158 |
+
|
| 159 |
+
mro = cls.__mro__
|
| 160 |
+
if Generic in mro and mro.index(Generic) < mro.index(BaseModel_):
|
| 161 |
+
warnings.warn(
|
| 162 |
+
GenericBeforeBaseModelWarning(
|
| 163 |
+
'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) '
|
| 164 |
+
'for pydantic generics to work properly.'
|
| 165 |
+
),
|
| 166 |
+
stacklevel=2,
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False)
|
| 170 |
+
cls.__pydantic_post_init__ = (
|
| 171 |
+
None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init'
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
cls.__pydantic_setattr_handlers__ = {}
|
| 175 |
+
|
| 176 |
+
cls.__pydantic_decorators__ = DecoratorInfos.build(cls, replace_wrapped_methods=True)
|
| 177 |
+
cls.__pydantic_decorators__.update_from_config(config_wrapper)
|
| 178 |
+
|
| 179 |
+
# Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class
|
| 180 |
+
if __pydantic_generic_metadata__:
|
| 181 |
+
cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__
|
| 182 |
+
else:
|
| 183 |
+
parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ())
|
| 184 |
+
parameters = getattr(cls, '__parameters__', None) or parent_parameters
|
| 185 |
+
if parameters and parent_parameters and not all(x in parameters for x in parent_parameters):
|
| 186 |
+
from ..root_model import RootModelRootType
|
| 187 |
+
|
| 188 |
+
missing_parameters = tuple(x for x in parameters if x not in parent_parameters)
|
| 189 |
+
if RootModelRootType in parent_parameters and RootModelRootType not in parameters:
|
| 190 |
+
# This is a special case where the user has subclassed RootModel, but has not parameterized
|
| 191 |
+
# RootModel with the generic type identifiers being used. Ex:
|
| 192 |
+
# class MyModel(RootModel, Generic[T]):
|
| 193 |
+
# root: T
|
| 194 |
+
# Should instead just be:
|
| 195 |
+
# class MyModel(RootModel[T]):
|
| 196 |
+
# root: T
|
| 197 |
+
parameters_str = ', '.join([x.__name__ for x in missing_parameters])
|
| 198 |
+
error_message = (
|
| 199 |
+
f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) '
|
| 200 |
+
f'{parameters_str} in its parameters. '
|
| 201 |
+
f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.'
|
| 202 |
+
)
|
| 203 |
+
else:
|
| 204 |
+
combined_parameters = parent_parameters + missing_parameters
|
| 205 |
+
parameters_str = ', '.join([str(x) for x in combined_parameters])
|
| 206 |
+
generic_type_label = f'typing.Generic[{parameters_str}]'
|
| 207 |
+
error_message = (
|
| 208 |
+
f'All parameters must be present on typing.Generic;'
|
| 209 |
+
f' you should inherit from {generic_type_label}.'
|
| 210 |
+
)
|
| 211 |
+
if Generic not in bases: # pragma: no cover
|
| 212 |
+
# We raise an error here not because it is desirable, but because some cases are mishandled.
|
| 213 |
+
# It would be nice to remove this error and still have things behave as expected, it's just
|
| 214 |
+
# challenging because we are using a custom `__class_getitem__` to parametrize generic models,
|
| 215 |
+
# and not returning a typing._GenericAlias from it.
|
| 216 |
+
bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label])
|
| 217 |
+
error_message += (
|
| 218 |
+
f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)'
|
| 219 |
+
)
|
| 220 |
+
raise TypeError(error_message)
|
| 221 |
+
|
| 222 |
+
cls.__pydantic_generic_metadata__ = {
|
| 223 |
+
'origin': None,
|
| 224 |
+
'args': (),
|
| 225 |
+
'parameters': parameters,
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
cls.__pydantic_complete__ = False # Ensure this specific class gets completed
|
| 229 |
+
|
| 230 |
+
# preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
|
| 231 |
+
# for attributes not in `new_namespace` (e.g. private attributes)
|
| 232 |
+
for name, obj in private_attributes.items():
|
| 233 |
+
obj.__set_name__(cls, name)
|
| 234 |
+
|
| 235 |
+
if __pydantic_reset_parent_namespace__:
|
| 236 |
+
cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace())
|
| 237 |
+
parent_namespace: dict[str, Any] | None = getattr(cls, '__pydantic_parent_namespace__', None)
|
| 238 |
+
if isinstance(parent_namespace, dict):
|
| 239 |
+
parent_namespace = unpack_lenient_weakvaluedict(parent_namespace)
|
| 240 |
+
|
| 241 |
+
ns_resolver = NsResolver(parent_namespace=parent_namespace)
|
| 242 |
+
|
| 243 |
+
set_model_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver)
|
| 244 |
+
|
| 245 |
+
# This is also set in `complete_model_class()`, after schema gen because they are recreated.
|
| 246 |
+
# We set them here as well for backwards compatibility:
|
| 247 |
+
cls.__pydantic_computed_fields__ = {
|
| 248 |
+
k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
if config_wrapper.defer_build:
|
| 252 |
+
set_model_mocks(cls)
|
| 253 |
+
else:
|
| 254 |
+
# Any operation that requires accessing the field infos instances should be put inside
|
| 255 |
+
# `complete_model_class()`:
|
| 256 |
+
complete_model_class(
|
| 257 |
+
cls,
|
| 258 |
+
config_wrapper,
|
| 259 |
+
ns_resolver,
|
| 260 |
+
raise_errors=False,
|
| 261 |
+
create_model_module=_create_model_module,
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
if config_wrapper.frozen and '__hash__' not in namespace:
|
| 265 |
+
set_default_hash_func(cls, bases)
|
| 266 |
+
|
| 267 |
+
# using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__
|
| 268 |
+
# I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is
|
| 269 |
+
# only hit for _proper_ subclasses of BaseModel
|
| 270 |
+
super(cls, cls).__pydantic_init_subclass__(**kwargs) # type: ignore[misc]
|
| 271 |
+
return cls
|
| 272 |
+
else:
|
| 273 |
+
# These are instance variables, but have been assigned to `NoInitField` to trick the type checker.
|
| 274 |
+
for instance_slot in '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__':
|
| 275 |
+
namespace.pop(
|
| 276 |
+
instance_slot,
|
| 277 |
+
None, # In case the metaclass is used with a class other than `BaseModel`.
|
| 278 |
+
)
|
| 279 |
+
namespace.get('__annotations__', {}).clear()
|
| 280 |
+
return super().__new__(mcs, cls_name, bases, namespace, **kwargs)
|
| 281 |
+
|
| 282 |
+
if not TYPE_CHECKING: # pragma: no branch
|
| 283 |
+
# We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
|
| 284 |
+
|
| 285 |
+
def __getattr__(self, item: str) -> Any:
|
| 286 |
+
"""This is necessary to keep attribute access working for class attribute access."""
|
| 287 |
+
private_attributes = self.__dict__.get('__private_attributes__')
|
| 288 |
+
if private_attributes and item in private_attributes:
|
| 289 |
+
return private_attributes[item]
|
| 290 |
+
raise AttributeError(item)
|
| 291 |
+
|
| 292 |
+
@classmethod
|
| 293 |
+
def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]:
|
| 294 |
+
return _ModelNamespaceDict()
|
| 295 |
+
|
| 296 |
+
# Due to performance and memory issues, in the ABCMeta.__subclasscheck__ implementation, we don't support
|
| 297 |
+
# registered virtual subclasses. See https://github.com/python/cpython/issues/92810#issuecomment-2762454345.
|
| 298 |
+
# This may change once CPython is fixed (possibly in 3.15), in which case we should conditionally
|
| 299 |
+
# define `register()`.
|
| 300 |
+
def register(self, subclass: type[_T]) -> type[_T]:
|
| 301 |
+
warnings.warn(
|
| 302 |
+
f"For performance reasons, virtual subclasses registered using '{self.__qualname__}.register()' "
|
| 303 |
+
"are not supported in 'isinstance()' and 'issubclass()' checks.",
|
| 304 |
+
stacklevel=2,
|
| 305 |
+
)
|
| 306 |
+
return super().register(subclass)
|
| 307 |
+
|
| 308 |
+
__instancecheck__ = type.__instancecheck__ # pyright: ignore[reportAssignmentType]
|
| 309 |
+
__subclasscheck__ = type.__subclasscheck__ # pyright: ignore[reportAssignmentType]
|
| 310 |
+
|
| 311 |
+
@staticmethod
|
| 312 |
+
def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]:
|
| 313 |
+
BaseModel = import_cached_base_model()
|
| 314 |
+
|
| 315 |
+
field_names: set[str] = set()
|
| 316 |
+
class_vars: set[str] = set()
|
| 317 |
+
private_attributes: dict[str, ModelPrivateAttr] = {}
|
| 318 |
+
for base in bases:
|
| 319 |
+
if issubclass(base, BaseModel) and base is not BaseModel:
|
| 320 |
+
# model_fields might not be defined yet in the case of generics, so we use getattr here:
|
| 321 |
+
field_names.update(getattr(base, '__pydantic_fields__', {}).keys())
|
| 322 |
+
class_vars.update(base.__class_vars__)
|
| 323 |
+
private_attributes.update(base.__private_attributes__)
|
| 324 |
+
return field_names, class_vars, private_attributes
|
| 325 |
+
|
| 326 |
+
@property
|
| 327 |
+
@deprecated(
|
| 328 |
+
'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None
|
| 329 |
+
)
|
| 330 |
+
def __fields__(self) -> dict[str, FieldInfo]:
|
| 331 |
+
warnings.warn(
|
| 332 |
+
'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.',
|
| 333 |
+
PydanticDeprecatedSince20,
|
| 334 |
+
stacklevel=2,
|
| 335 |
+
)
|
| 336 |
+
return getattr(self, '__pydantic_fields__', {})
|
| 337 |
+
|
| 338 |
+
@property
|
| 339 |
+
def __pydantic_fields_complete__(self) -> bool:
|
| 340 |
+
"""Whether the fields were successfully collected (i.e. type hints were successfully resolved).
|
| 341 |
+
|
| 342 |
+
This is a private attribute, not meant to be used outside Pydantic.
|
| 343 |
+
"""
|
| 344 |
+
if '__pydantic_fields__' not in self.__dict__:
|
| 345 |
+
return False
|
| 346 |
+
|
| 347 |
+
field_infos = cast('dict[str, FieldInfo]', self.__pydantic_fields__) # pyright: ignore[reportAttributeAccessIssue]
|
| 348 |
+
|
| 349 |
+
pydantic_extra_info = cast('PydanticExtraInfo | None', self.__pydantic_extra_info__) # pyright: ignore[reportAttributeAccessIssue]
|
| 350 |
+
if pydantic_extra_info is not None:
|
| 351 |
+
extra_complete = pydantic_extra_info.complete
|
| 352 |
+
else:
|
| 353 |
+
extra_complete = True
|
| 354 |
+
|
| 355 |
+
return all(field_info._complete for field_info in field_infos.values()) and extra_complete
|
| 356 |
+
|
| 357 |
+
def __dir__(self) -> list[str]:
|
| 358 |
+
attributes = list(super().__dir__())
|
| 359 |
+
if '__fields__' in attributes:
|
| 360 |
+
attributes.remove('__fields__')
|
| 361 |
+
return attributes
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def init_private_attributes(self: BaseModel, context: Any, /) -> None:
|
| 365 |
+
"""This function is meant to behave like a BaseModel method to initialize private attributes.
|
| 366 |
+
|
| 367 |
+
It takes context as an argument since that's what pydantic-core passes when calling it.
|
| 368 |
+
|
| 369 |
+
Args:
|
| 370 |
+
self: The BaseModel instance.
|
| 371 |
+
context: The context.
|
| 372 |
+
"""
|
| 373 |
+
if getattr(self, '__pydantic_private__', None) is None:
|
| 374 |
+
pydantic_private = {}
|
| 375 |
+
for name, private_attr in self.__private_attributes__.items():
|
| 376 |
+
# Avoid needlessly creating a new dict for the validated data:
|
| 377 |
+
if private_attr.default_factory_takes_validated_data:
|
| 378 |
+
default = private_attr.get_default(
|
| 379 |
+
call_default_factory=True, validated_data={**self.__dict__, **pydantic_private}
|
| 380 |
+
)
|
| 381 |
+
else:
|
| 382 |
+
default = private_attr.get_default(call_default_factory=True)
|
| 383 |
+
if default is not PydanticUndefined:
|
| 384 |
+
pydantic_private[name] = default
|
| 385 |
+
object_setattr(self, '__pydantic_private__', pydantic_private)
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None:
|
| 389 |
+
"""Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined."""
|
| 390 |
+
if 'model_post_init' in namespace:
|
| 391 |
+
return namespace['model_post_init']
|
| 392 |
+
|
| 393 |
+
BaseModel = import_cached_base_model()
|
| 394 |
+
|
| 395 |
+
model_post_init = get_attribute_from_bases(bases, 'model_post_init')
|
| 396 |
+
if model_post_init is not BaseModel.model_post_init:
|
| 397 |
+
return model_post_init
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def inspect_namespace( # noqa C901
|
| 401 |
+
namespace: dict[str, Any],
|
| 402 |
+
raw_annotations: dict[str, Any],
|
| 403 |
+
ignored_types: tuple[type[Any], ...],
|
| 404 |
+
base_class_vars: set[str],
|
| 405 |
+
base_class_fields: set[str],
|
| 406 |
+
) -> dict[str, ModelPrivateAttr]:
|
| 407 |
+
"""Iterate over the namespace and:
|
| 408 |
+
* gather private attributes
|
| 409 |
+
* check for items which look like fields but are not (e.g. have no annotation) and warn.
|
| 410 |
+
|
| 411 |
+
Args:
|
| 412 |
+
namespace: The attribute dictionary of the class to be created.
|
| 413 |
+
raw_annotations: The (non-evaluated) annotations of the model.
|
| 414 |
+
ignored_types: A tuple of ignore types.
|
| 415 |
+
base_class_vars: A set of base class class variables.
|
| 416 |
+
base_class_fields: A set of base class fields.
|
| 417 |
+
|
| 418 |
+
Returns:
|
| 419 |
+
A dict containing private attributes info.
|
| 420 |
+
|
| 421 |
+
Raises:
|
| 422 |
+
TypeError: If there is a `__root__` field in model.
|
| 423 |
+
NameError: If private attribute name is invalid.
|
| 424 |
+
PydanticUserError:
|
| 425 |
+
- If a field does not have a type annotation.
|
| 426 |
+
- If a field on base class was overridden by a non-annotated attribute.
|
| 427 |
+
"""
|
| 428 |
+
from ..fields import ModelPrivateAttr, PrivateAttr
|
| 429 |
+
|
| 430 |
+
FieldInfo = import_cached_field_info()
|
| 431 |
+
|
| 432 |
+
all_ignored_types = ignored_types + default_ignored_types()
|
| 433 |
+
|
| 434 |
+
private_attributes: dict[str, ModelPrivateAttr] = {}
|
| 435 |
+
|
| 436 |
+
if '__root__' in raw_annotations or '__root__' in namespace:
|
| 437 |
+
raise TypeError("To define root models, use `pydantic.RootModel` rather than a field called '__root__'")
|
| 438 |
+
|
| 439 |
+
ignored_names: set[str] = set()
|
| 440 |
+
for var_name, value in list(namespace.items()):
|
| 441 |
+
if var_name == 'model_config' or var_name == '__pydantic_extra__':
|
| 442 |
+
continue
|
| 443 |
+
elif (
|
| 444 |
+
isinstance(value, type)
|
| 445 |
+
and value.__module__ == namespace['__module__']
|
| 446 |
+
and '__qualname__' in namespace
|
| 447 |
+
and value.__qualname__.startswith(f'{namespace["__qualname__"]}.')
|
| 448 |
+
):
|
| 449 |
+
# `value` is a nested type defined in this namespace; don't error
|
| 450 |
+
continue
|
| 451 |
+
elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools':
|
| 452 |
+
ignored_names.add(var_name)
|
| 453 |
+
continue
|
| 454 |
+
elif isinstance(value, ModelPrivateAttr):
|
| 455 |
+
if var_name.startswith('__'):
|
| 456 |
+
raise NameError(
|
| 457 |
+
'Private attributes must not use dunder names;'
|
| 458 |
+
f' use a single underscore prefix instead of {var_name!r}.'
|
| 459 |
+
)
|
| 460 |
+
elif is_valid_field_name(var_name):
|
| 461 |
+
raise NameError(
|
| 462 |
+
'Private attributes must not use valid field names;'
|
| 463 |
+
f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.'
|
| 464 |
+
)
|
| 465 |
+
private_attributes[var_name] = value
|
| 466 |
+
del namespace[var_name]
|
| 467 |
+
elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name):
|
| 468 |
+
suggested_name = var_name.lstrip('_') or 'my_field' # don't suggest '' for all-underscore name
|
| 469 |
+
raise NameError(
|
| 470 |
+
f'Fields must not use names with leading underscores;'
|
| 471 |
+
f' e.g., use {suggested_name!r} instead of {var_name!r}.'
|
| 472 |
+
)
|
| 473 |
+
|
| 474 |
+
elif var_name.startswith('__'):
|
| 475 |
+
continue
|
| 476 |
+
elif is_valid_privateattr_name(var_name):
|
| 477 |
+
if var_name not in raw_annotations or not is_classvar_annotation(raw_annotations[var_name]):
|
| 478 |
+
private_attributes[var_name] = cast(ModelPrivateAttr, PrivateAttr(default=value))
|
| 479 |
+
del namespace[var_name]
|
| 480 |
+
elif var_name in base_class_vars:
|
| 481 |
+
continue
|
| 482 |
+
elif var_name not in raw_annotations:
|
| 483 |
+
if var_name in base_class_fields:
|
| 484 |
+
raise PydanticUserError(
|
| 485 |
+
f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. '
|
| 486 |
+
f'All field definitions, including overrides, require a type annotation.',
|
| 487 |
+
code='model-field-overridden',
|
| 488 |
+
)
|
| 489 |
+
elif isinstance(value, FieldInfo):
|
| 490 |
+
raise PydanticUserError(
|
| 491 |
+
f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation'
|
| 492 |
+
)
|
| 493 |
+
else:
|
| 494 |
+
raise PydanticUserError(
|
| 495 |
+
f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a '
|
| 496 |
+
f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this '
|
| 497 |
+
f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.",
|
| 498 |
+
code='model-field-missing-annotation',
|
| 499 |
+
)
|
| 500 |
+
|
| 501 |
+
for ann_name, ann_type in raw_annotations.items():
|
| 502 |
+
if (
|
| 503 |
+
is_valid_privateattr_name(ann_name)
|
| 504 |
+
and ann_name not in private_attributes
|
| 505 |
+
and ann_name not in ignored_names
|
| 506 |
+
# This condition can be a false negative when `ann_type` is stringified,
|
| 507 |
+
# but it is handled in most cases in `set_model_fields`:
|
| 508 |
+
and not is_classvar_annotation(ann_type)
|
| 509 |
+
and ann_type not in all_ignored_types
|
| 510 |
+
and getattr(ann_type, '__module__', None) != 'functools'
|
| 511 |
+
):
|
| 512 |
+
if isinstance(ann_type, str):
|
| 513 |
+
# Walking up the frames to get the module namespace where the model is defined
|
| 514 |
+
# (as the model class wasn't created yet, we unfortunately can't use `cls.__module__`):
|
| 515 |
+
frame = sys._getframe(2)
|
| 516 |
+
if frame is not None:
|
| 517 |
+
try:
|
| 518 |
+
ann_type = eval_type_backport(
|
| 519 |
+
_make_forward_ref(ann_type, is_argument=False, is_class=True),
|
| 520 |
+
globalns=frame.f_globals,
|
| 521 |
+
localns=frame.f_locals,
|
| 522 |
+
)
|
| 523 |
+
except (NameError, TypeError):
|
| 524 |
+
pass
|
| 525 |
+
|
| 526 |
+
if typing_objects.is_annotated(get_origin(ann_type)):
|
| 527 |
+
_, *metadata = get_args(ann_type)
|
| 528 |
+
private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None)
|
| 529 |
+
if private_attr is not None:
|
| 530 |
+
private_attributes[ann_name] = private_attr
|
| 531 |
+
continue
|
| 532 |
+
private_attributes[ann_name] = PrivateAttr()
|
| 533 |
+
|
| 534 |
+
return private_attributes
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None:
|
| 538 |
+
base_hash_func = get_attribute_from_bases(bases, '__hash__')
|
| 539 |
+
new_hash_func = make_hash_func(cls)
|
| 540 |
+
if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__:
|
| 541 |
+
# If `__hash__` is some default, we generate a hash function.
|
| 542 |
+
# It will be `None` if not overridden from BaseModel.
|
| 543 |
+
# It may be `object.__hash__` if there is another
|
| 544 |
+
# parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`).
|
| 545 |
+
# It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model.
|
| 546 |
+
# In the last case we still need a new hash function to account for new `model_fields`.
|
| 547 |
+
cls.__hash__ = new_hash_func
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
def make_hash_func(cls: type[BaseModel]) -> Any:
|
| 551 |
+
getter = operator.itemgetter(*cls.__pydantic_fields__.keys()) if cls.__pydantic_fields__ else lambda _: 0
|
| 552 |
+
|
| 553 |
+
def hash_func(self: Any) -> int:
|
| 554 |
+
try:
|
| 555 |
+
return hash(getter(self.__dict__))
|
| 556 |
+
except KeyError:
|
| 557 |
+
# In rare cases (such as when using the deprecated copy method), the __dict__ may not contain
|
| 558 |
+
# all model fields, which is how we can get here.
|
| 559 |
+
# getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys,
|
| 560 |
+
# and wrapping it in a `try` doesn't slow things down much in the common case.
|
| 561 |
+
return hash(getter(SafeGetItemProxy(self.__dict__)))
|
| 562 |
+
|
| 563 |
+
return hash_func
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
def set_model_fields(
|
| 567 |
+
cls: type[BaseModel],
|
| 568 |
+
config_wrapper: ConfigWrapper,
|
| 569 |
+
ns_resolver: NsResolver,
|
| 570 |
+
) -> None:
|
| 571 |
+
"""Collect and set `cls.__pydantic_fields__` and `cls.__class_vars__`.
|
| 572 |
+
|
| 573 |
+
Args:
|
| 574 |
+
cls: BaseModel or dataclass.
|
| 575 |
+
config_wrapper: The config wrapper instance.
|
| 576 |
+
ns_resolver: Namespace resolver to use when getting model annotations.
|
| 577 |
+
"""
|
| 578 |
+
typevars_map = get_model_typevars_map(cls)
|
| 579 |
+
fields, pydantic_extra_info, class_vars = collect_model_fields(
|
| 580 |
+
cls, config_wrapper, ns_resolver, typevars_map=typevars_map
|
| 581 |
+
)
|
| 582 |
+
|
| 583 |
+
cls.__pydantic_fields__ = fields
|
| 584 |
+
cls.__pydantic_extra_info__ = pydantic_extra_info
|
| 585 |
+
cls.__class_vars__.update(class_vars)
|
| 586 |
+
|
| 587 |
+
for k in class_vars:
|
| 588 |
+
# Class vars should not be private attributes
|
| 589 |
+
# We remove them _here_ and not earlier because we rely on inspecting the class to determine its classvars,
|
| 590 |
+
# but private attributes are determined by inspecting the namespace _prior_ to class creation.
|
| 591 |
+
# In the case that a classvar with a leading-'_' is defined via a ForwardRef (e.g., when using
|
| 592 |
+
# `__future__.annotations`), we want to remove the private attribute which was detected _before_ we knew it
|
| 593 |
+
# evaluated to a classvar
|
| 594 |
+
|
| 595 |
+
value = cls.__private_attributes__.pop(k, None)
|
| 596 |
+
if value is not None and value.default is not PydanticUndefined:
|
| 597 |
+
setattr(cls, k, value.default)
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
def complete_model_class(
|
| 601 |
+
cls: type[BaseModel],
|
| 602 |
+
config_wrapper: ConfigWrapper,
|
| 603 |
+
ns_resolver: NsResolver,
|
| 604 |
+
*,
|
| 605 |
+
raise_errors: bool = True,
|
| 606 |
+
call_on_complete_hook: bool = True,
|
| 607 |
+
create_model_module: str | None = None,
|
| 608 |
+
is_force_rebuild: bool = False,
|
| 609 |
+
) -> bool:
|
| 610 |
+
"""Finish building a model class.
|
| 611 |
+
|
| 612 |
+
This logic must be called after class has been created since validation functions must be bound
|
| 613 |
+
and `get_type_hints` requires a class object.
|
| 614 |
+
|
| 615 |
+
Args:
|
| 616 |
+
cls: BaseModel or dataclass.
|
| 617 |
+
config_wrapper: The config wrapper instance.
|
| 618 |
+
ns_resolver: The namespace resolver instance to use during schema building.
|
| 619 |
+
raise_errors: Whether to raise errors.
|
| 620 |
+
call_on_complete_hook: Whether to call the `__pydantic_on_complete__` hook.
|
| 621 |
+
create_model_module: The module of the class to be created, if created by `create_model`.
|
| 622 |
+
is_force_rebuild: Whether the model is being force-rebuilt (if True, pre-built serializers and
|
| 623 |
+
validators are not used, to avoid stale references).
|
| 624 |
+
|
| 625 |
+
Returns:
|
| 626 |
+
`True` if the model is successfully completed, else `False`.
|
| 627 |
+
|
| 628 |
+
Raises:
|
| 629 |
+
PydanticUndefinedAnnotation: If PydanticUndefinedAnnotation occurs in __get_pydantic_core_schema__
|
| 630 |
+
and `raise_errors=True`.
|
| 631 |
+
"""
|
| 632 |
+
typevars_map = get_model_typevars_map(cls)
|
| 633 |
+
|
| 634 |
+
if not cls.__pydantic_fields_complete__:
|
| 635 |
+
# Note: when coming from `ModelMetaclass.__new__()`, this results in fields being built twice.
|
| 636 |
+
# We do so a second time here so that we can get the ``NameError`` for the specific undefined annotation.
|
| 637 |
+
# Alternatively, we could let `GenerateSchema()` raise the error, but there are cases where incomplete
|
| 638 |
+
# fields are inherited in `collect_model_fields()` and can actually have their annotation resolved in the
|
| 639 |
+
# generate schema process. As we want to avoid having `__pydantic_fields_complete__` set to `False`
|
| 640 |
+
# when `__pydantic_complete__` is `True`, we rebuild here:
|
| 641 |
+
try:
|
| 642 |
+
cls.__pydantic_fields__, cls.__pydantic_extra_info__ = rebuild_model_fields(
|
| 643 |
+
cls,
|
| 644 |
+
config_wrapper=config_wrapper,
|
| 645 |
+
ns_resolver=ns_resolver,
|
| 646 |
+
typevars_map=typevars_map,
|
| 647 |
+
)
|
| 648 |
+
except NameError as e:
|
| 649 |
+
exc = PydanticUndefinedAnnotation.from_name_error(e)
|
| 650 |
+
set_model_mocks(cls, f'`{exc.name}`')
|
| 651 |
+
if raise_errors:
|
| 652 |
+
raise exc from e
|
| 653 |
+
|
| 654 |
+
if not raise_errors and not cls.__pydantic_fields_complete__:
|
| 655 |
+
# No need to continue with schema gen, it is guaranteed to fail
|
| 656 |
+
return False
|
| 657 |
+
|
| 658 |
+
assert cls.__pydantic_fields_complete__
|
| 659 |
+
|
| 660 |
+
gen_schema = GenerateSchema(
|
| 661 |
+
config_wrapper,
|
| 662 |
+
ns_resolver,
|
| 663 |
+
typevars_map,
|
| 664 |
+
)
|
| 665 |
+
|
| 666 |
+
try:
|
| 667 |
+
schema = gen_schema.generate_schema(cls)
|
| 668 |
+
except PydanticUndefinedAnnotation as e:
|
| 669 |
+
if raise_errors:
|
| 670 |
+
raise
|
| 671 |
+
set_model_mocks(cls, f'`{e.name}`')
|
| 672 |
+
return False
|
| 673 |
+
|
| 674 |
+
core_config = config_wrapper.core_config(title=cls.__name__)
|
| 675 |
+
|
| 676 |
+
try:
|
| 677 |
+
schema = gen_schema.clean_schema(schema)
|
| 678 |
+
except InvalidSchemaError:
|
| 679 |
+
set_model_mocks(cls)
|
| 680 |
+
return False
|
| 681 |
+
|
| 682 |
+
# This needs to happen *after* model schema generation, as the return types
|
| 683 |
+
# of the properties are evaluated and the `ComputedFieldInfo` are recreated:
|
| 684 |
+
cls.__pydantic_computed_fields__ = {k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()}
|
| 685 |
+
|
| 686 |
+
set_deprecated_descriptors(cls)
|
| 687 |
+
|
| 688 |
+
cls.__pydantic_core_schema__ = schema
|
| 689 |
+
|
| 690 |
+
cls.__pydantic_validator__ = create_schema_validator(
|
| 691 |
+
schema,
|
| 692 |
+
cls,
|
| 693 |
+
create_model_module or cls.__module__,
|
| 694 |
+
cls.__qualname__,
|
| 695 |
+
'create_model' if create_model_module else 'BaseModel',
|
| 696 |
+
core_config,
|
| 697 |
+
config_wrapper.plugin_settings,
|
| 698 |
+
_use_prebuilt=not is_force_rebuild,
|
| 699 |
+
)
|
| 700 |
+
cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config, _use_prebuilt=not is_force_rebuild)
|
| 701 |
+
|
| 702 |
+
# set __signature__ attr only for model class, but not for its instances
|
| 703 |
+
# (because instances can define `__call__`, and `inspect.signature` shouldn't
|
| 704 |
+
# use the `__signature__` attribute and instead generate from `__call__`).
|
| 705 |
+
cls.__signature__ = LazyClassAttribute(
|
| 706 |
+
'__signature__',
|
| 707 |
+
partial(
|
| 708 |
+
generate_pydantic_signature,
|
| 709 |
+
init=cls.__init__,
|
| 710 |
+
fields=cls.__pydantic_fields__,
|
| 711 |
+
validate_by_name=config_wrapper.validate_by_name,
|
| 712 |
+
extra=config_wrapper.extra,
|
| 713 |
+
),
|
| 714 |
+
)
|
| 715 |
+
|
| 716 |
+
cls.__pydantic_complete__ = True
|
| 717 |
+
|
| 718 |
+
if call_on_complete_hook:
|
| 719 |
+
cls.__pydantic_on_complete__()
|
| 720 |
+
|
| 721 |
+
return True
|
| 722 |
+
|
| 723 |
+
|
| 724 |
+
def set_deprecated_descriptors(cls: type[BaseModel]) -> None:
|
| 725 |
+
"""Set data descriptors on the class for deprecated fields."""
|
| 726 |
+
for field, field_info in cls.__pydantic_fields__.items():
|
| 727 |
+
if (msg := field_info.deprecation_message) is not None:
|
| 728 |
+
desc = _DeprecatedFieldDescriptor(msg)
|
| 729 |
+
desc.__set_name__(cls, field)
|
| 730 |
+
setattr(cls, field, desc)
|
| 731 |
+
|
| 732 |
+
for field, computed_field_info in cls.__pydantic_computed_fields__.items():
|
| 733 |
+
if (
|
| 734 |
+
(msg := computed_field_info.deprecation_message) is not None
|
| 735 |
+
# Avoid having two warnings emitted:
|
| 736 |
+
and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__')
|
| 737 |
+
):
|
| 738 |
+
desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property)
|
| 739 |
+
desc.__set_name__(cls, field)
|
| 740 |
+
setattr(cls, field, desc)
|
| 741 |
+
|
| 742 |
+
|
| 743 |
+
class _DeprecatedFieldDescriptor:
|
| 744 |
+
"""Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.
|
| 745 |
+
|
| 746 |
+
Attributes:
|
| 747 |
+
msg: The deprecation message to be emitted.
|
| 748 |
+
wrapped_property: The property instance if the deprecated field is a computed field, or `None`.
|
| 749 |
+
field_name: The name of the field being deprecated.
|
| 750 |
+
"""
|
| 751 |
+
|
| 752 |
+
field_name: str
|
| 753 |
+
|
| 754 |
+
def __init__(self, msg: str, wrapped_property: property | None = None) -> None:
|
| 755 |
+
self.msg = msg
|
| 756 |
+
self.wrapped_property = wrapped_property
|
| 757 |
+
|
| 758 |
+
def __set_name__(self, cls: type[BaseModel], name: str) -> None:
|
| 759 |
+
self.field_name = name
|
| 760 |
+
|
| 761 |
+
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
|
| 762 |
+
if obj is None:
|
| 763 |
+
if self.wrapped_property is not None:
|
| 764 |
+
return self.wrapped_property.__get__(None, obj_type)
|
| 765 |
+
raise AttributeError(self.field_name)
|
| 766 |
+
|
| 767 |
+
warnings.warn(self.msg, DeprecationWarning, stacklevel=2)
|
| 768 |
+
|
| 769 |
+
if self.wrapped_property is not None:
|
| 770 |
+
return self.wrapped_property.__get__(obj, obj_type)
|
| 771 |
+
return obj.__dict__[self.field_name]
|
| 772 |
+
|
| 773 |
+
# Defined to make it a data descriptor and take precedence over the instance's dictionary.
|
| 774 |
+
# Note that it will not be called when setting a value on a model instance
|
| 775 |
+
# as `BaseModel.__setattr__` is defined and takes priority.
|
| 776 |
+
def __set__(self, obj: Any, value: Any) -> NoReturn:
|
| 777 |
+
raise AttributeError(self.field_name)
|
| 778 |
+
|
| 779 |
+
|
| 780 |
+
class _PydanticWeakRef:
|
| 781 |
+
"""Wrapper for `weakref.ref` that enables `pickle` serialization.
|
| 782 |
+
|
| 783 |
+
Cloudpickle fails to serialize weakref.ref objects due to an arcane error related to
|
| 784 |
+
to abstract base classes (`abc.ABC`). This class works around the issue by wrapping
|
| 785 |
+
`weakref.ref` instead of subclassing it.
|
| 786 |
+
|
| 787 |
+
See https://github.com/pydantic/pydantic/issues/6763 for context.
|
| 788 |
+
|
| 789 |
+
Semantics:
|
| 790 |
+
- If not pickled, behaves the same as a `weakref.ref`.
|
| 791 |
+
- If pickled along with the referenced object, the same `weakref.ref` behavior
|
| 792 |
+
will be maintained between them after unpickling.
|
| 793 |
+
- If pickled without the referenced object, after unpickling the underlying
|
| 794 |
+
reference will be cleared (`__call__` will always return `None`).
|
| 795 |
+
"""
|
| 796 |
+
|
| 797 |
+
def __init__(self, obj: Any):
|
| 798 |
+
if obj is None:
|
| 799 |
+
# The object will be `None` upon deserialization if the serialized weakref
|
| 800 |
+
# had lost its underlying object.
|
| 801 |
+
self._wr = None
|
| 802 |
+
else:
|
| 803 |
+
self._wr = weakref.ref(obj)
|
| 804 |
+
|
| 805 |
+
def __call__(self) -> Any:
|
| 806 |
+
if self._wr is None:
|
| 807 |
+
return None
|
| 808 |
+
else:
|
| 809 |
+
return self._wr()
|
| 810 |
+
|
| 811 |
+
def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]:
|
| 812 |
+
return _PydanticWeakRef, (self(),)
|
| 813 |
+
|
| 814 |
+
|
| 815 |
+
def build_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
|
| 816 |
+
"""Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs.
|
| 817 |
+
|
| 818 |
+
We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values
|
| 819 |
+
in a WeakValueDictionary.
|
| 820 |
+
|
| 821 |
+
The `unpack_lenient_weakvaluedict` function can be used to reverse this operation.
|
| 822 |
+
"""
|
| 823 |
+
if d is None:
|
| 824 |
+
return None
|
| 825 |
+
result = {}
|
| 826 |
+
for k, v in d.items():
|
| 827 |
+
try:
|
| 828 |
+
proxy = _PydanticWeakRef(v)
|
| 829 |
+
except TypeError:
|
| 830 |
+
proxy = v
|
| 831 |
+
result[k] = proxy
|
| 832 |
+
return result
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
|
| 836 |
+
"""Inverts the transform performed by `build_lenient_weakvaluedict`."""
|
| 837 |
+
if d is None:
|
| 838 |
+
return None
|
| 839 |
+
|
| 840 |
+
result = {}
|
| 841 |
+
for k, v in d.items():
|
| 842 |
+
if isinstance(v, _PydanticWeakRef):
|
| 843 |
+
v = v()
|
| 844 |
+
if v is not None:
|
| 845 |
+
result[k] = v
|
| 846 |
+
else:
|
| 847 |
+
result[k] = v
|
| 848 |
+
return result
|
| 849 |
+
|
| 850 |
+
|
| 851 |
+
@cache
|
| 852 |
+
def default_ignored_types() -> tuple[type[Any], ...]:
|
| 853 |
+
from ..fields import ComputedFieldInfo
|
| 854 |
+
|
| 855 |
+
ignored_types = [
|
| 856 |
+
FunctionType,
|
| 857 |
+
property,
|
| 858 |
+
classmethod,
|
| 859 |
+
staticmethod,
|
| 860 |
+
PydanticDescriptorProxy,
|
| 861 |
+
ComputedFieldInfo,
|
| 862 |
+
TypeAliasType, # from `typing_extensions`
|
| 863 |
+
]
|
| 864 |
+
|
| 865 |
+
if sys.version_info >= (3, 12):
|
| 866 |
+
ignored_types.append(typing.TypeAliasType)
|
| 867 |
+
|
| 868 |
+
return tuple(ignored_types)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
from collections.abc import Generator, Iterator, Mapping
|
| 5 |
+
from contextlib import contextmanager
|
| 6 |
+
from functools import cached_property
|
| 7 |
+
from typing import Any, Callable, NamedTuple, TypeVar
|
| 8 |
+
|
| 9 |
+
from typing_extensions import ParamSpec, TypeAlias, TypeAliasType, TypeVarTuple
|
| 10 |
+
|
| 11 |
+
GlobalsNamespace: TypeAlias = 'dict[str, Any]'
|
| 12 |
+
"""A global namespace.
|
| 13 |
+
|
| 14 |
+
In most cases, this is a reference to the `__dict__` attribute of a module.
|
| 15 |
+
This namespace type is expected as the `globals` argument during annotations evaluation.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
MappingNamespace: TypeAlias = Mapping[str, Any]
|
| 19 |
+
"""Any kind of namespace.
|
| 20 |
+
|
| 21 |
+
In most cases, this is a local namespace (e.g. the `__dict__` attribute of a class,
|
| 22 |
+
the [`f_locals`][frame.f_locals] attribute of a frame object, when dealing with types
|
| 23 |
+
defined inside functions).
|
| 24 |
+
This namespace type is expected as the `locals` argument during annotations evaluation.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
_TypeVarLike: TypeAlias = 'TypeVar | ParamSpec | TypeVarTuple'
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class NamespacesTuple(NamedTuple):
|
| 31 |
+
"""A tuple of globals and locals to be used during annotations evaluation.
|
| 32 |
+
|
| 33 |
+
This datastructure is defined as a named tuple so that it can easily be unpacked:
|
| 34 |
+
|
| 35 |
+
```python {lint="skip" test="skip"}
|
| 36 |
+
def eval_type(typ: type[Any], ns: NamespacesTuple) -> None:
|
| 37 |
+
return eval(typ, *ns)
|
| 38 |
+
```
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
globals: GlobalsNamespace
|
| 42 |
+
"""The namespace to be used as the `globals` argument during annotations evaluation."""
|
| 43 |
+
|
| 44 |
+
locals: MappingNamespace
|
| 45 |
+
"""The namespace to be used as the `locals` argument during annotations evaluation."""
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def get_module_ns_of(obj: Any) -> dict[str, Any]:
|
| 49 |
+
"""Get the namespace of the module where the object is defined.
|
| 50 |
+
|
| 51 |
+
Caution: this function does not return a copy of the module namespace, so the result
|
| 52 |
+
should not be mutated. The burden of enforcing this is on the caller.
|
| 53 |
+
"""
|
| 54 |
+
module_name = getattr(obj, '__module__', None)
|
| 55 |
+
if module_name:
|
| 56 |
+
try:
|
| 57 |
+
return sys.modules[module_name].__dict__
|
| 58 |
+
except KeyError:
|
| 59 |
+
# happens occasionally, see https://github.com/pydantic/pydantic/issues/2363
|
| 60 |
+
return {}
|
| 61 |
+
return {}
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# Note that this class is almost identical to `collections.ChainMap`, but need to enforce
|
| 65 |
+
# immutable mappings here:
|
| 66 |
+
class LazyLocalNamespace(Mapping[str, Any]):
|
| 67 |
+
"""A lazily evaluated mapping, to be used as the `locals` argument during annotations evaluation.
|
| 68 |
+
|
| 69 |
+
While the [`eval`][eval] function expects a mapping as the `locals` argument, it only
|
| 70 |
+
performs `__getitem__` calls. The [`Mapping`][collections.abc.Mapping] abstract base class
|
| 71 |
+
is fully implemented only for type checking purposes.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
*namespaces: The namespaces to consider, in ascending order of priority.
|
| 75 |
+
|
| 76 |
+
Example:
|
| 77 |
+
```python {lint="skip" test="skip"}
|
| 78 |
+
ns = LazyLocalNamespace({'a': 1, 'b': 2}, {'a': 3})
|
| 79 |
+
ns['a']
|
| 80 |
+
#> 3
|
| 81 |
+
ns['b']
|
| 82 |
+
#> 2
|
| 83 |
+
```
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
def __init__(self, *namespaces: MappingNamespace) -> None:
|
| 87 |
+
self._namespaces = namespaces
|
| 88 |
+
|
| 89 |
+
@cached_property
|
| 90 |
+
def data(self) -> dict[str, Any]:
|
| 91 |
+
return {k: v for ns in self._namespaces for k, v in ns.items()}
|
| 92 |
+
|
| 93 |
+
def __len__(self) -> int:
|
| 94 |
+
return len(self.data)
|
| 95 |
+
|
| 96 |
+
def __getitem__(self, key: str) -> Any:
|
| 97 |
+
return self.data[key]
|
| 98 |
+
|
| 99 |
+
def __contains__(self, key: object) -> bool:
|
| 100 |
+
return key in self.data
|
| 101 |
+
|
| 102 |
+
def __iter__(self) -> Iterator[str]:
|
| 103 |
+
return iter(self.data)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def ns_for_function(obj: Callable[..., Any], parent_namespace: MappingNamespace | None = None) -> NamespacesTuple:
|
| 107 |
+
"""Return the global and local namespaces to be used when evaluating annotations for the provided function.
|
| 108 |
+
|
| 109 |
+
The global namespace will be the `__dict__` attribute of the module the function was defined in.
|
| 110 |
+
The local namespace will contain the `__type_params__` introduced by PEP 695.
|
| 111 |
+
|
| 112 |
+
Args:
|
| 113 |
+
obj: The object to use when building namespaces.
|
| 114 |
+
parent_namespace: Optional namespace to be added with the lowest priority in the local namespace.
|
| 115 |
+
If the passed function is a method, the `parent_namespace` will be the namespace of the class
|
| 116 |
+
the method is defined in. Thus, we also fetch type `__type_params__` from there (i.e. the
|
| 117 |
+
class-scoped type variables).
|
| 118 |
+
"""
|
| 119 |
+
locals_list: list[MappingNamespace] = []
|
| 120 |
+
if parent_namespace is not None:
|
| 121 |
+
locals_list.append(parent_namespace)
|
| 122 |
+
|
| 123 |
+
# Get the `__type_params__` attribute introduced by PEP 695.
|
| 124 |
+
# Note that the `typing._eval_type` function expects type params to be
|
| 125 |
+
# passed as a separate argument. However, internally, `_eval_type` calls
|
| 126 |
+
# `ForwardRef._evaluate` which will merge type params with the localns,
|
| 127 |
+
# essentially mimicking what we do here.
|
| 128 |
+
type_params: tuple[_TypeVarLike, ...] = getattr(obj, '__type_params__', ())
|
| 129 |
+
if parent_namespace is not None:
|
| 130 |
+
# We also fetch type params from the parent namespace. If present, it probably
|
| 131 |
+
# means the function was defined in a class. This is to support the following:
|
| 132 |
+
# https://github.com/python/cpython/issues/124089.
|
| 133 |
+
type_params += parent_namespace.get('__type_params__', ())
|
| 134 |
+
|
| 135 |
+
locals_list.append({t.__name__: t for t in type_params})
|
| 136 |
+
|
| 137 |
+
# What about short-circuiting to `obj.__globals__`?
|
| 138 |
+
globalns = get_module_ns_of(obj)
|
| 139 |
+
|
| 140 |
+
return NamespacesTuple(globalns, LazyLocalNamespace(*locals_list))
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
class NsResolver:
|
| 144 |
+
"""A class responsible for the namespaces resolving logic for annotations evaluation.
|
| 145 |
+
|
| 146 |
+
This class handles the namespace logic when evaluating annotations mainly for class objects.
|
| 147 |
+
|
| 148 |
+
It holds a stack of classes that are being inspected during the core schema building,
|
| 149 |
+
and the `types_namespace` property exposes the globals and locals to be used for
|
| 150 |
+
type annotation evaluation. Additionally -- if no class is present in the stack -- a
|
| 151 |
+
fallback globals and locals can be provided using the `namespaces_tuple` argument
|
| 152 |
+
(this is useful when generating a schema for a simple annotation, e.g. when using
|
| 153 |
+
`TypeAdapter`).
|
| 154 |
+
|
| 155 |
+
The namespace creation logic is unfortunately flawed in some cases, for backwards
|
| 156 |
+
compatibility reasons and to better support valid edge cases. See the description
|
| 157 |
+
for the `parent_namespace` argument and the example for more details.
|
| 158 |
+
|
| 159 |
+
Args:
|
| 160 |
+
namespaces_tuple: The default globals and locals to use if no class is present
|
| 161 |
+
on the stack. This can be useful when using the `GenerateSchema` class
|
| 162 |
+
with `TypeAdapter`, where the "type" being analyzed is a simple annotation.
|
| 163 |
+
parent_namespace: An optional parent namespace that will be added to the locals
|
| 164 |
+
with the lowest priority. For a given class defined in a function, the locals
|
| 165 |
+
of this function are usually used as the parent namespace:
|
| 166 |
+
|
| 167 |
+
```python {lint="skip" test="skip"}
|
| 168 |
+
from pydantic import BaseModel
|
| 169 |
+
|
| 170 |
+
def func() -> None:
|
| 171 |
+
SomeType = int
|
| 172 |
+
|
| 173 |
+
class Model(BaseModel):
|
| 174 |
+
f: 'SomeType'
|
| 175 |
+
|
| 176 |
+
# when collecting fields, an namespace resolver instance will be created
|
| 177 |
+
# this way:
|
| 178 |
+
# ns_resolver = NsResolver(parent_namespace={'SomeType': SomeType})
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
For backwards compatibility reasons and to support valid edge cases, this parent
|
| 182 |
+
namespace will be used for *every* type being pushed to the stack. In the future,
|
| 183 |
+
we might want to be smarter by only doing so when the type being pushed is defined
|
| 184 |
+
in the same module as the parent namespace.
|
| 185 |
+
|
| 186 |
+
Example:
|
| 187 |
+
```python {lint="skip" test="skip"}
|
| 188 |
+
ns_resolver = NsResolver(
|
| 189 |
+
parent_namespace={'fallback': 1},
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
class Sub:
|
| 193 |
+
m: 'Model'
|
| 194 |
+
|
| 195 |
+
class Model:
|
| 196 |
+
some_local = 1
|
| 197 |
+
sub: Sub
|
| 198 |
+
|
| 199 |
+
ns_resolver = NsResolver()
|
| 200 |
+
|
| 201 |
+
# This is roughly what happens when we build a core schema for `Model`:
|
| 202 |
+
with ns_resolver.push(Model):
|
| 203 |
+
ns_resolver.types_namespace
|
| 204 |
+
#> NamespacesTuple({'Sub': Sub}, {'Model': Model, 'some_local': 1})
|
| 205 |
+
# First thing to notice here, the model being pushed is added to the locals.
|
| 206 |
+
# Because `NsResolver` is being used during the model definition, it is not
|
| 207 |
+
# yet added to the globals. This is useful when resolving self-referencing annotations.
|
| 208 |
+
|
| 209 |
+
with ns_resolver.push(Sub):
|
| 210 |
+
ns_resolver.types_namespace
|
| 211 |
+
#> NamespacesTuple({'Sub': Sub}, {'Sub': Sub, 'Model': Model})
|
| 212 |
+
# Second thing to notice: `Sub` is present in both the globals and locals.
|
| 213 |
+
# This is not an issue, just that as described above, the model being pushed
|
| 214 |
+
# is added to the locals, but it happens to be present in the globals as well
|
| 215 |
+
# because it is already defined.
|
| 216 |
+
# Third thing to notice: `Model` is also added in locals. This is a backwards
|
| 217 |
+
# compatibility workaround that allows for `Sub` to be able to resolve `'Model'`
|
| 218 |
+
# correctly (as otherwise models would have to be rebuilt even though this
|
| 219 |
+
# doesn't look necessary).
|
| 220 |
+
```
|
| 221 |
+
"""
|
| 222 |
+
|
| 223 |
+
def __init__(
|
| 224 |
+
self,
|
| 225 |
+
namespaces_tuple: NamespacesTuple | None = None,
|
| 226 |
+
parent_namespace: MappingNamespace | None = None,
|
| 227 |
+
) -> None:
|
| 228 |
+
self._base_ns_tuple = namespaces_tuple or NamespacesTuple({}, {})
|
| 229 |
+
self._parent_ns = parent_namespace
|
| 230 |
+
self._types_stack: list[type[Any] | TypeAliasType] = []
|
| 231 |
+
|
| 232 |
+
@cached_property
|
| 233 |
+
def types_namespace(self) -> NamespacesTuple:
|
| 234 |
+
"""The current global and local namespaces to be used for annotations evaluation."""
|
| 235 |
+
if not self._types_stack:
|
| 236 |
+
# TODO: should we merge the parent namespace here?
|
| 237 |
+
# This is relevant for TypeAdapter, where there are no types on the stack, and we might
|
| 238 |
+
# need access to the parent_ns. Right now, we sidestep this in `type_adapter.py` by passing
|
| 239 |
+
# locals to both parent_ns and the base_ns_tuple, but this is a bit hacky.
|
| 240 |
+
# we might consider something like:
|
| 241 |
+
# if self._parent_ns is not None:
|
| 242 |
+
# # Hacky workarounds, see class docstring:
|
| 243 |
+
# # An optional parent namespace that will be added to the locals with the lowest priority
|
| 244 |
+
# locals_list: list[MappingNamespace] = [self._parent_ns, self._base_ns_tuple.locals]
|
| 245 |
+
# return NamespacesTuple(self._base_ns_tuple.globals, LazyLocalNamespace(*locals_list))
|
| 246 |
+
return self._base_ns_tuple
|
| 247 |
+
|
| 248 |
+
typ = self._types_stack[-1]
|
| 249 |
+
|
| 250 |
+
globalns = get_module_ns_of(typ)
|
| 251 |
+
|
| 252 |
+
locals_list: list[MappingNamespace] = []
|
| 253 |
+
# Hacky workarounds, see class docstring:
|
| 254 |
+
# An optional parent namespace that will be added to the locals with the lowest priority
|
| 255 |
+
if self._parent_ns is not None:
|
| 256 |
+
locals_list.append(self._parent_ns)
|
| 257 |
+
if len(self._types_stack) > 1:
|
| 258 |
+
first_type = self._types_stack[0]
|
| 259 |
+
locals_list.append({first_type.__name__: first_type})
|
| 260 |
+
|
| 261 |
+
# Adding `__type_params__` *before* `vars(typ)`, as the latter takes priority
|
| 262 |
+
# (see https://github.com/python/cpython/pull/120272).
|
| 263 |
+
# TODO `typ.__type_params__` when we drop support for Python 3.11:
|
| 264 |
+
type_params: tuple[_TypeVarLike, ...] = getattr(typ, '__type_params__', ())
|
| 265 |
+
if type_params:
|
| 266 |
+
# Adding `__type_params__` is mostly useful for generic classes defined using
|
| 267 |
+
# PEP 695 syntax *and* using forward annotations (see the example in
|
| 268 |
+
# https://github.com/python/cpython/issues/114053). For TypeAliasType instances,
|
| 269 |
+
# it is way less common, but still required if using a string annotation in the alias
|
| 270 |
+
# value, e.g. `type A[T] = 'T'` (which is not necessary in most cases).
|
| 271 |
+
locals_list.append({t.__name__: t for t in type_params})
|
| 272 |
+
|
| 273 |
+
# TypeAliasType instances don't have a `__dict__` attribute, so the check
|
| 274 |
+
# is necessary:
|
| 275 |
+
if hasattr(typ, '__dict__'):
|
| 276 |
+
locals_list.append(vars(typ))
|
| 277 |
+
|
| 278 |
+
# The `len(self._types_stack) > 1` check above prevents this from being added twice:
|
| 279 |
+
locals_list.append({typ.__name__: typ})
|
| 280 |
+
|
| 281 |
+
return NamespacesTuple(globalns, LazyLocalNamespace(*locals_list))
|
| 282 |
+
|
| 283 |
+
@contextmanager
|
| 284 |
+
def push(self, typ: type[Any] | TypeAliasType, /) -> Generator[None]:
|
| 285 |
+
"""Push a type to the stack."""
|
| 286 |
+
self._types_stack.append(typ)
|
| 287 |
+
# Reset the cached property:
|
| 288 |
+
self.__dict__.pop('types_namespace', None)
|
| 289 |
+
try:
|
| 290 |
+
yield
|
| 291 |
+
finally:
|
| 292 |
+
self._types_stack.pop()
|
| 293 |
+
self.__dict__.pop('types_namespace', None)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tools to provide pretty/human-readable display of objects."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import types
|
| 6 |
+
from collections.abc import Callable, Collection, Generator, Iterable
|
| 7 |
+
from typing import TYPE_CHECKING, Any, ForwardRef, cast
|
| 8 |
+
|
| 9 |
+
import typing_extensions
|
| 10 |
+
from typing_extensions import TypeAlias
|
| 11 |
+
from typing_inspection import typing_objects
|
| 12 |
+
from typing_inspection.introspection import is_union_origin
|
| 13 |
+
|
| 14 |
+
from . import _typing_extra
|
| 15 |
+
|
| 16 |
+
if TYPE_CHECKING:
|
| 17 |
+
# TODO remove type error comments when we drop support for Python 3.9
|
| 18 |
+
ReprArgs: TypeAlias = Iterable[tuple[str | None, Any]] # pyright: ignore[reportGeneralTypeIssues]
|
| 19 |
+
RichReprResult: TypeAlias = Iterable[Any | tuple[Any] | tuple[str, Any] | tuple[str, Any, Any]] # pyright: ignore[reportGeneralTypeIssues]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class PlainRepr(str):
|
| 23 |
+
"""String class where repr doesn't include quotes. Useful with Representation when you want to return a string
|
| 24 |
+
representation of something that is valid (or pseudo-valid) python.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
def __repr__(self) -> str:
|
| 28 |
+
return str(self)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class Representation:
|
| 32 |
+
# Mixin to provide `__str__`, `__repr__`, and `__pretty__` and `__rich_repr__` methods.
|
| 33 |
+
# `__pretty__` is used by [devtools](https://python-devtools.helpmanual.io/).
|
| 34 |
+
# `__rich_repr__` is used by [rich](https://rich.readthedocs.io/en/stable/pretty.html).
|
| 35 |
+
# (this is not a docstring to avoid adding a docstring to classes which inherit from Representation)
|
| 36 |
+
|
| 37 |
+
__slots__ = ()
|
| 38 |
+
|
| 39 |
+
def __repr_args__(self) -> ReprArgs:
|
| 40 |
+
"""Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden.
|
| 41 |
+
|
| 42 |
+
Can either return:
|
| 43 |
+
* name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]`
|
| 44 |
+
* or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]`
|
| 45 |
+
"""
|
| 46 |
+
attrs_names = cast(Collection[str], self.__slots__)
|
| 47 |
+
if not attrs_names and hasattr(self, '__dict__'):
|
| 48 |
+
attrs_names = self.__dict__.keys()
|
| 49 |
+
attrs = ((s, getattr(self, s)) for s in attrs_names)
|
| 50 |
+
return [(a, v if v is not self else self.__repr_recursion__(v)) for a, v in attrs if v is not None]
|
| 51 |
+
|
| 52 |
+
def __repr_name__(self) -> str:
|
| 53 |
+
"""Name of the instance's class, used in __repr__."""
|
| 54 |
+
return self.__class__.__name__
|
| 55 |
+
|
| 56 |
+
def __repr_recursion__(self, object: Any) -> str:
|
| 57 |
+
"""Returns the string representation of a recursive object."""
|
| 58 |
+
# This is copied over from the stdlib `pprint` module:
|
| 59 |
+
return f'<Recursion on {type(object).__name__} with id={id(object)}>'
|
| 60 |
+
|
| 61 |
+
def __repr_str__(self, join_str: str) -> str:
|
| 62 |
+
return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
|
| 63 |
+
|
| 64 |
+
def __pretty__(self, fmt: Callable[[Any], Any], **kwargs: Any) -> Generator[Any]:
|
| 65 |
+
"""Used by devtools (https://python-devtools.helpmanual.io/) to pretty print objects."""
|
| 66 |
+
yield self.__repr_name__() + '('
|
| 67 |
+
yield 1
|
| 68 |
+
for name, value in self.__repr_args__():
|
| 69 |
+
if name is not None:
|
| 70 |
+
yield name + '='
|
| 71 |
+
yield fmt(value)
|
| 72 |
+
yield ','
|
| 73 |
+
yield 0
|
| 74 |
+
yield -1
|
| 75 |
+
yield ')'
|
| 76 |
+
|
| 77 |
+
def __rich_repr__(self) -> RichReprResult:
|
| 78 |
+
"""Used by Rich (https://rich.readthedocs.io/en/stable/pretty.html) to pretty print objects."""
|
| 79 |
+
for name, field_repr in self.__repr_args__():
|
| 80 |
+
if name is None:
|
| 81 |
+
yield field_repr
|
| 82 |
+
else:
|
| 83 |
+
yield name, field_repr
|
| 84 |
+
|
| 85 |
+
def __str__(self) -> str:
|
| 86 |
+
return self.__repr_str__(' ')
|
| 87 |
+
|
| 88 |
+
def __repr__(self) -> str:
|
| 89 |
+
return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def display_as_type(obj: Any) -> str:
|
| 93 |
+
"""Pretty representation of a type, should be as close as possible to the original type definition string.
|
| 94 |
+
|
| 95 |
+
Takes some logic from `typing._type_repr`.
|
| 96 |
+
"""
|
| 97 |
+
if isinstance(obj, (types.FunctionType, types.BuiltinFunctionType)):
|
| 98 |
+
return obj.__name__
|
| 99 |
+
elif obj is ...:
|
| 100 |
+
return '...'
|
| 101 |
+
elif isinstance(obj, Representation):
|
| 102 |
+
return repr(obj)
|
| 103 |
+
elif isinstance(obj, ForwardRef) or typing_objects.is_typealiastype(obj):
|
| 104 |
+
return str(obj)
|
| 105 |
+
|
| 106 |
+
if not isinstance(obj, (_typing_extra.typing_base, _typing_extra.WithArgsTypes, type)):
|
| 107 |
+
obj = obj.__class__
|
| 108 |
+
|
| 109 |
+
if is_union_origin(typing_extensions.get_origin(obj)):
|
| 110 |
+
args = ', '.join(map(display_as_type, typing_extensions.get_args(obj)))
|
| 111 |
+
return f'Union[{args}]'
|
| 112 |
+
elif isinstance(obj, _typing_extra.WithArgsTypes):
|
| 113 |
+
if typing_objects.is_literal(typing_extensions.get_origin(obj)):
|
| 114 |
+
args = ', '.join(map(repr, typing_extensions.get_args(obj)))
|
| 115 |
+
else:
|
| 116 |
+
args = ', '.join(map(display_as_type, typing_extensions.get_args(obj)))
|
| 117 |
+
try:
|
| 118 |
+
return f'{obj.__qualname__}[{args}]'
|
| 119 |
+
except AttributeError:
|
| 120 |
+
return str(obj).replace('typing.', '').replace('typing_extensions.', '') # handles TypeAliasType in 3.12
|
| 121 |
+
elif isinstance(obj, type):
|
| 122 |
+
return obj.__qualname__
|
| 123 |
+
else:
|
| 124 |
+
return repr(obj).replace('typing.', '').replace('typing_extensions.', '')
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# pyright: reportTypedDictNotRequiredAccess=false, reportGeneralTypeIssues=false, reportArgumentType=false, reportAttributeAccessIssue=false
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
from dataclasses import dataclass, field
|
| 5 |
+
from typing import TypedDict
|
| 6 |
+
|
| 7 |
+
from pydantic_core.core_schema import (
|
| 8 |
+
ComputedField,
|
| 9 |
+
CoreSchema,
|
| 10 |
+
DefinitionReferenceSchema,
|
| 11 |
+
SerSchema,
|
| 12 |
+
iter_union_choices,
|
| 13 |
+
)
|
| 14 |
+
from typing_extensions import TypeAlias
|
| 15 |
+
|
| 16 |
+
AllSchemas: TypeAlias = 'CoreSchema | SerSchema | ComputedField'
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class GatherResult(TypedDict):
|
| 20 |
+
"""Schema traversing result."""
|
| 21 |
+
|
| 22 |
+
collected_references: dict[str, DefinitionReferenceSchema | None]
|
| 23 |
+
"""The collected definition references.
|
| 24 |
+
|
| 25 |
+
If a definition reference schema can be inlined, it means that there is
|
| 26 |
+
only one in the whole core schema. As such, it is stored as the value.
|
| 27 |
+
Otherwise, the value is set to `None`.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
deferred_discriminator_schemas: list[CoreSchema]
|
| 31 |
+
"""The list of core schemas having the discriminator application deferred."""
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class MissingDefinitionError(LookupError):
|
| 35 |
+
"""A reference was pointing to a non-existing core schema."""
|
| 36 |
+
|
| 37 |
+
def __init__(self, schema_reference: str, /) -> None:
|
| 38 |
+
self.schema_reference = schema_reference
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
@dataclass
|
| 42 |
+
class GatherContext:
|
| 43 |
+
"""The current context used during core schema traversing.
|
| 44 |
+
|
| 45 |
+
Context instances should only be used during schema traversing.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
definitions: dict[str, CoreSchema]
|
| 49 |
+
"""The available definitions."""
|
| 50 |
+
|
| 51 |
+
deferred_discriminator_schemas: list[CoreSchema] = field(init=False, default_factory=list)
|
| 52 |
+
"""The list of core schemas having the discriminator application deferred.
|
| 53 |
+
|
| 54 |
+
Internally, these core schemas have a specific key set in the core metadata dict.
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
collected_references: dict[str, DefinitionReferenceSchema | None] = field(init=False, default_factory=dict)
|
| 58 |
+
"""The collected definition references.
|
| 59 |
+
|
| 60 |
+
If a definition reference schema can be inlined, it means that there is
|
| 61 |
+
only one in the whole core schema. As such, it is stored as the value.
|
| 62 |
+
Otherwise, the value is set to `None`.
|
| 63 |
+
|
| 64 |
+
During schema traversing, definition reference schemas can be added as candidates, or removed
|
| 65 |
+
(by setting the value to `None`).
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def traverse_metadata(schema: AllSchemas, ctx: GatherContext) -> None:
|
| 70 |
+
meta = schema.get('metadata')
|
| 71 |
+
if meta is not None and 'pydantic_internal_union_discriminator' in meta:
|
| 72 |
+
ctx.deferred_discriminator_schemas.append(schema) # pyright: ignore[reportArgumentType]
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def traverse_definition_ref(def_ref_schema: DefinitionReferenceSchema, ctx: GatherContext) -> None:
|
| 76 |
+
schema_ref = def_ref_schema['schema_ref']
|
| 77 |
+
|
| 78 |
+
if schema_ref not in ctx.collected_references:
|
| 79 |
+
definition = ctx.definitions.get(schema_ref)
|
| 80 |
+
if definition is None:
|
| 81 |
+
raise MissingDefinitionError(schema_ref)
|
| 82 |
+
|
| 83 |
+
# The `'definition-ref'` schema was only encountered once, make it
|
| 84 |
+
# a candidate to be inlined:
|
| 85 |
+
ctx.collected_references[schema_ref] = def_ref_schema
|
| 86 |
+
traverse_schema(definition, ctx)
|
| 87 |
+
if 'serialization' in def_ref_schema:
|
| 88 |
+
traverse_schema(def_ref_schema['serialization'], ctx)
|
| 89 |
+
traverse_metadata(def_ref_schema, ctx)
|
| 90 |
+
else:
|
| 91 |
+
# The `'definition-ref'` schema was already encountered, meaning
|
| 92 |
+
# the previously encountered schema (and this one) can't be inlined:
|
| 93 |
+
ctx.collected_references[schema_ref] = None
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def traverse_schema(schema: AllSchemas, context: GatherContext) -> None:
|
| 97 |
+
# TODO When we drop 3.9, use a match statement to get better type checking and remove
|
| 98 |
+
# file-level type ignore.
|
| 99 |
+
# (the `'type'` could also be fetched in every `if/elif` statement, but this alters performance).
|
| 100 |
+
schema_type = schema['type']
|
| 101 |
+
|
| 102 |
+
if schema_type == 'definition-ref':
|
| 103 |
+
traverse_definition_ref(schema, context)
|
| 104 |
+
# `traverse_definition_ref` handles the possible serialization and metadata schemas:
|
| 105 |
+
return
|
| 106 |
+
elif schema_type == 'definitions':
|
| 107 |
+
traverse_schema(schema['schema'], context)
|
| 108 |
+
for definition in schema['definitions']:
|
| 109 |
+
traverse_schema(definition, context)
|
| 110 |
+
elif schema_type in {'list', 'set', 'frozenset', 'generator'}:
|
| 111 |
+
if 'items_schema' in schema:
|
| 112 |
+
traverse_schema(schema['items_schema'], context)
|
| 113 |
+
elif schema_type == 'tuple':
|
| 114 |
+
if 'items_schema' in schema:
|
| 115 |
+
for s in schema['items_schema']:
|
| 116 |
+
traverse_schema(s, context)
|
| 117 |
+
elif schema_type == 'dict':
|
| 118 |
+
if 'keys_schema' in schema:
|
| 119 |
+
traverse_schema(schema['keys_schema'], context)
|
| 120 |
+
if 'values_schema' in schema:
|
| 121 |
+
traverse_schema(schema['values_schema'], context)
|
| 122 |
+
elif schema_type == 'union':
|
| 123 |
+
for choice in iter_union_choices(schema):
|
| 124 |
+
traverse_schema(choice, context)
|
| 125 |
+
elif schema_type == 'tagged-union':
|
| 126 |
+
for v in schema['choices'].values():
|
| 127 |
+
traverse_schema(v, context)
|
| 128 |
+
elif schema_type == 'chain':
|
| 129 |
+
for step in schema['steps']:
|
| 130 |
+
traverse_schema(step, context)
|
| 131 |
+
elif schema_type == 'lax-or-strict':
|
| 132 |
+
traverse_schema(schema['lax_schema'], context)
|
| 133 |
+
traverse_schema(schema['strict_schema'], context)
|
| 134 |
+
elif schema_type == 'json-or-python':
|
| 135 |
+
traverse_schema(schema['json_schema'], context)
|
| 136 |
+
traverse_schema(schema['python_schema'], context)
|
| 137 |
+
elif schema_type in {'model-fields', 'typed-dict'}:
|
| 138 |
+
if 'extras_schema' in schema:
|
| 139 |
+
traverse_schema(schema['extras_schema'], context)
|
| 140 |
+
if 'computed_fields' in schema:
|
| 141 |
+
for s in schema['computed_fields']:
|
| 142 |
+
traverse_schema(s, context)
|
| 143 |
+
for s in schema['fields'].values():
|
| 144 |
+
traverse_schema(s, context)
|
| 145 |
+
elif schema_type == 'dataclass-args':
|
| 146 |
+
if 'computed_fields' in schema:
|
| 147 |
+
for s in schema['computed_fields']:
|
| 148 |
+
traverse_schema(s, context)
|
| 149 |
+
for s in schema['fields']:
|
| 150 |
+
traverse_schema(s, context)
|
| 151 |
+
elif schema_type == 'arguments':
|
| 152 |
+
for s in schema['arguments_schema']:
|
| 153 |
+
traverse_schema(s['schema'], context)
|
| 154 |
+
if 'var_args_schema' in schema:
|
| 155 |
+
traverse_schema(schema['var_args_schema'], context)
|
| 156 |
+
if 'var_kwargs_schema' in schema:
|
| 157 |
+
traverse_schema(schema['var_kwargs_schema'], context)
|
| 158 |
+
elif schema_type == 'arguments-v3':
|
| 159 |
+
for s in schema['arguments_schema']:
|
| 160 |
+
traverse_schema(s['schema'], context)
|
| 161 |
+
elif schema_type == 'call':
|
| 162 |
+
traverse_schema(schema['arguments_schema'], context)
|
| 163 |
+
if 'return_schema' in schema:
|
| 164 |
+
traverse_schema(schema['return_schema'], context)
|
| 165 |
+
elif schema_type == 'computed-field':
|
| 166 |
+
traverse_schema(schema['return_schema'], context)
|
| 167 |
+
elif schema_type == 'function-before':
|
| 168 |
+
if 'schema' in schema:
|
| 169 |
+
traverse_schema(schema['schema'], context)
|
| 170 |
+
if 'json_schema_input_schema' in schema:
|
| 171 |
+
traverse_schema(schema['json_schema_input_schema'], context)
|
| 172 |
+
elif schema_type == 'function-plain':
|
| 173 |
+
# TODO duplicate schema types for serializers and validators, needs to be deduplicated.
|
| 174 |
+
if 'return_schema' in schema:
|
| 175 |
+
traverse_schema(schema['return_schema'], context)
|
| 176 |
+
if 'json_schema_input_schema' in schema:
|
| 177 |
+
traverse_schema(schema['json_schema_input_schema'], context)
|
| 178 |
+
elif schema_type == 'function-wrap':
|
| 179 |
+
# TODO duplicate schema types for serializers and validators, needs to be deduplicated.
|
| 180 |
+
if 'return_schema' in schema:
|
| 181 |
+
traverse_schema(schema['return_schema'], context)
|
| 182 |
+
if 'schema' in schema:
|
| 183 |
+
traverse_schema(schema['schema'], context)
|
| 184 |
+
if 'json_schema_input_schema' in schema:
|
| 185 |
+
traverse_schema(schema['json_schema_input_schema'], context)
|
| 186 |
+
else:
|
| 187 |
+
if 'schema' in schema:
|
| 188 |
+
traverse_schema(schema['schema'], context)
|
| 189 |
+
|
| 190 |
+
if 'serialization' in schema:
|
| 191 |
+
traverse_schema(schema['serialization'], context)
|
| 192 |
+
traverse_metadata(schema, context)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def gather_schemas_for_cleaning(schema: CoreSchema, definitions: dict[str, CoreSchema]) -> GatherResult:
|
| 196 |
+
"""Traverse the core schema and definitions and return the necessary information for schema cleaning.
|
| 197 |
+
|
| 198 |
+
During the core schema traversing, any `'definition-ref'` schema is:
|
| 199 |
+
|
| 200 |
+
- Validated: the reference must point to an existing definition. If this is not the case, a
|
| 201 |
+
`MissingDefinitionError` exception is raised.
|
| 202 |
+
- Stored in the context: the actual reference is stored in the context. Depending on whether
|
| 203 |
+
the `'definition-ref'` schema is encountered more that once, the schema itself is also
|
| 204 |
+
saved in the context to be inlined (i.e. replaced by the definition it points to).
|
| 205 |
+
"""
|
| 206 |
+
context = GatherContext(definitions)
|
| 207 |
+
traverse_schema(schema, context)
|
| 208 |
+
|
| 209 |
+
return {
|
| 210 |
+
'collected_references': context.collected_references,
|
| 211 |
+
'deferred_discriminator_schemas': context.deferred_discriminator_schemas,
|
| 212 |
+
}
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Types and utility functions used by various other internal tools."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING, Any, Callable, Literal
|
| 6 |
+
|
| 7 |
+
from pydantic_core import core_schema
|
| 8 |
+
|
| 9 |
+
from ..annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
|
| 10 |
+
|
| 11 |
+
if TYPE_CHECKING:
|
| 12 |
+
from ..json_schema import GenerateJsonSchema, JsonSchemaValue
|
| 13 |
+
from ._core_utils import CoreSchemaOrField
|
| 14 |
+
from ._generate_schema import GenerateSchema
|
| 15 |
+
from ._namespace_utils import NamespacesTuple
|
| 16 |
+
|
| 17 |
+
GetJsonSchemaFunction = Callable[[CoreSchemaOrField, GetJsonSchemaHandler], JsonSchemaValue]
|
| 18 |
+
HandlerOverride = Callable[[CoreSchemaOrField], JsonSchemaValue]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class GenerateJsonSchemaHandler(GetJsonSchemaHandler):
|
| 22 |
+
"""JsonSchemaHandler implementation that doesn't do ref unwrapping by default.
|
| 23 |
+
|
| 24 |
+
This is used for any Annotated metadata so that we don't end up with conflicting
|
| 25 |
+
modifications to the definition schema.
|
| 26 |
+
|
| 27 |
+
Used internally by Pydantic, please do not rely on this implementation.
|
| 28 |
+
See `GetJsonSchemaHandler` for the handler API.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
def __init__(self, generate_json_schema: GenerateJsonSchema, handler_override: HandlerOverride | None) -> None:
|
| 32 |
+
self.generate_json_schema = generate_json_schema
|
| 33 |
+
self.handler = handler_override or generate_json_schema.generate_inner
|
| 34 |
+
self.mode = generate_json_schema.mode
|
| 35 |
+
|
| 36 |
+
def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:
|
| 37 |
+
return self.handler(core_schema)
|
| 38 |
+
|
| 39 |
+
def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue) -> JsonSchemaValue:
|
| 40 |
+
"""Resolves `$ref` in the json schema.
|
| 41 |
+
|
| 42 |
+
This returns the input json schema if there is no `$ref` in json schema.
|
| 43 |
+
|
| 44 |
+
Args:
|
| 45 |
+
maybe_ref_json_schema: The input json schema that may contains `$ref`.
|
| 46 |
+
|
| 47 |
+
Returns:
|
| 48 |
+
Resolved json schema.
|
| 49 |
+
|
| 50 |
+
Raises:
|
| 51 |
+
LookupError: If it can't find the definition for `$ref`.
|
| 52 |
+
"""
|
| 53 |
+
if '$ref' not in maybe_ref_json_schema:
|
| 54 |
+
return maybe_ref_json_schema
|
| 55 |
+
ref = maybe_ref_json_schema['$ref']
|
| 56 |
+
json_schema = self.generate_json_schema.get_schema_from_definitions(ref)
|
| 57 |
+
if json_schema is None:
|
| 58 |
+
raise LookupError(
|
| 59 |
+
f'Could not find a ref for {ref}.'
|
| 60 |
+
' Maybe you tried to call resolve_ref_schema from within a recursive model?'
|
| 61 |
+
)
|
| 62 |
+
return json_schema
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class CallbackGetCoreSchemaHandler(GetCoreSchemaHandler):
|
| 66 |
+
"""Wrapper to use an arbitrary function as a `GetCoreSchemaHandler`.
|
| 67 |
+
|
| 68 |
+
Used internally by Pydantic, please do not rely on this implementation.
|
| 69 |
+
See `GetCoreSchemaHandler` for the handler API.
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
def __init__(
|
| 73 |
+
self,
|
| 74 |
+
handler: Callable[[Any], core_schema.CoreSchema],
|
| 75 |
+
generate_schema: GenerateSchema,
|
| 76 |
+
ref_mode: Literal['to-def', 'unpack'] = 'to-def',
|
| 77 |
+
) -> None:
|
| 78 |
+
self._handler = handler
|
| 79 |
+
self._generate_schema = generate_schema
|
| 80 |
+
self._ref_mode = ref_mode
|
| 81 |
+
|
| 82 |
+
def __call__(self, source_type: Any, /) -> core_schema.CoreSchema:
|
| 83 |
+
schema = self._handler(source_type)
|
| 84 |
+
if self._ref_mode == 'to-def':
|
| 85 |
+
ref = schema.get('ref')
|
| 86 |
+
if ref is not None:
|
| 87 |
+
return self._generate_schema.defs.create_definition_reference_schema(schema)
|
| 88 |
+
return schema
|
| 89 |
+
else: # ref_mode = 'unpack'
|
| 90 |
+
return self.resolve_ref_schema(schema)
|
| 91 |
+
|
| 92 |
+
def _get_types_namespace(self) -> NamespacesTuple:
|
| 93 |
+
return self._generate_schema._types_namespace
|
| 94 |
+
|
| 95 |
+
def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema:
|
| 96 |
+
return self._generate_schema.generate_schema(source_type)
|
| 97 |
+
|
| 98 |
+
@property
|
| 99 |
+
def field_name(self) -> str | None:
|
| 100 |
+
return self._generate_schema.field_name_stack.get()
|
| 101 |
+
|
| 102 |
+
def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema) -> core_schema.CoreSchema:
|
| 103 |
+
"""Resolves reference in the core schema.
|
| 104 |
+
|
| 105 |
+
Args:
|
| 106 |
+
maybe_ref_schema: The input core schema that may contains reference.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
Resolved core schema.
|
| 110 |
+
|
| 111 |
+
Raises:
|
| 112 |
+
LookupError: If it can't find the definition for reference.
|
| 113 |
+
"""
|
| 114 |
+
if maybe_ref_schema['type'] == 'definition-ref':
|
| 115 |
+
ref = maybe_ref_schema['schema_ref']
|
| 116 |
+
definition = self._generate_schema.defs.get_schema_from_ref(ref)
|
| 117 |
+
if definition is None:
|
| 118 |
+
raise LookupError(
|
| 119 |
+
f'Could not find a ref for {ref}.'
|
| 120 |
+
' Maybe you tried to call resolve_ref_schema from within a recursive model?'
|
| 121 |
+
)
|
| 122 |
+
return definition
|
| 123 |
+
elif maybe_ref_schema['type'] == 'definitions':
|
| 124 |
+
return self.resolve_ref_schema(maybe_ref_schema['schema'])
|
| 125 |
+
return maybe_ref_schema
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections
|
| 4 |
+
import collections.abc
|
| 5 |
+
import typing
|
| 6 |
+
from typing import Any
|
| 7 |
+
|
| 8 |
+
from pydantic_core import PydanticOmit, core_schema
|
| 9 |
+
|
| 10 |
+
SEQUENCE_ORIGIN_MAP: dict[Any, Any] = {
|
| 11 |
+
typing.Deque: collections.deque, # noqa: UP006
|
| 12 |
+
collections.deque: collections.deque,
|
| 13 |
+
list: list,
|
| 14 |
+
typing.List: list, # noqa: UP006
|
| 15 |
+
tuple: tuple,
|
| 16 |
+
typing.Tuple: tuple, # noqa: UP006
|
| 17 |
+
set: set,
|
| 18 |
+
typing.AbstractSet: set,
|
| 19 |
+
typing.Set: set, # noqa: UP006
|
| 20 |
+
frozenset: frozenset,
|
| 21 |
+
typing.FrozenSet: frozenset, # noqa: UP006
|
| 22 |
+
typing.Sequence: list,
|
| 23 |
+
typing.MutableSequence: list,
|
| 24 |
+
typing.MutableSet: set,
|
| 25 |
+
# this doesn't handle subclasses of these
|
| 26 |
+
# parametrized typing.Set creates one of these
|
| 27 |
+
collections.abc.MutableSet: set,
|
| 28 |
+
collections.abc.Set: frozenset,
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def serialize_sequence_via_list(
|
| 33 |
+
v: Any, handler: core_schema.SerializerFunctionWrapHandler, info: core_schema.SerializationInfo
|
| 34 |
+
) -> Any:
|
| 35 |
+
items: list[Any] = []
|
| 36 |
+
|
| 37 |
+
mapped_origin = SEQUENCE_ORIGIN_MAP.get(type(v), None)
|
| 38 |
+
if mapped_origin is None:
|
| 39 |
+
# we shouldn't hit this branch, should probably add a serialization error or something
|
| 40 |
+
return v
|
| 41 |
+
|
| 42 |
+
for index, item in enumerate(v):
|
| 43 |
+
try:
|
| 44 |
+
v = handler(item, index)
|
| 45 |
+
except PydanticOmit: # noqa: PERF203
|
| 46 |
+
pass
|
| 47 |
+
else:
|
| 48 |
+
items.append(v)
|
| 49 |
+
|
| 50 |
+
if info.mode_is_json():
|
| 51 |
+
return items
|
| 52 |
+
else:
|
| 53 |
+
return mapped_origin(items)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import dataclasses
|
| 4 |
+
from inspect import Parameter, Signature
|
| 5 |
+
from typing import TYPE_CHECKING, Any, Callable
|
| 6 |
+
|
| 7 |
+
from pydantic_core import PydanticUndefined
|
| 8 |
+
|
| 9 |
+
from ._typing_extra import signature_no_eval
|
| 10 |
+
from ._utils import is_valid_identifier
|
| 11 |
+
|
| 12 |
+
if TYPE_CHECKING:
|
| 13 |
+
from ..config import ExtraValues
|
| 14 |
+
from ..fields import FieldInfo
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# Copied over from stdlib dataclasses
|
| 18 |
+
class _HAS_DEFAULT_FACTORY_CLASS:
|
| 19 |
+
def __repr__(self):
|
| 20 |
+
return '<factory>'
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
_HAS_DEFAULT_FACTORY = _HAS_DEFAULT_FACTORY_CLASS()
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _field_name_for_signature(field_name: str, field_info: FieldInfo) -> str:
|
| 27 |
+
"""Extract the correct name to use for the field when generating a signature.
|
| 28 |
+
|
| 29 |
+
Assuming the field has a valid alias, this will return the alias. Otherwise, it will return the field name.
|
| 30 |
+
First priority is given to the alias, then the validation_alias, then the field name.
|
| 31 |
+
|
| 32 |
+
Args:
|
| 33 |
+
field_name: The name of the field
|
| 34 |
+
field_info: The corresponding FieldInfo object.
|
| 35 |
+
|
| 36 |
+
Returns:
|
| 37 |
+
The correct name to use when generating a signature.
|
| 38 |
+
"""
|
| 39 |
+
if isinstance(field_info.alias, str) and is_valid_identifier(field_info.alias):
|
| 40 |
+
return field_info.alias
|
| 41 |
+
if isinstance(field_info.validation_alias, str) and is_valid_identifier(field_info.validation_alias):
|
| 42 |
+
return field_info.validation_alias
|
| 43 |
+
|
| 44 |
+
return field_name
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _process_param_defaults(param: Parameter) -> Parameter:
|
| 48 |
+
"""Modify the signature for a parameter in a dataclass where the default value is a FieldInfo instance.
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
param (Parameter): The parameter
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
Parameter: The custom processed parameter
|
| 55 |
+
"""
|
| 56 |
+
from ..fields import FieldInfo
|
| 57 |
+
|
| 58 |
+
param_default = param.default
|
| 59 |
+
if isinstance(param_default, FieldInfo):
|
| 60 |
+
annotation = param.annotation
|
| 61 |
+
# Replace the annotation if appropriate
|
| 62 |
+
# inspect does "clever" things to show annotations as strings because we have
|
| 63 |
+
# `from __future__ import annotations` in main, we don't want that
|
| 64 |
+
if annotation == 'Any':
|
| 65 |
+
annotation = Any
|
| 66 |
+
|
| 67 |
+
# Replace the field default
|
| 68 |
+
default = param_default.default
|
| 69 |
+
if default is PydanticUndefined:
|
| 70 |
+
if param_default.default_factory is None:
|
| 71 |
+
default = Signature.empty
|
| 72 |
+
else:
|
| 73 |
+
# this is used by dataclasses to indicate a factory exists:
|
| 74 |
+
default = dataclasses._HAS_DEFAULT_FACTORY # type: ignore
|
| 75 |
+
return param.replace(
|
| 76 |
+
annotation=annotation, name=_field_name_for_signature(param.name, param_default), default=default
|
| 77 |
+
)
|
| 78 |
+
return param
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _generate_signature_parameters( # noqa: C901 (ignore complexity, could use a refactor)
|
| 82 |
+
init: Callable[..., None],
|
| 83 |
+
fields: dict[str, FieldInfo],
|
| 84 |
+
validate_by_name: bool,
|
| 85 |
+
extra: ExtraValues | None,
|
| 86 |
+
) -> dict[str, Parameter]:
|
| 87 |
+
"""Generate a mapping of parameter names to Parameter objects for a pydantic BaseModel or dataclass."""
|
| 88 |
+
from itertools import islice
|
| 89 |
+
|
| 90 |
+
present_params = signature_no_eval(init).parameters.values()
|
| 91 |
+
merged_params: dict[str, Parameter] = {}
|
| 92 |
+
var_kw = None
|
| 93 |
+
use_var_kw = False
|
| 94 |
+
|
| 95 |
+
for param in islice(present_params, 1, None): # skip self arg
|
| 96 |
+
# inspect does "clever" things to show annotations as strings because we have
|
| 97 |
+
# `from __future__ import annotations` in main, we don't want that
|
| 98 |
+
if fields.get(param.name):
|
| 99 |
+
# exclude params with init=False
|
| 100 |
+
if getattr(fields[param.name], 'init', True) is False:
|
| 101 |
+
continue
|
| 102 |
+
param = param.replace(name=_field_name_for_signature(param.name, fields[param.name]))
|
| 103 |
+
if param.annotation == 'Any':
|
| 104 |
+
param = param.replace(annotation=Any)
|
| 105 |
+
if param.kind is param.VAR_KEYWORD:
|
| 106 |
+
var_kw = param
|
| 107 |
+
continue
|
| 108 |
+
merged_params[param.name] = param
|
| 109 |
+
|
| 110 |
+
if var_kw: # if custom init has no var_kw, fields which are not declared in it cannot be passed through
|
| 111 |
+
allow_names = validate_by_name
|
| 112 |
+
for field_name, field in fields.items():
|
| 113 |
+
# when alias is a str it should be used for signature generation
|
| 114 |
+
param_name = _field_name_for_signature(field_name, field)
|
| 115 |
+
|
| 116 |
+
if field_name in merged_params or param_name in merged_params:
|
| 117 |
+
continue
|
| 118 |
+
|
| 119 |
+
if not is_valid_identifier(param_name):
|
| 120 |
+
if allow_names:
|
| 121 |
+
param_name = field_name
|
| 122 |
+
else:
|
| 123 |
+
use_var_kw = True
|
| 124 |
+
continue
|
| 125 |
+
|
| 126 |
+
if field.is_required():
|
| 127 |
+
default = Parameter.empty
|
| 128 |
+
elif field.default_factory is not None:
|
| 129 |
+
# Mimics stdlib dataclasses:
|
| 130 |
+
default = _HAS_DEFAULT_FACTORY
|
| 131 |
+
else:
|
| 132 |
+
default = field.default
|
| 133 |
+
merged_params[param_name] = Parameter(
|
| 134 |
+
param_name,
|
| 135 |
+
Parameter.KEYWORD_ONLY,
|
| 136 |
+
annotation=field.rebuild_annotation(),
|
| 137 |
+
default=default,
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
if extra == 'allow':
|
| 141 |
+
use_var_kw = True
|
| 142 |
+
|
| 143 |
+
if var_kw and use_var_kw:
|
| 144 |
+
# Make sure the parameter for extra kwargs
|
| 145 |
+
# does not have the same name as a field
|
| 146 |
+
default_model_signature = [
|
| 147 |
+
('self', Parameter.POSITIONAL_ONLY),
|
| 148 |
+
('data', Parameter.VAR_KEYWORD),
|
| 149 |
+
]
|
| 150 |
+
if [(p.name, p.kind) for p in present_params] == default_model_signature:
|
| 151 |
+
# if this is the standard model signature, use extra_data as the extra args name
|
| 152 |
+
var_kw_name = 'extra_data'
|
| 153 |
+
else:
|
| 154 |
+
# else start from var_kw
|
| 155 |
+
var_kw_name = var_kw.name
|
| 156 |
+
|
| 157 |
+
# generate a name that's definitely unique
|
| 158 |
+
while var_kw_name in fields:
|
| 159 |
+
var_kw_name += '_'
|
| 160 |
+
merged_params[var_kw_name] = var_kw.replace(name=var_kw_name)
|
| 161 |
+
|
| 162 |
+
return merged_params
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def generate_pydantic_signature(
|
| 166 |
+
init: Callable[..., None],
|
| 167 |
+
fields: dict[str, FieldInfo],
|
| 168 |
+
validate_by_name: bool,
|
| 169 |
+
extra: ExtraValues | None,
|
| 170 |
+
is_dataclass: bool = False,
|
| 171 |
+
) -> Signature:
|
| 172 |
+
"""Generate signature for a pydantic BaseModel or dataclass.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
init: The class init.
|
| 176 |
+
fields: The model fields.
|
| 177 |
+
validate_by_name: The `validate_by_name` value of the config.
|
| 178 |
+
extra: The `extra` value of the config.
|
| 179 |
+
is_dataclass: Whether the model is a dataclass.
|
| 180 |
+
|
| 181 |
+
Returns:
|
| 182 |
+
The dataclass/BaseModel subclass signature.
|
| 183 |
+
"""
|
| 184 |
+
merged_params = _generate_signature_parameters(init, fields, validate_by_name, extra)
|
| 185 |
+
|
| 186 |
+
if is_dataclass:
|
| 187 |
+
merged_params = {k: _process_param_defaults(v) for k, v in merged_params.items()}
|
| 188 |
+
|
| 189 |
+
return Signature(parameters=list(merged_params.values()), return_annotation=None)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py
ADDED
|
@@ -0,0 +1,785 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap Python's typing module."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import collections.abc
|
| 6 |
+
import re
|
| 7 |
+
import sys
|
| 8 |
+
import types
|
| 9 |
+
import typing
|
| 10 |
+
from functools import partial
|
| 11 |
+
from inspect import Signature, signature
|
| 12 |
+
from typing import TYPE_CHECKING, Any, Callable, cast
|
| 13 |
+
|
| 14 |
+
import typing_extensions
|
| 15 |
+
from typing_extensions import deprecated, get_args, get_origin
|
| 16 |
+
from typing_inspection import typing_objects
|
| 17 |
+
from typing_inspection.introspection import is_union_origin
|
| 18 |
+
|
| 19 |
+
from pydantic.version import version_short
|
| 20 |
+
|
| 21 |
+
from ._namespace_utils import GlobalsNamespace, MappingNamespace, NsResolver, get_module_ns_of
|
| 22 |
+
|
| 23 |
+
if sys.version_info < (3, 10):
|
| 24 |
+
NoneType = type(None)
|
| 25 |
+
EllipsisType = type(Ellipsis)
|
| 26 |
+
else:
|
| 27 |
+
from types import EllipsisType as EllipsisType
|
| 28 |
+
from types import NoneType as NoneType
|
| 29 |
+
|
| 30 |
+
if sys.version_info >= (3, 14):
|
| 31 |
+
import annotationlib
|
| 32 |
+
|
| 33 |
+
if TYPE_CHECKING:
|
| 34 |
+
from pydantic import BaseModel
|
| 35 |
+
from pydantic.fields import FieldInfo
|
| 36 |
+
|
| 37 |
+
# As per https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types,
|
| 38 |
+
# always check for both `typing` and `typing_extensions` variants of a typing construct.
|
| 39 |
+
# (this is implemented differently than the suggested approach in the `typing_extensions`
|
| 40 |
+
# docs for performance).
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
_t_annotated = typing.Annotated
|
| 44 |
+
_te_annotated = typing_extensions.Annotated
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def is_annotated(tp: Any, /) -> bool:
|
| 48 |
+
"""Return whether the provided argument is a `Annotated` special form.
|
| 49 |
+
|
| 50 |
+
```python {test="skip" lint="skip"}
|
| 51 |
+
is_annotated(Annotated[int, ...])
|
| 52 |
+
#> True
|
| 53 |
+
```
|
| 54 |
+
"""
|
| 55 |
+
origin = get_origin(tp)
|
| 56 |
+
return origin is _t_annotated or origin is _te_annotated
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def annotated_type(tp: Any, /) -> Any | None:
|
| 60 |
+
"""Return the type of the `Annotated` special form, or `None`."""
|
| 61 |
+
return tp.__origin__ if typing_objects.is_annotated(get_origin(tp)) else None
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def unpack_type(tp: Any, /) -> Any | None:
|
| 65 |
+
"""Return the type wrapped by the `Unpack` special form, or `None`."""
|
| 66 |
+
return get_args(tp)[0] if typing_objects.is_unpack(get_origin(tp)) else None
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def is_hashable(tp: Any, /) -> bool:
|
| 70 |
+
"""Return whether the provided argument is the `Hashable` class.
|
| 71 |
+
|
| 72 |
+
```python {test="skip" lint="skip"}
|
| 73 |
+
is_hashable(Hashable)
|
| 74 |
+
#> True
|
| 75 |
+
```
|
| 76 |
+
"""
|
| 77 |
+
# `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
|
| 78 |
+
# hence the second check:
|
| 79 |
+
return tp is collections.abc.Hashable or get_origin(tp) is collections.abc.Hashable
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def is_callable(tp: Any, /) -> bool:
|
| 83 |
+
"""Return whether the provided argument is a `Callable`, parametrized or not.
|
| 84 |
+
|
| 85 |
+
```python {test="skip" lint="skip"}
|
| 86 |
+
is_callable(Callable[[int], str])
|
| 87 |
+
#> True
|
| 88 |
+
is_callable(typing.Callable)
|
| 89 |
+
#> True
|
| 90 |
+
is_callable(collections.abc.Callable)
|
| 91 |
+
#> True
|
| 92 |
+
```
|
| 93 |
+
"""
|
| 94 |
+
# `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
|
| 95 |
+
# hence the second check:
|
| 96 |
+
return tp is collections.abc.Callable or get_origin(tp) is collections.abc.Callable
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
_classvar_re = re.compile(r'((\w+\.)?Annotated\[)?(\w+\.)?ClassVar\[')
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def is_classvar_annotation(tp: Any, /) -> bool:
|
| 103 |
+
"""Return whether the provided argument represents a class variable annotation.
|
| 104 |
+
|
| 105 |
+
Although not explicitly stated by the typing specification, `ClassVar` can be used
|
| 106 |
+
inside `Annotated` and as such, this function checks for this specific scenario.
|
| 107 |
+
|
| 108 |
+
Because this function is used to detect class variables before evaluating forward references
|
| 109 |
+
(or because evaluation failed), we also implement a naive regex match implementation. This is
|
| 110 |
+
required because class variables are inspected before fields are collected, so we try to be
|
| 111 |
+
as accurate as possible.
|
| 112 |
+
"""
|
| 113 |
+
if typing_objects.is_classvar(tp):
|
| 114 |
+
return True
|
| 115 |
+
|
| 116 |
+
origin = get_origin(tp)
|
| 117 |
+
|
| 118 |
+
if typing_objects.is_classvar(origin):
|
| 119 |
+
return True
|
| 120 |
+
|
| 121 |
+
if typing_objects.is_annotated(origin):
|
| 122 |
+
annotated_type = tp.__origin__
|
| 123 |
+
if typing_objects.is_classvar(annotated_type) or typing_objects.is_classvar(get_origin(annotated_type)):
|
| 124 |
+
return True
|
| 125 |
+
|
| 126 |
+
str_ann: str | None = None
|
| 127 |
+
if isinstance(tp, typing.ForwardRef):
|
| 128 |
+
str_ann = tp.__forward_arg__
|
| 129 |
+
if isinstance(tp, str):
|
| 130 |
+
str_ann = tp
|
| 131 |
+
|
| 132 |
+
if str_ann is not None and _classvar_re.match(str_ann):
|
| 133 |
+
# stdlib dataclasses do something similar, although a bit more advanced
|
| 134 |
+
# (see `dataclass._is_type`).
|
| 135 |
+
return True
|
| 136 |
+
|
| 137 |
+
return False
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
_t_final = typing.Final
|
| 141 |
+
_te_final = typing_extensions.Final
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
# TODO implement `is_finalvar_annotation` as Final can be wrapped with other special forms:
|
| 145 |
+
def is_finalvar(tp: Any, /) -> bool:
|
| 146 |
+
"""Return whether the provided argument is a `Final` special form, parametrized or not.
|
| 147 |
+
|
| 148 |
+
```python {test="skip" lint="skip"}
|
| 149 |
+
is_finalvar(Final[int])
|
| 150 |
+
#> True
|
| 151 |
+
is_finalvar(Final)
|
| 152 |
+
#> True
|
| 153 |
+
"""
|
| 154 |
+
# Final is not necessarily parametrized:
|
| 155 |
+
if tp is _t_final or tp is _te_final:
|
| 156 |
+
return True
|
| 157 |
+
origin = get_origin(tp)
|
| 158 |
+
return origin is _t_final or origin is _te_final
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
_NONE_TYPES: tuple[Any, ...] = (None, NoneType, typing.Literal[None], typing_extensions.Literal[None])
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def is_none_type(tp: Any, /) -> bool:
|
| 165 |
+
"""Return whether the argument represents the `None` type as part of an annotation.
|
| 166 |
+
|
| 167 |
+
```python {test="skip" lint="skip"}
|
| 168 |
+
is_none_type(None)
|
| 169 |
+
#> True
|
| 170 |
+
is_none_type(NoneType)
|
| 171 |
+
#> True
|
| 172 |
+
is_none_type(Literal[None])
|
| 173 |
+
#> True
|
| 174 |
+
is_none_type(type[None])
|
| 175 |
+
#> False
|
| 176 |
+
"""
|
| 177 |
+
return tp in _NONE_TYPES
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def is_namedtuple(tp: Any, /) -> bool:
|
| 181 |
+
"""Return whether the provided argument is a named tuple class.
|
| 182 |
+
|
| 183 |
+
The class can be created using `typing.NamedTuple` or `collections.namedtuple`.
|
| 184 |
+
Parametrized generic classes are *not* assumed to be named tuples.
|
| 185 |
+
"""
|
| 186 |
+
from ._utils import lenient_issubclass # circ. import
|
| 187 |
+
|
| 188 |
+
return lenient_issubclass(tp, tuple) and hasattr(tp, '_fields')
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
# TODO In 2.12, delete this export. It is currently defined only to not break
|
| 192 |
+
# pydantic-settings which relies on it:
|
| 193 |
+
origin_is_union = is_union_origin
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
def is_generic_alias(tp: Any, /) -> bool:
|
| 197 |
+
return isinstance(tp, (types.GenericAlias, typing._GenericAlias)) # pyright: ignore[reportAttributeAccessIssue]
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
# TODO: Ideally, we should avoid relying on the private `typing` constructs:
|
| 201 |
+
|
| 202 |
+
if sys.version_info < (3, 10):
|
| 203 |
+
WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # pyright: ignore[reportAttributeAccessIssue]
|
| 204 |
+
else:
|
| 205 |
+
WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias, types.UnionType) # pyright: ignore[reportAttributeAccessIssue]
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
# Similarly, we shouldn't rely on this `_Final` class, which is even more private than `_GenericAlias`:
|
| 209 |
+
typing_base: Any = typing._Final # pyright: ignore[reportAttributeAccessIssue]
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
### Annotation evaluations functions:
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> dict[str, Any] | None:
|
| 216 |
+
"""Fetch the local namespace of the parent frame where this function is called.
|
| 217 |
+
|
| 218 |
+
Using this function is mostly useful to resolve forward annotations pointing to members defined in a local namespace,
|
| 219 |
+
such as assignments inside a function. Using the standard library tools, it is currently not possible to resolve
|
| 220 |
+
such annotations:
|
| 221 |
+
|
| 222 |
+
```python {lint="skip" test="skip"}
|
| 223 |
+
from typing import get_type_hints
|
| 224 |
+
|
| 225 |
+
def func() -> None:
|
| 226 |
+
Alias = int
|
| 227 |
+
|
| 228 |
+
class C:
|
| 229 |
+
a: 'Alias'
|
| 230 |
+
|
| 231 |
+
# Raises a `NameError: 'Alias' is not defined`
|
| 232 |
+
get_type_hints(C)
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
Pydantic uses this function when a Pydantic model is being defined to fetch the parent frame locals. However,
|
| 236 |
+
this only allows us to fetch the parent frame namespace and not other parents (e.g. a model defined in a function,
|
| 237 |
+
itself defined in another function). Inspecting the next outer frames (using `f_back`) is not reliable enough
|
| 238 |
+
(see https://discuss.python.org/t/20659).
|
| 239 |
+
|
| 240 |
+
Because this function is mostly used to better resolve forward annotations, nothing is returned if the parent frame's
|
| 241 |
+
code object is defined at the module level. In this case, the locals of the frame will be the same as the module
|
| 242 |
+
globals where the class is defined (see `_namespace_utils.get_module_ns_of`). However, if you still want to fetch
|
| 243 |
+
the module globals (e.g. when rebuilding a model, where the frame where the rebuild call is performed might contain
|
| 244 |
+
members that you want to use for forward annotations evaluation), you can use the `force` parameter.
|
| 245 |
+
|
| 246 |
+
Args:
|
| 247 |
+
parent_depth: The depth at which to get the frame. Defaults to 2, meaning the parent frame where this function
|
| 248 |
+
is called will be used.
|
| 249 |
+
force: Whether to always return the frame locals, even if the frame's code object is defined at the module level.
|
| 250 |
+
|
| 251 |
+
Returns:
|
| 252 |
+
The locals of the namespace, or `None` if it was skipped as per the described logic.
|
| 253 |
+
"""
|
| 254 |
+
frame = sys._getframe(parent_depth)
|
| 255 |
+
|
| 256 |
+
if frame.f_code.co_name.startswith('<generic parameters of'):
|
| 257 |
+
# As `parent_frame_namespace` is mostly called in `ModelMetaclass.__new__`,
|
| 258 |
+
# the parent frame can be the annotation scope if the PEP 695 generic syntax is used.
|
| 259 |
+
# (see https://docs.python.org/3/reference/executionmodel.html#annotation-scopes,
|
| 260 |
+
# https://docs.python.org/3/reference/compound_stmts.html#generic-classes).
|
| 261 |
+
# In this case, the code name is set to `<generic parameters of MyClass>`,
|
| 262 |
+
# and we need to skip this frame as it is irrelevant.
|
| 263 |
+
frame = cast(types.FrameType, frame.f_back) # guaranteed to not be `None`
|
| 264 |
+
|
| 265 |
+
# note, we don't copy frame.f_locals here (or during the last return call), because we don't expect the namespace to be
|
| 266 |
+
# modified down the line if this becomes a problem, we could implement some sort of frozen mapping structure to enforce this.
|
| 267 |
+
if force:
|
| 268 |
+
return frame.f_locals
|
| 269 |
+
|
| 270 |
+
# If either of the following conditions are true, the class is defined at the top module level.
|
| 271 |
+
# To better understand why we need both of these checks, see
|
| 272 |
+
# https://github.com/pydantic/pydantic/pull/10113#discussion_r1714981531.
|
| 273 |
+
if frame.f_back is None or frame.f_code.co_name == '<module>':
|
| 274 |
+
return None
|
| 275 |
+
|
| 276 |
+
return frame.f_locals
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def _type_convert(arg: Any) -> Any:
|
| 280 |
+
"""Convert `None` to `NoneType` and strings to `ForwardRef` instances.
|
| 281 |
+
|
| 282 |
+
This is a backport of the private `typing._type_convert` function. When
|
| 283 |
+
evaluating a type, `ForwardRef._evaluate` ends up being called, and is
|
| 284 |
+
responsible for making this conversion. However, we still have to apply
|
| 285 |
+
it for the first argument passed to our type evaluation functions, similarly
|
| 286 |
+
to the `typing.get_type_hints` function.
|
| 287 |
+
"""
|
| 288 |
+
if arg is None:
|
| 289 |
+
return NoneType
|
| 290 |
+
if isinstance(arg, str):
|
| 291 |
+
# Like `typing.get_type_hints`, assume the arg can be in any context,
|
| 292 |
+
# hence the proper `is_argument` and `is_class` args:
|
| 293 |
+
return _make_forward_ref(arg, is_argument=False, is_class=True)
|
| 294 |
+
return arg
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
def safe_get_annotations(obj: Any) -> dict[str, Any]:
|
| 298 |
+
"""Get the annotations for the provided object, accounting for potential deferred forward references.
|
| 299 |
+
|
| 300 |
+
Starting with Python 3.14, accessing the `__annotations__` attribute might raise a `NameError` if
|
| 301 |
+
a referenced symbol isn't defined yet. In this case, we return the annotation in the *forward ref*
|
| 302 |
+
format.
|
| 303 |
+
"""
|
| 304 |
+
if sys.version_info >= (3, 14):
|
| 305 |
+
return annotationlib.get_annotations(obj, format=annotationlib.Format.FORWARDREF)
|
| 306 |
+
else:
|
| 307 |
+
# TODO just do getattr(obj, '__annotations__', {}) when dropping support for Python 3.9:
|
| 308 |
+
if isinstance(obj, type):
|
| 309 |
+
return obj.__dict__.get('__annotations__', {})
|
| 310 |
+
else:
|
| 311 |
+
return getattr(obj, '__annotations__', {})
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def get_model_type_hints(
|
| 315 |
+
model_class: type[BaseModel],
|
| 316 |
+
*,
|
| 317 |
+
ns_resolver: NsResolver | None = None,
|
| 318 |
+
) -> dict[str, tuple[Any, bool]]:
|
| 319 |
+
"""Collect annotations from a Pydantic model class, including those from parent classes.
|
| 320 |
+
|
| 321 |
+
Args:
|
| 322 |
+
model_class: The Pydantic model class to inspect.
|
| 323 |
+
ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
|
| 324 |
+
|
| 325 |
+
Returns:
|
| 326 |
+
A dictionary mapping annotation names to a two-tuple: the first element is the evaluated
|
| 327 |
+
type or the original annotation if a `NameError` occurred, the second element is a boolean
|
| 328 |
+
indicating if whether the evaluation succeeded.
|
| 329 |
+
"""
|
| 330 |
+
hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {}
|
| 331 |
+
ns_resolver = ns_resolver or NsResolver()
|
| 332 |
+
|
| 333 |
+
for base in reversed(model_class.__mro__[:-1]):
|
| 334 |
+
# For Python 3.14, we could also use `Format.VALUE` and pass the globals/locals
|
| 335 |
+
# from the ns_resolver, but we want to be able to know which specific field failed
|
| 336 |
+
# to evaluate:
|
| 337 |
+
ann = safe_get_annotations(base)
|
| 338 |
+
|
| 339 |
+
if not ann:
|
| 340 |
+
continue
|
| 341 |
+
|
| 342 |
+
with ns_resolver.push(base):
|
| 343 |
+
base_model_fields: dict[str, FieldInfo] | None = base.__dict__.get('__pydantic_fields__')
|
| 344 |
+
|
| 345 |
+
for name, value in ann.items():
|
| 346 |
+
if name.startswith('_'):
|
| 347 |
+
globalns, localns = ns_resolver.types_namespace
|
| 348 |
+
|
| 349 |
+
# For private attributes, we only need the annotation to detect the `ClassVar` special form.
|
| 350 |
+
# For this reason, we still try to evaluate it, but we also catch any possible exception (on
|
| 351 |
+
# top of the `NameError`s caught in `try_eval_type`) that could happen so that users are free
|
| 352 |
+
# to use any kind of forward annotation for private fields (e.g. circular imports, new typing
|
| 353 |
+
# syntax, etc).
|
| 354 |
+
try:
|
| 355 |
+
hints[name] = try_eval_type(value, globalns, localns)
|
| 356 |
+
except Exception:
|
| 357 |
+
hints[name] = (value, False)
|
| 358 |
+
else:
|
| 359 |
+
if base_model_fields is not None and name in base_model_fields:
|
| 360 |
+
# Avoid unnecessarily evaluating annotations from parent models, as we'll end up
|
| 361 |
+
# copying the `FieldInfo` instance from it anyway if we need to.
|
| 362 |
+
# We use the `annotation` attribute here, but in reality could put anything here,
|
| 363 |
+
# As we are guaranteed to not make use of it:
|
| 364 |
+
hints[name] = (base_model_fields[name].annotation, True)
|
| 365 |
+
else:
|
| 366 |
+
globalns, localns = ns_resolver.types_namespace
|
| 367 |
+
|
| 368 |
+
hints[name] = try_eval_type(value, globalns, localns)
|
| 369 |
+
|
| 370 |
+
return hints
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
def get_cls_type_hints(
|
| 374 |
+
obj: type[Any],
|
| 375 |
+
*,
|
| 376 |
+
ns_resolver: NsResolver | None = None,
|
| 377 |
+
) -> dict[str, Any]:
|
| 378 |
+
"""Collect annotations from a class, including those from parent classes.
|
| 379 |
+
|
| 380 |
+
Args:
|
| 381 |
+
obj: The class to inspect.
|
| 382 |
+
ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
|
| 383 |
+
"""
|
| 384 |
+
hints: dict[str, Any] = {}
|
| 385 |
+
ns_resolver = ns_resolver or NsResolver()
|
| 386 |
+
|
| 387 |
+
for base in reversed(obj.__mro__):
|
| 388 |
+
# For Python 3.14, we could also use `Format.VALUE` and pass the globals/locals
|
| 389 |
+
# from the ns_resolver, but we want to be able to know which specific field failed
|
| 390 |
+
# to evaluate:
|
| 391 |
+
ann = safe_get_annotations(base)
|
| 392 |
+
|
| 393 |
+
if not ann:
|
| 394 |
+
continue
|
| 395 |
+
|
| 396 |
+
with ns_resolver.push(base):
|
| 397 |
+
globalns, localns = ns_resolver.types_namespace
|
| 398 |
+
for name, value in ann.items():
|
| 399 |
+
hints[name] = eval_type(value, globalns, localns)
|
| 400 |
+
return hints
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def try_eval_type(
|
| 404 |
+
value: Any,
|
| 405 |
+
globalns: GlobalsNamespace | None = None,
|
| 406 |
+
localns: MappingNamespace | None = None,
|
| 407 |
+
) -> tuple[Any, bool]:
|
| 408 |
+
"""Try evaluating the annotation using the provided namespaces.
|
| 409 |
+
|
| 410 |
+
Args:
|
| 411 |
+
value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
|
| 412 |
+
of `str`, it will be converted to a `ForwardRef`.
|
| 413 |
+
localns: The global namespace to use during annotation evaluation.
|
| 414 |
+
globalns: The local namespace to use during annotation evaluation.
|
| 415 |
+
|
| 416 |
+
Returns:
|
| 417 |
+
A two-tuple containing the possibly evaluated type and a boolean indicating
|
| 418 |
+
whether the evaluation succeeded or not.
|
| 419 |
+
"""
|
| 420 |
+
value = _type_convert(value)
|
| 421 |
+
|
| 422 |
+
try:
|
| 423 |
+
return eval_type_backport(value, globalns, localns), True
|
| 424 |
+
except NameError:
|
| 425 |
+
return value, False
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
def eval_type(
|
| 429 |
+
value: Any,
|
| 430 |
+
globalns: GlobalsNamespace | None = None,
|
| 431 |
+
localns: MappingNamespace | None = None,
|
| 432 |
+
) -> Any:
|
| 433 |
+
"""Evaluate the annotation using the provided namespaces.
|
| 434 |
+
|
| 435 |
+
Args:
|
| 436 |
+
value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
|
| 437 |
+
of `str`, it will be converted to a `ForwardRef`.
|
| 438 |
+
localns: The global namespace to use during annotation evaluation.
|
| 439 |
+
globalns: The local namespace to use during annotation evaluation.
|
| 440 |
+
"""
|
| 441 |
+
value = _type_convert(value)
|
| 442 |
+
return eval_type_backport(value, globalns, localns)
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
@deprecated(
|
| 446 |
+
'`eval_type_lenient` is deprecated, use `try_eval_type` instead.',
|
| 447 |
+
category=None,
|
| 448 |
+
)
|
| 449 |
+
def eval_type_lenient(
|
| 450 |
+
value: Any,
|
| 451 |
+
globalns: GlobalsNamespace | None = None,
|
| 452 |
+
localns: MappingNamespace | None = None,
|
| 453 |
+
) -> Any:
|
| 454 |
+
ev, _ = try_eval_type(value, globalns, localns)
|
| 455 |
+
return ev
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def eval_type_backport(
|
| 459 |
+
value: Any,
|
| 460 |
+
globalns: GlobalsNamespace | None = None,
|
| 461 |
+
localns: MappingNamespace | None = None,
|
| 462 |
+
type_params: tuple[Any, ...] | None = None,
|
| 463 |
+
) -> Any:
|
| 464 |
+
"""An enhanced version of `typing._eval_type` which will fall back to using the `eval_type_backport`
|
| 465 |
+
package if it's installed to let older Python versions use newer typing constructs.
|
| 466 |
+
|
| 467 |
+
Specifically, this transforms `X | Y` into `typing.Union[X, Y]` and `list[X]` into `typing.List[X]`
|
| 468 |
+
(as well as all the types made generic in PEP 585) if the original syntax is not supported in the
|
| 469 |
+
current Python version.
|
| 470 |
+
|
| 471 |
+
This function will also display a helpful error if the value passed fails to evaluate.
|
| 472 |
+
"""
|
| 473 |
+
try:
|
| 474 |
+
return _eval_type_backport(value, globalns, localns, type_params)
|
| 475 |
+
except TypeError as e:
|
| 476 |
+
if 'Unable to evaluate type annotation' in str(e):
|
| 477 |
+
raise
|
| 478 |
+
|
| 479 |
+
# If it is a `TypeError` and value isn't a `ForwardRef`, it would have failed during annotation definition.
|
| 480 |
+
# Thus we assert here for type checking purposes:
|
| 481 |
+
assert isinstance(value, typing.ForwardRef)
|
| 482 |
+
|
| 483 |
+
message = f'Unable to evaluate type annotation {value.__forward_arg__!r}.'
|
| 484 |
+
if sys.version_info >= (3, 11):
|
| 485 |
+
e.add_note(message)
|
| 486 |
+
raise
|
| 487 |
+
else:
|
| 488 |
+
raise TypeError(message) from e
|
| 489 |
+
except RecursionError as e:
|
| 490 |
+
# TODO ideally recursion errors should be checked in `eval_type` above, but `eval_type_backport`
|
| 491 |
+
# is used directly in some places.
|
| 492 |
+
message = (
|
| 493 |
+
"If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), "
|
| 494 |
+
'consider using PEP 695 type aliases instead. For more details, refer to the documentation: '
|
| 495 |
+
f'https://docs.pydantic.dev/{version_short()}/concepts/types/#named-recursive-types'
|
| 496 |
+
)
|
| 497 |
+
if sys.version_info >= (3, 11):
|
| 498 |
+
e.add_note(message)
|
| 499 |
+
raise
|
| 500 |
+
else:
|
| 501 |
+
raise RecursionError(f'{e.args[0]}\n{message}')
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
def _eval_type_backport(
|
| 505 |
+
value: Any,
|
| 506 |
+
globalns: GlobalsNamespace | None = None,
|
| 507 |
+
localns: MappingNamespace | None = None,
|
| 508 |
+
type_params: tuple[Any, ...] | None = None,
|
| 509 |
+
) -> Any:
|
| 510 |
+
try:
|
| 511 |
+
return _eval_type(value, globalns, localns, type_params)
|
| 512 |
+
except TypeError as e:
|
| 513 |
+
if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)):
|
| 514 |
+
raise
|
| 515 |
+
|
| 516 |
+
try:
|
| 517 |
+
from eval_type_backport import eval_type_backport
|
| 518 |
+
except ImportError:
|
| 519 |
+
raise TypeError(
|
| 520 |
+
f'Unable to evaluate type annotation {value.__forward_arg__!r}. If you are making use '
|
| 521 |
+
'of the new typing syntax (unions using `|` since Python 3.10 or builtins subscripting '
|
| 522 |
+
'since Python 3.9), you should either replace the use of new syntax with the existing '
|
| 523 |
+
'`typing` constructs or install the `eval_type_backport` package.'
|
| 524 |
+
) from e
|
| 525 |
+
|
| 526 |
+
return eval_type_backport(
|
| 527 |
+
value,
|
| 528 |
+
globalns,
|
| 529 |
+
localns, # pyright: ignore[reportArgumentType], waiting on a new `eval_type_backport` release.
|
| 530 |
+
try_default=False,
|
| 531 |
+
)
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
def _eval_type(
|
| 535 |
+
value: Any,
|
| 536 |
+
globalns: GlobalsNamespace | None = None,
|
| 537 |
+
localns: MappingNamespace | None = None,
|
| 538 |
+
type_params: tuple[Any, ...] | None = None,
|
| 539 |
+
) -> Any:
|
| 540 |
+
if sys.version_info >= (3, 14):
|
| 541 |
+
# Starting in 3.14, `_eval_type()` does *not* apply `_type_convert()`
|
| 542 |
+
# anymore. This means the `None` -> `type(None)` conversion does not apply:
|
| 543 |
+
evaluated = typing._eval_type( # type: ignore
|
| 544 |
+
value,
|
| 545 |
+
globalns,
|
| 546 |
+
localns,
|
| 547 |
+
type_params=type_params,
|
| 548 |
+
# This is relevant when evaluating types from `TypedDict` classes, where string annotations
|
| 549 |
+
# are automatically converted to `ForwardRef` instances with a module set. In this case,
|
| 550 |
+
# Our `globalns` is irrelevant and we need to indicate `typing._eval_type()` that it should
|
| 551 |
+
# infer it from the `ForwardRef.__forward_module__` attribute instead (`typing.get_type_hints()`
|
| 552 |
+
# does the same). Note that this would probably be unnecessary if we properly iterated over the
|
| 553 |
+
# `__orig_bases__` for TypedDicts in `get_cls_type_hints()`:
|
| 554 |
+
prefer_fwd_module=True,
|
| 555 |
+
)
|
| 556 |
+
if evaluated is None:
|
| 557 |
+
evaluated = type(None)
|
| 558 |
+
return evaluated
|
| 559 |
+
elif sys.version_info >= (3, 13):
|
| 560 |
+
return typing._eval_type( # type: ignore
|
| 561 |
+
value, globalns, localns, type_params=type_params
|
| 562 |
+
)
|
| 563 |
+
else:
|
| 564 |
+
return typing._eval_type( # type: ignore
|
| 565 |
+
value, globalns, localns
|
| 566 |
+
)
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
def is_backport_fixable_error(e: TypeError) -> bool:
|
| 570 |
+
msg = str(e)
|
| 571 |
+
|
| 572 |
+
return sys.version_info < (3, 10) and msg.startswith('unsupported operand type(s) for |: ')
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
def signature_no_eval(f: Callable[..., Any]) -> Signature:
|
| 576 |
+
"""Get the signature of a callable without evaluating any annotations."""
|
| 577 |
+
if sys.version_info >= (3, 14):
|
| 578 |
+
from annotationlib import Format
|
| 579 |
+
|
| 580 |
+
return signature(f, annotation_format=Format.FORWARDREF)
|
| 581 |
+
else:
|
| 582 |
+
return signature(f)
|
| 583 |
+
|
| 584 |
+
|
| 585 |
+
def get_function_type_hints(
|
| 586 |
+
function: Callable[..., Any],
|
| 587 |
+
*,
|
| 588 |
+
include_keys: set[str] | None = None,
|
| 589 |
+
globalns: GlobalsNamespace | None = None,
|
| 590 |
+
localns: MappingNamespace | None = None,
|
| 591 |
+
) -> dict[str, Any]:
|
| 592 |
+
"""Return type hints for a function.
|
| 593 |
+
|
| 594 |
+
This is similar to the `typing.get_type_hints` function, with a few differences:
|
| 595 |
+
- Support `functools.partial` by using the underlying `func` attribute.
|
| 596 |
+
- Do not wrap type annotation of a parameter with `Optional` if it has a default value of `None`
|
| 597 |
+
(related bug: https://github.com/python/cpython/issues/90353, only fixed in 3.11+).
|
| 598 |
+
"""
|
| 599 |
+
if isinstance(function, partial):
|
| 600 |
+
annotations = safe_get_annotations(function.func)
|
| 601 |
+
else:
|
| 602 |
+
annotations = safe_get_annotations(function)
|
| 603 |
+
|
| 604 |
+
if globalns is None:
|
| 605 |
+
globalns = get_module_ns_of(function)
|
| 606 |
+
type_params: tuple[Any, ...] | None = None
|
| 607 |
+
if localns is None:
|
| 608 |
+
# If localns was specified, it is assumed to already contain type params. This is because
|
| 609 |
+
# Pydantic has more advanced logic to do so (see `_namespace_utils.ns_for_function`).
|
| 610 |
+
type_params = getattr(function, '__type_params__', ())
|
| 611 |
+
|
| 612 |
+
type_hints = {}
|
| 613 |
+
for name, value in annotations.items():
|
| 614 |
+
if include_keys is not None and name not in include_keys:
|
| 615 |
+
continue
|
| 616 |
+
if value is None:
|
| 617 |
+
value = NoneType
|
| 618 |
+
elif isinstance(value, str):
|
| 619 |
+
value = _make_forward_ref(value)
|
| 620 |
+
|
| 621 |
+
type_hints[name] = eval_type_backport(value, globalns, localns, type_params)
|
| 622 |
+
|
| 623 |
+
return type_hints
|
| 624 |
+
|
| 625 |
+
|
| 626 |
+
# TODO use typing.ForwardRef directly when we stop supporting 3.9:
|
| 627 |
+
if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1):
|
| 628 |
+
|
| 629 |
+
def _make_forward_ref(
|
| 630 |
+
arg: Any,
|
| 631 |
+
is_argument: bool = True,
|
| 632 |
+
*,
|
| 633 |
+
is_class: bool = False,
|
| 634 |
+
) -> typing.ForwardRef:
|
| 635 |
+
"""Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions.
|
| 636 |
+
The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below.
|
| 637 |
+
|
| 638 |
+
See https://github.com/python/cpython/pull/28560 for some background.
|
| 639 |
+
The backport happened on 3.9.8, see:
|
| 640 |
+
https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458,
|
| 641 |
+
and on 3.10.1 for the 3.10 branch, see:
|
| 642 |
+
https://github.com/pydantic/pydantic/issues/6912
|
| 643 |
+
|
| 644 |
+
Implemented as EAFP with memory.
|
| 645 |
+
"""
|
| 646 |
+
return typing.ForwardRef(arg, is_argument) # pyright: ignore[reportCallIssue]
|
| 647 |
+
|
| 648 |
+
else:
|
| 649 |
+
_make_forward_ref = typing.ForwardRef # pyright: ignore[reportAssignmentType]
|
| 650 |
+
|
| 651 |
+
|
| 652 |
+
if sys.version_info >= (3, 10):
|
| 653 |
+
get_type_hints = typing.get_type_hints
|
| 654 |
+
|
| 655 |
+
else:
|
| 656 |
+
"""
|
| 657 |
+
For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to
|
| 658 |
+
the implementation in CPython 3.10.8.
|
| 659 |
+
"""
|
| 660 |
+
|
| 661 |
+
@typing.no_type_check
|
| 662 |
+
def get_type_hints( # noqa: C901
|
| 663 |
+
obj: Any,
|
| 664 |
+
globalns: dict[str, Any] | None = None,
|
| 665 |
+
localns: dict[str, Any] | None = None,
|
| 666 |
+
include_extras: bool = False,
|
| 667 |
+
) -> dict[str, Any]: # pragma: no cover
|
| 668 |
+
"""Taken verbatim from python 3.10.8 unchanged, except:
|
| 669 |
+
* type annotations of the function definition above.
|
| 670 |
+
* prefixing `typing.` where appropriate
|
| 671 |
+
* Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument.
|
| 672 |
+
|
| 673 |
+
https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875
|
| 674 |
+
|
| 675 |
+
DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY.
|
| 676 |
+
======================================================
|
| 677 |
+
|
| 678 |
+
Return type hints for an object.
|
| 679 |
+
|
| 680 |
+
This is often the same as obj.__annotations__, but it handles
|
| 681 |
+
forward references encoded as string literals, adds Optional[t] if a
|
| 682 |
+
default value equal to None is set and recursively replaces all
|
| 683 |
+
'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
|
| 684 |
+
|
| 685 |
+
The argument may be a module, class, method, or function. The annotations
|
| 686 |
+
are returned as a dictionary. For classes, annotations include also
|
| 687 |
+
inherited members.
|
| 688 |
+
|
| 689 |
+
TypeError is raised if the argument is not of a type that can contain
|
| 690 |
+
annotations, and an empty dictionary is returned if no annotations are
|
| 691 |
+
present.
|
| 692 |
+
|
| 693 |
+
BEWARE -- the behavior of globalns and localns is counterintuitive
|
| 694 |
+
(unless you are familiar with how eval() and exec() work). The
|
| 695 |
+
search order is locals first, then globals.
|
| 696 |
+
|
| 697 |
+
- If no dict arguments are passed, an attempt is made to use the
|
| 698 |
+
globals from obj (or the respective module's globals for classes),
|
| 699 |
+
and these are also used as the locals. If the object does not appear
|
| 700 |
+
to have globals, an empty dictionary is used. For classes, the search
|
| 701 |
+
order is globals first then locals.
|
| 702 |
+
|
| 703 |
+
- If one dict argument is passed, it is used for both globals and
|
| 704 |
+
locals.
|
| 705 |
+
|
| 706 |
+
- If two dict arguments are passed, they specify globals and
|
| 707 |
+
locals, respectively.
|
| 708 |
+
"""
|
| 709 |
+
if getattr(obj, '__no_type_check__', None):
|
| 710 |
+
return {}
|
| 711 |
+
# Classes require a special treatment.
|
| 712 |
+
if isinstance(obj, type):
|
| 713 |
+
hints = {}
|
| 714 |
+
for base in reversed(obj.__mro__):
|
| 715 |
+
if globalns is None:
|
| 716 |
+
base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {})
|
| 717 |
+
else:
|
| 718 |
+
base_globals = globalns
|
| 719 |
+
ann = base.__dict__.get('__annotations__', {})
|
| 720 |
+
if isinstance(ann, types.GetSetDescriptorType):
|
| 721 |
+
ann = {}
|
| 722 |
+
base_locals = dict(vars(base)) if localns is None else localns
|
| 723 |
+
if localns is None and globalns is None:
|
| 724 |
+
# This is surprising, but required. Before Python 3.10,
|
| 725 |
+
# get_type_hints only evaluated the globalns of
|
| 726 |
+
# a class. To maintain backwards compatibility, we reverse
|
| 727 |
+
# the globalns and localns order so that eval() looks into
|
| 728 |
+
# *base_globals* first rather than *base_locals*.
|
| 729 |
+
# This only affects ForwardRefs.
|
| 730 |
+
base_globals, base_locals = base_locals, base_globals
|
| 731 |
+
for name, value in ann.items():
|
| 732 |
+
if value is None:
|
| 733 |
+
value = type(None)
|
| 734 |
+
if isinstance(value, str):
|
| 735 |
+
value = _make_forward_ref(value, is_argument=False, is_class=True)
|
| 736 |
+
|
| 737 |
+
value = eval_type_backport(value, base_globals, base_locals)
|
| 738 |
+
hints[name] = value
|
| 739 |
+
if not include_extras and hasattr(typing, '_strip_annotations'):
|
| 740 |
+
return {
|
| 741 |
+
k: typing._strip_annotations(t) # type: ignore
|
| 742 |
+
for k, t in hints.items()
|
| 743 |
+
}
|
| 744 |
+
else:
|
| 745 |
+
return hints
|
| 746 |
+
|
| 747 |
+
if globalns is None:
|
| 748 |
+
if isinstance(obj, types.ModuleType):
|
| 749 |
+
globalns = obj.__dict__
|
| 750 |
+
else:
|
| 751 |
+
nsobj = obj
|
| 752 |
+
# Find globalns for the unwrapped object.
|
| 753 |
+
while hasattr(nsobj, '__wrapped__'):
|
| 754 |
+
nsobj = nsobj.__wrapped__
|
| 755 |
+
globalns = getattr(nsobj, '__globals__', {})
|
| 756 |
+
if localns is None:
|
| 757 |
+
localns = globalns
|
| 758 |
+
elif localns is None:
|
| 759 |
+
localns = globalns
|
| 760 |
+
hints = getattr(obj, '__annotations__', None)
|
| 761 |
+
if hints is None:
|
| 762 |
+
# Return empty annotations for something that _could_ have them.
|
| 763 |
+
if isinstance(obj, typing._allowed_types): # type: ignore
|
| 764 |
+
return {}
|
| 765 |
+
else:
|
| 766 |
+
raise TypeError(f'{obj!r} is not a module, class, method, or function.')
|
| 767 |
+
defaults = typing._get_defaults(obj) # type: ignore
|
| 768 |
+
hints = dict(hints)
|
| 769 |
+
for name, value in hints.items():
|
| 770 |
+
if value is None:
|
| 771 |
+
value = type(None)
|
| 772 |
+
if isinstance(value, str):
|
| 773 |
+
# class-level forward refs were handled above, this must be either
|
| 774 |
+
# a module-level annotation or a function argument annotation
|
| 775 |
+
|
| 776 |
+
value = _make_forward_ref(
|
| 777 |
+
value,
|
| 778 |
+
is_argument=not isinstance(obj, types.ModuleType),
|
| 779 |
+
is_class=False,
|
| 780 |
+
)
|
| 781 |
+
value = eval_type_backport(value, globalns, localns)
|
| 782 |
+
if name in defaults and defaults[name] is None:
|
| 783 |
+
value = typing.Optional[value]
|
| 784 |
+
hints[name] = value
|
| 785 |
+
return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Bucket of reusable internal utilities.
|
| 2 |
+
|
| 3 |
+
This should be reduced as much as possible with functions only used in one place, moved to that place.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from __future__ import annotations as _annotations
|
| 7 |
+
|
| 8 |
+
import dataclasses
|
| 9 |
+
import keyword
|
| 10 |
+
import sys
|
| 11 |
+
import warnings
|
| 12 |
+
import weakref
|
| 13 |
+
from collections import OrderedDict, defaultdict, deque
|
| 14 |
+
from collections.abc import Callable, Iterable, Mapping
|
| 15 |
+
from collections.abc import Set as AbstractSet
|
| 16 |
+
from copy import deepcopy
|
| 17 |
+
from functools import cached_property
|
| 18 |
+
from inspect import Parameter
|
| 19 |
+
from itertools import zip_longest
|
| 20 |
+
from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType
|
| 21 |
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
|
| 22 |
+
|
| 23 |
+
from pydantic_core import MISSING, PydanticUndefined
|
| 24 |
+
from typing_extensions import TypeAlias, TypeGuard, deprecated
|
| 25 |
+
|
| 26 |
+
from pydantic import PydanticDeprecatedSince211
|
| 27 |
+
|
| 28 |
+
from . import _repr, _typing_extra
|
| 29 |
+
from ._import_utils import import_cached_base_model
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
# TODO remove type error comments when we drop support for Python 3.9
|
| 33 |
+
MappingIntStrAny: TypeAlias = Mapping[int, Any] | Mapping[str, Any] # pyright: ignore[reportGeneralTypeIssues]
|
| 34 |
+
AbstractSetIntStr: TypeAlias = AbstractSet[int] | AbstractSet[str] # pyright: ignore[reportGeneralTypeIssues]
|
| 35 |
+
from ..main import BaseModel
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# these are types that are returned unchanged by deepcopy
|
| 39 |
+
IMMUTABLE_NON_COLLECTIONS_TYPES: set[type[Any]] = {
|
| 40 |
+
int,
|
| 41 |
+
float,
|
| 42 |
+
complex,
|
| 43 |
+
str,
|
| 44 |
+
bool,
|
| 45 |
+
bytes,
|
| 46 |
+
type,
|
| 47 |
+
_typing_extra.NoneType,
|
| 48 |
+
FunctionType,
|
| 49 |
+
BuiltinFunctionType,
|
| 50 |
+
LambdaType,
|
| 51 |
+
weakref.ref,
|
| 52 |
+
CodeType,
|
| 53 |
+
# note: including ModuleType will differ from behaviour of deepcopy by not producing error.
|
| 54 |
+
# It might be not a good idea in general, but considering that this function used only internally
|
| 55 |
+
# against default values of fields, this will allow to actually have a field with module as default value
|
| 56 |
+
ModuleType,
|
| 57 |
+
NotImplemented.__class__,
|
| 58 |
+
Ellipsis.__class__,
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
# these are types that if empty, might be copied with simple copy() instead of deepcopy()
|
| 62 |
+
BUILTIN_COLLECTIONS: set[type[Any]] = {
|
| 63 |
+
list,
|
| 64 |
+
set,
|
| 65 |
+
tuple,
|
| 66 |
+
frozenset,
|
| 67 |
+
dict,
|
| 68 |
+
OrderedDict,
|
| 69 |
+
defaultdict,
|
| 70 |
+
deque,
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def can_be_positional(param: Parameter) -> bool:
|
| 75 |
+
"""Return whether the parameter accepts a positional argument.
|
| 76 |
+
|
| 77 |
+
```python {test="skip" lint="skip"}
|
| 78 |
+
def func(a, /, b, *, c):
|
| 79 |
+
pass
|
| 80 |
+
|
| 81 |
+
params = inspect.signature(func).parameters
|
| 82 |
+
can_be_positional(params['a'])
|
| 83 |
+
#> True
|
| 84 |
+
can_be_positional(params['b'])
|
| 85 |
+
#> True
|
| 86 |
+
can_be_positional(params['c'])
|
| 87 |
+
#> False
|
| 88 |
+
```
|
| 89 |
+
"""
|
| 90 |
+
return param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def sequence_like(v: Any) -> bool:
|
| 94 |
+
return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque))
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def lenient_isinstance(o: Any, class_or_tuple: type[Any] | tuple[type[Any], ...] | None) -> bool: # pragma: no cover
|
| 98 |
+
try:
|
| 99 |
+
return isinstance(o, class_or_tuple) # type: ignore[arg-type]
|
| 100 |
+
except TypeError:
|
| 101 |
+
return False
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no cover
|
| 105 |
+
try:
|
| 106 |
+
return isinstance(cls, type) and issubclass(cls, class_or_tuple)
|
| 107 |
+
except TypeError:
|
| 108 |
+
if isinstance(cls, _typing_extra.WithArgsTypes):
|
| 109 |
+
return False
|
| 110 |
+
raise # pragma: no cover
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def is_model_class(cls: Any) -> TypeGuard[type[BaseModel]]:
|
| 114 |
+
"""Returns true if cls is a _proper_ subclass of BaseModel, and provides proper type-checking,
|
| 115 |
+
unlike raw calls to lenient_issubclass.
|
| 116 |
+
"""
|
| 117 |
+
BaseModel = import_cached_base_model()
|
| 118 |
+
|
| 119 |
+
return lenient_issubclass(cls, BaseModel) and cls is not BaseModel
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def is_valid_identifier(identifier: str) -> bool:
|
| 123 |
+
"""Checks that a string is a valid identifier and not a Python keyword.
|
| 124 |
+
:param identifier: The identifier to test.
|
| 125 |
+
:return: True if the identifier is valid.
|
| 126 |
+
"""
|
| 127 |
+
return identifier.isidentifier() and not keyword.iskeyword(identifier)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
KeyType = TypeVar('KeyType')
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def deep_update(mapping: dict[KeyType, Any], *updating_mappings: dict[KeyType, Any]) -> dict[KeyType, Any]:
|
| 134 |
+
updated_mapping = mapping.copy()
|
| 135 |
+
for updating_mapping in updating_mappings:
|
| 136 |
+
for k, v in updating_mapping.items():
|
| 137 |
+
if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict):
|
| 138 |
+
updated_mapping[k] = deep_update(updated_mapping[k], v)
|
| 139 |
+
else:
|
| 140 |
+
updated_mapping[k] = v
|
| 141 |
+
return updated_mapping
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def update_not_none(mapping: dict[Any, Any], **update: Any) -> None:
|
| 145 |
+
mapping.update({k: v for k, v in update.items() if v is not None})
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
T = TypeVar('T')
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def unique_list(
|
| 152 |
+
input_list: list[T] | tuple[T, ...],
|
| 153 |
+
*,
|
| 154 |
+
name_factory: Callable[[T], str] = str,
|
| 155 |
+
) -> list[T]:
|
| 156 |
+
"""Make a list unique while maintaining order.
|
| 157 |
+
We update the list if another one with the same name is set
|
| 158 |
+
(e.g. model validator overridden in subclass).
|
| 159 |
+
"""
|
| 160 |
+
result: list[T] = []
|
| 161 |
+
result_names: list[str] = []
|
| 162 |
+
for v in input_list:
|
| 163 |
+
v_name = name_factory(v)
|
| 164 |
+
if v_name not in result_names:
|
| 165 |
+
result_names.append(v_name)
|
| 166 |
+
result.append(v)
|
| 167 |
+
else:
|
| 168 |
+
result[result_names.index(v_name)] = v
|
| 169 |
+
|
| 170 |
+
return result
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class ValueItems(_repr.Representation):
|
| 174 |
+
"""Class for more convenient calculation of excluded or included fields on values."""
|
| 175 |
+
|
| 176 |
+
__slots__ = ('_items', '_type')
|
| 177 |
+
|
| 178 |
+
def __init__(self, value: Any, items: AbstractSetIntStr | MappingIntStrAny) -> None:
|
| 179 |
+
items = self._coerce_items(items)
|
| 180 |
+
|
| 181 |
+
if isinstance(value, (list, tuple)):
|
| 182 |
+
items = self._normalize_indexes(items, len(value)) # type: ignore
|
| 183 |
+
|
| 184 |
+
self._items: MappingIntStrAny = items # type: ignore
|
| 185 |
+
|
| 186 |
+
def is_excluded(self, item: Any) -> bool:
|
| 187 |
+
"""Check if item is fully excluded.
|
| 188 |
+
|
| 189 |
+
:param item: key or index of a value
|
| 190 |
+
"""
|
| 191 |
+
return self.is_true(self._items.get(item))
|
| 192 |
+
|
| 193 |
+
def is_included(self, item: Any) -> bool:
|
| 194 |
+
"""Check if value is contained in self._items.
|
| 195 |
+
|
| 196 |
+
:param item: key or index of value
|
| 197 |
+
"""
|
| 198 |
+
return item in self._items
|
| 199 |
+
|
| 200 |
+
def for_element(self, e: int | str) -> AbstractSetIntStr | MappingIntStrAny | None:
|
| 201 |
+
""":param e: key or index of element on value
|
| 202 |
+
:return: raw values for element if self._items is dict and contain needed element
|
| 203 |
+
"""
|
| 204 |
+
item = self._items.get(e) # type: ignore
|
| 205 |
+
return item if not self.is_true(item) else None
|
| 206 |
+
|
| 207 |
+
def _normalize_indexes(self, items: MappingIntStrAny, v_length: int) -> dict[int | str, Any]:
|
| 208 |
+
""":param items: dict or set of indexes which will be normalized
|
| 209 |
+
:param v_length: length of sequence indexes of which will be
|
| 210 |
+
|
| 211 |
+
>>> self._normalize_indexes({0: True, -2: True, -1: True}, 4)
|
| 212 |
+
{0: True, 2: True, 3: True}
|
| 213 |
+
>>> self._normalize_indexes({'__all__': True}, 4)
|
| 214 |
+
{0: True, 1: True, 2: True, 3: True}
|
| 215 |
+
"""
|
| 216 |
+
normalized_items: dict[int | str, Any] = {}
|
| 217 |
+
all_items = None
|
| 218 |
+
for i, v in items.items():
|
| 219 |
+
if not (isinstance(v, Mapping) or isinstance(v, AbstractSet) or self.is_true(v)):
|
| 220 |
+
raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}')
|
| 221 |
+
if i == '__all__':
|
| 222 |
+
all_items = self._coerce_value(v)
|
| 223 |
+
continue
|
| 224 |
+
if not isinstance(i, int):
|
| 225 |
+
raise TypeError(
|
| 226 |
+
'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: '
|
| 227 |
+
'expected integer keys or keyword "__all__"'
|
| 228 |
+
)
|
| 229 |
+
normalized_i = v_length + i if i < 0 else i
|
| 230 |
+
normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i))
|
| 231 |
+
|
| 232 |
+
if not all_items:
|
| 233 |
+
return normalized_items
|
| 234 |
+
if self.is_true(all_items):
|
| 235 |
+
for i in range(v_length):
|
| 236 |
+
normalized_items.setdefault(i, ...)
|
| 237 |
+
return normalized_items
|
| 238 |
+
for i in range(v_length):
|
| 239 |
+
normalized_item = normalized_items.setdefault(i, {})
|
| 240 |
+
if not self.is_true(normalized_item):
|
| 241 |
+
normalized_items[i] = self.merge(all_items, normalized_item)
|
| 242 |
+
return normalized_items
|
| 243 |
+
|
| 244 |
+
@classmethod
|
| 245 |
+
def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any:
|
| 246 |
+
"""Merge a `base` item with an `override` item.
|
| 247 |
+
|
| 248 |
+
Both `base` and `override` are converted to dictionaries if possible.
|
| 249 |
+
Sets are converted to dictionaries with the sets entries as keys and
|
| 250 |
+
Ellipsis as values.
|
| 251 |
+
|
| 252 |
+
Each key-value pair existing in `base` is merged with `override`,
|
| 253 |
+
while the rest of the key-value pairs are updated recursively with this function.
|
| 254 |
+
|
| 255 |
+
Merging takes place based on the "union" of keys if `intersect` is
|
| 256 |
+
set to `False` (default) and on the intersection of keys if
|
| 257 |
+
`intersect` is set to `True`.
|
| 258 |
+
"""
|
| 259 |
+
override = cls._coerce_value(override)
|
| 260 |
+
base = cls._coerce_value(base)
|
| 261 |
+
if override is None:
|
| 262 |
+
return base
|
| 263 |
+
if cls.is_true(base) or base is None:
|
| 264 |
+
return override
|
| 265 |
+
if cls.is_true(override):
|
| 266 |
+
return base if intersect else override
|
| 267 |
+
|
| 268 |
+
# intersection or union of keys while preserving ordering:
|
| 269 |
+
if intersect:
|
| 270 |
+
merge_keys = [k for k in base if k in override] + [k for k in override if k in base]
|
| 271 |
+
else:
|
| 272 |
+
merge_keys = list(base) + [k for k in override if k not in base]
|
| 273 |
+
|
| 274 |
+
merged: dict[int | str, Any] = {}
|
| 275 |
+
for k in merge_keys:
|
| 276 |
+
merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect)
|
| 277 |
+
if merged_item is not None:
|
| 278 |
+
merged[k] = merged_item
|
| 279 |
+
|
| 280 |
+
return merged
|
| 281 |
+
|
| 282 |
+
@staticmethod
|
| 283 |
+
def _coerce_items(items: AbstractSetIntStr | MappingIntStrAny) -> MappingIntStrAny:
|
| 284 |
+
if isinstance(items, Mapping):
|
| 285 |
+
pass
|
| 286 |
+
elif isinstance(items, AbstractSet):
|
| 287 |
+
items = dict.fromkeys(items, ...) # type: ignore
|
| 288 |
+
else:
|
| 289 |
+
class_name = getattr(items, '__class__', '???')
|
| 290 |
+
raise TypeError(f'Unexpected type of exclude value {class_name}')
|
| 291 |
+
return items # type: ignore
|
| 292 |
+
|
| 293 |
+
@classmethod
|
| 294 |
+
def _coerce_value(cls, value: Any) -> Any:
|
| 295 |
+
if value is None or cls.is_true(value):
|
| 296 |
+
return value
|
| 297 |
+
return cls._coerce_items(value)
|
| 298 |
+
|
| 299 |
+
@staticmethod
|
| 300 |
+
def is_true(v: Any) -> bool:
|
| 301 |
+
return v is True or v is ...
|
| 302 |
+
|
| 303 |
+
def __repr_args__(self) -> _repr.ReprArgs:
|
| 304 |
+
return [(None, self._items)]
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
if TYPE_CHECKING:
|
| 308 |
+
|
| 309 |
+
def LazyClassAttribute(name: str, get_value: Callable[[], T]) -> T: ...
|
| 310 |
+
|
| 311 |
+
else:
|
| 312 |
+
|
| 313 |
+
class LazyClassAttribute:
|
| 314 |
+
"""A descriptor exposing an attribute only accessible on a class (hidden from instances).
|
| 315 |
+
|
| 316 |
+
The attribute is lazily computed and cached during the first access.
|
| 317 |
+
"""
|
| 318 |
+
|
| 319 |
+
def __init__(self, name: str, get_value: Callable[[], Any]) -> None:
|
| 320 |
+
self.name = name
|
| 321 |
+
self.get_value = get_value
|
| 322 |
+
|
| 323 |
+
@cached_property
|
| 324 |
+
def value(self) -> Any:
|
| 325 |
+
return self.get_value()
|
| 326 |
+
|
| 327 |
+
def __get__(self, instance: Any, owner: type[Any]) -> None:
|
| 328 |
+
if instance is None:
|
| 329 |
+
return self.value
|
| 330 |
+
raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only')
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
Obj = TypeVar('Obj')
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def smart_deepcopy(obj: Obj) -> Obj:
|
| 337 |
+
"""Return type as is for immutable built-in types
|
| 338 |
+
Use obj.copy() for built-in empty collections
|
| 339 |
+
Use copy.deepcopy() for non-empty collections and unknown objects.
|
| 340 |
+
"""
|
| 341 |
+
if obj is MISSING or obj is PydanticUndefined:
|
| 342 |
+
return obj # pyright: ignore[reportReturnType]
|
| 343 |
+
obj_type = obj.__class__
|
| 344 |
+
if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES:
|
| 345 |
+
return obj # fastest case: obj is immutable and not collection therefore will not be copied anyway
|
| 346 |
+
try:
|
| 347 |
+
if not obj and obj_type in BUILTIN_COLLECTIONS:
|
| 348 |
+
# faster way for empty collections, no need to copy its members
|
| 349 |
+
return obj if obj_type is tuple else obj.copy() # tuple doesn't have copy method # type: ignore
|
| 350 |
+
except (TypeError, ValueError, RuntimeError):
|
| 351 |
+
# do we really dare to catch ALL errors? Seems a bit risky
|
| 352 |
+
pass
|
| 353 |
+
|
| 354 |
+
return deepcopy(obj) # slowest way when we actually might need a deepcopy
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
_SENTINEL = object()
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def all_identical(left: Iterable[Any], right: Iterable[Any]) -> bool:
|
| 361 |
+
"""Check that the items of `left` are the same objects as those in `right`.
|
| 362 |
+
|
| 363 |
+
>>> a, b = object(), object()
|
| 364 |
+
>>> all_identical([a, b, a], [a, b, a])
|
| 365 |
+
True
|
| 366 |
+
>>> all_identical([a, b, [a]], [a, b, [a]]) # new list object, while "equal" is not "identical"
|
| 367 |
+
False
|
| 368 |
+
"""
|
| 369 |
+
for left_item, right_item in zip_longest(left, right, fillvalue=_SENTINEL):
|
| 370 |
+
if left_item is not right_item:
|
| 371 |
+
return False
|
| 372 |
+
return True
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
def get_first_not_none(a: Any, b: Any) -> Any:
|
| 376 |
+
"""Return the first argument if it is not `None`, otherwise return the second argument."""
|
| 377 |
+
return a if a is not None else b
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
@dataclasses.dataclass(frozen=True)
|
| 381 |
+
class SafeGetItemProxy:
|
| 382 |
+
"""Wrapper redirecting `__getitem__` to `get` with a sentinel value as default
|
| 383 |
+
|
| 384 |
+
This makes is safe to use in `operator.itemgetter` when some keys may be missing
|
| 385 |
+
"""
|
| 386 |
+
|
| 387 |
+
# Define __slots__manually for performances
|
| 388 |
+
# @dataclasses.dataclass() only support slots=True in python>=3.10
|
| 389 |
+
__slots__ = ('wrapped',)
|
| 390 |
+
|
| 391 |
+
wrapped: Mapping[str, Any]
|
| 392 |
+
|
| 393 |
+
def __getitem__(self, key: str, /) -> Any:
|
| 394 |
+
return self.wrapped.get(key, _SENTINEL)
|
| 395 |
+
|
| 396 |
+
# required to pass the object to operator.itemgetter() instances due to a quirk of typeshed
|
| 397 |
+
# https://github.com/python/mypy/issues/13713
|
| 398 |
+
# https://github.com/python/typeshed/pull/8785
|
| 399 |
+
# Since this is typing-only, hide it in a typing.TYPE_CHECKING block
|
| 400 |
+
if TYPE_CHECKING:
|
| 401 |
+
|
| 402 |
+
def __contains__(self, key: str, /) -> bool:
|
| 403 |
+
return self.wrapped.__contains__(key)
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
_ModelT = TypeVar('_ModelT', bound='BaseModel')
|
| 407 |
+
_RT = TypeVar('_RT')
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
class deprecated_instance_property(Generic[_ModelT, _RT]):
|
| 411 |
+
"""A decorator exposing the decorated class method as a property, with a warning on instance access.
|
| 412 |
+
|
| 413 |
+
This decorator takes a class method defined on the `BaseModel` class and transforms it into
|
| 414 |
+
an attribute. The attribute can be accessed on both the class and instances of the class. If accessed
|
| 415 |
+
via an instance, a deprecation warning is emitted stating that instance access will be removed in V3.
|
| 416 |
+
"""
|
| 417 |
+
|
| 418 |
+
def __init__(self, fget: Callable[[type[_ModelT]], _RT], /) -> None:
|
| 419 |
+
# Note: fget should be a classmethod:
|
| 420 |
+
self.fget = fget
|
| 421 |
+
|
| 422 |
+
@overload
|
| 423 |
+
def __get__(self, instance: None, objtype: type[_ModelT]) -> _RT: ...
|
| 424 |
+
@overload
|
| 425 |
+
@deprecated(
|
| 426 |
+
'Accessing this attribute on the instance is deprecated, and will be removed in Pydantic V3. '
|
| 427 |
+
'Instead, you should access this attribute from the model class.',
|
| 428 |
+
category=None,
|
| 429 |
+
)
|
| 430 |
+
def __get__(self, instance: _ModelT, objtype: type[_ModelT]) -> _RT: ...
|
| 431 |
+
def __get__(self, instance: _ModelT | None, objtype: type[_ModelT]) -> _RT:
|
| 432 |
+
if instance is not None:
|
| 433 |
+
# fmt: off
|
| 434 |
+
attr_name = (
|
| 435 |
+
self.fget.__name__
|
| 436 |
+
if sys.version_info >= (3, 10)
|
| 437 |
+
else self.fget.__func__.__name__ # pyright: ignore[reportFunctionMemberAccess]
|
| 438 |
+
)
|
| 439 |
+
# fmt: on
|
| 440 |
+
warnings.warn(
|
| 441 |
+
f'Accessing the {attr_name!r} attribute on the instance is deprecated. '
|
| 442 |
+
'Instead, you should access this attribute from the model class.',
|
| 443 |
+
category=PydanticDeprecatedSince211,
|
| 444 |
+
stacklevel=2,
|
| 445 |
+
)
|
| 446 |
+
return self.fget.__get__(instance, objtype)()
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations as _annotations
|
| 2 |
+
|
| 3 |
+
import functools
|
| 4 |
+
import inspect
|
| 5 |
+
from collections.abc import Awaitable
|
| 6 |
+
from functools import partial
|
| 7 |
+
from typing import Any, Callable
|
| 8 |
+
|
| 9 |
+
import pydantic_core
|
| 10 |
+
|
| 11 |
+
from ..config import ConfigDict
|
| 12 |
+
from ..plugin._schema_validator import create_schema_validator
|
| 13 |
+
from ._config import ConfigWrapper
|
| 14 |
+
from ._generate_schema import GenerateSchema, ValidateCallSupportedTypes
|
| 15 |
+
from ._namespace_utils import MappingNamespace, NsResolver, ns_for_function
|
| 16 |
+
from ._typing_extra import signature_no_eval
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def extract_function_name(func: ValidateCallSupportedTypes) -> str:
|
| 20 |
+
"""Extract the name of a `ValidateCallSupportedTypes` object."""
|
| 21 |
+
return f'partial({func.func.__name__})' if isinstance(func, functools.partial) else func.__name__
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def extract_function_qualname(func: ValidateCallSupportedTypes) -> str:
|
| 25 |
+
"""Extract the qualname of a `ValidateCallSupportedTypes` object."""
|
| 26 |
+
return f'partial({func.func.__qualname__})' if isinstance(func, functools.partial) else func.__qualname__
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def update_wrapper_attributes(wrapped: ValidateCallSupportedTypes, wrapper: Callable[..., Any]):
|
| 30 |
+
"""Update the `wrapper` function with the attributes of the `wrapped` function. Return the updated function."""
|
| 31 |
+
if inspect.iscoroutinefunction(wrapped):
|
| 32 |
+
|
| 33 |
+
@functools.wraps(wrapped)
|
| 34 |
+
async def wrapper_function(*args, **kwargs): # type: ignore
|
| 35 |
+
return await wrapper(*args, **kwargs)
|
| 36 |
+
else:
|
| 37 |
+
|
| 38 |
+
@functools.wraps(wrapped)
|
| 39 |
+
def wrapper_function(*args, **kwargs):
|
| 40 |
+
return wrapper(*args, **kwargs)
|
| 41 |
+
|
| 42 |
+
# We need to manually update this because `partial` object has no `__name__` and `__qualname__`.
|
| 43 |
+
wrapper_function.__name__ = extract_function_name(wrapped)
|
| 44 |
+
wrapper_function.__qualname__ = extract_function_qualname(wrapped)
|
| 45 |
+
wrapper_function.raw_function = wrapped # type: ignore
|
| 46 |
+
|
| 47 |
+
return wrapper_function
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class ValidateCallWrapper:
|
| 51 |
+
"""This is a wrapper around a function that validates the arguments passed to it, and optionally the return value."""
|
| 52 |
+
|
| 53 |
+
__slots__ = (
|
| 54 |
+
'function',
|
| 55 |
+
'validate_return',
|
| 56 |
+
'schema_type',
|
| 57 |
+
'module',
|
| 58 |
+
'qualname',
|
| 59 |
+
'ns_resolver',
|
| 60 |
+
'config_wrapper',
|
| 61 |
+
'__pydantic_complete__',
|
| 62 |
+
'__pydantic_validator__',
|
| 63 |
+
'__return_pydantic_validator__',
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
def __init__(
|
| 67 |
+
self,
|
| 68 |
+
function: ValidateCallSupportedTypes,
|
| 69 |
+
config: ConfigDict | None,
|
| 70 |
+
validate_return: bool,
|
| 71 |
+
parent_namespace: MappingNamespace | None,
|
| 72 |
+
) -> None:
|
| 73 |
+
self.function = function
|
| 74 |
+
self.validate_return = validate_return
|
| 75 |
+
if isinstance(function, partial):
|
| 76 |
+
self.schema_type = function.func
|
| 77 |
+
self.module = function.func.__module__
|
| 78 |
+
else:
|
| 79 |
+
self.schema_type = function
|
| 80 |
+
self.module = function.__module__
|
| 81 |
+
self.qualname = extract_function_qualname(function)
|
| 82 |
+
|
| 83 |
+
self.ns_resolver = NsResolver(
|
| 84 |
+
namespaces_tuple=ns_for_function(self.schema_type, parent_namespace=parent_namespace)
|
| 85 |
+
)
|
| 86 |
+
self.config_wrapper = ConfigWrapper(config)
|
| 87 |
+
if not self.config_wrapper.defer_build:
|
| 88 |
+
self._create_validators()
|
| 89 |
+
else:
|
| 90 |
+
self.__pydantic_complete__ = False
|
| 91 |
+
|
| 92 |
+
def _create_validators(self) -> None:
|
| 93 |
+
gen_schema = GenerateSchema(self.config_wrapper, self.ns_resolver)
|
| 94 |
+
schema = gen_schema.clean_schema(gen_schema.generate_schema(self.function))
|
| 95 |
+
core_config = self.config_wrapper.core_config(title=self.qualname)
|
| 96 |
+
|
| 97 |
+
self.__pydantic_validator__ = create_schema_validator(
|
| 98 |
+
schema,
|
| 99 |
+
self.schema_type,
|
| 100 |
+
self.module,
|
| 101 |
+
self.qualname,
|
| 102 |
+
'validate_call',
|
| 103 |
+
core_config,
|
| 104 |
+
self.config_wrapper.plugin_settings,
|
| 105 |
+
)
|
| 106 |
+
if self.validate_return:
|
| 107 |
+
signature = signature_no_eval(self.function)
|
| 108 |
+
return_type = signature.return_annotation if signature.return_annotation is not signature.empty else Any
|
| 109 |
+
gen_schema = GenerateSchema(self.config_wrapper, self.ns_resolver)
|
| 110 |
+
schema = gen_schema.clean_schema(gen_schema.generate_schema(return_type))
|
| 111 |
+
validator = create_schema_validator(
|
| 112 |
+
schema,
|
| 113 |
+
self.schema_type,
|
| 114 |
+
self.module,
|
| 115 |
+
self.qualname,
|
| 116 |
+
'validate_call',
|
| 117 |
+
core_config,
|
| 118 |
+
self.config_wrapper.plugin_settings,
|
| 119 |
+
)
|
| 120 |
+
if inspect.iscoroutinefunction(self.function):
|
| 121 |
+
|
| 122 |
+
async def return_val_wrapper(aw: Awaitable[Any]) -> None:
|
| 123 |
+
return validator.validate_python(await aw)
|
| 124 |
+
|
| 125 |
+
self.__return_pydantic_validator__ = return_val_wrapper
|
| 126 |
+
else:
|
| 127 |
+
self.__return_pydantic_validator__ = validator.validate_python
|
| 128 |
+
else:
|
| 129 |
+
self.__return_pydantic_validator__ = None
|
| 130 |
+
|
| 131 |
+
self.__pydantic_complete__ = True
|
| 132 |
+
|
| 133 |
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
| 134 |
+
if not self.__pydantic_complete__:
|
| 135 |
+
self._create_validators()
|
| 136 |
+
|
| 137 |
+
res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
|
| 138 |
+
if self.__return_pydantic_validator__:
|
| 139 |
+
return self.__return_pydantic_validator__(res)
|
| 140 |
+
else:
|
| 141 |
+
return res
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Validator functions for standard library types.
|
| 2 |
+
|
| 3 |
+
Import of this module is deferred since it contains imports of many standard library modules.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from __future__ import annotations as _annotations
|
| 7 |
+
|
| 8 |
+
import collections.abc
|
| 9 |
+
import math
|
| 10 |
+
import re
|
| 11 |
+
import typing
|
| 12 |
+
from collections.abc import Sequence
|
| 13 |
+
from decimal import Decimal
|
| 14 |
+
from fractions import Fraction
|
| 15 |
+
from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
|
| 16 |
+
from typing import Any, Callable, TypeVar, Union, cast
|
| 17 |
+
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
| 18 |
+
|
| 19 |
+
import typing_extensions
|
| 20 |
+
from pydantic_core import PydanticCustomError, PydanticKnownError, core_schema
|
| 21 |
+
from typing_extensions import get_args, get_origin
|
| 22 |
+
from typing_inspection import typing_objects
|
| 23 |
+
|
| 24 |
+
from pydantic._internal._import_utils import import_cached_field_info
|
| 25 |
+
from pydantic.errors import PydanticSchemaGenerationError
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def sequence_validator(
|
| 29 |
+
input_value: Sequence[Any],
|
| 30 |
+
/,
|
| 31 |
+
validator: core_schema.ValidatorFunctionWrapHandler,
|
| 32 |
+
) -> Sequence[Any]:
|
| 33 |
+
"""Validator for `Sequence` types, isinstance(v, Sequence) has already been called."""
|
| 34 |
+
value_type = type(input_value)
|
| 35 |
+
|
| 36 |
+
# We don't accept any plain string as a sequence
|
| 37 |
+
# Relevant issue: https://github.com/pydantic/pydantic/issues/5595
|
| 38 |
+
if issubclass(value_type, (str, bytes)):
|
| 39 |
+
raise PydanticCustomError(
|
| 40 |
+
'sequence_str',
|
| 41 |
+
"'{type_name}' instances are not allowed as a Sequence value",
|
| 42 |
+
{'type_name': value_type.__name__},
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
# TODO: refactor sequence validation to validate with either a list or a tuple
|
| 46 |
+
# schema, depending on the type of the value.
|
| 47 |
+
# Additionally, we should be able to remove one of either this validator or the
|
| 48 |
+
# SequenceValidator in _std_types_schema.py (preferably this one, while porting over some logic).
|
| 49 |
+
# Effectively, a refactor for sequence validation is needed.
|
| 50 |
+
if value_type is tuple:
|
| 51 |
+
input_value = list(input_value)
|
| 52 |
+
|
| 53 |
+
v_list = validator(input_value)
|
| 54 |
+
|
| 55 |
+
# the rest of the logic is just re-creating the original type from `v_list`
|
| 56 |
+
if value_type is list:
|
| 57 |
+
return v_list
|
| 58 |
+
elif issubclass(value_type, range):
|
| 59 |
+
# return the list as we probably can't re-create the range
|
| 60 |
+
return v_list
|
| 61 |
+
elif value_type is tuple:
|
| 62 |
+
return tuple(v_list)
|
| 63 |
+
else:
|
| 64 |
+
# best guess at how to re-create the original type, more custom construction logic might be required
|
| 65 |
+
return value_type(v_list) # type: ignore[call-arg]
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def import_string(value: Any) -> Any:
|
| 69 |
+
if isinstance(value, str):
|
| 70 |
+
try:
|
| 71 |
+
return _import_string_logic(value)
|
| 72 |
+
except ImportError as e:
|
| 73 |
+
raise PydanticCustomError('import_error', 'Invalid python path: {error}', {'error': str(e)}) from e
|
| 74 |
+
else:
|
| 75 |
+
# otherwise we just return the value and let the next validator do the rest of the work
|
| 76 |
+
return value
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _import_string_logic(dotted_path: str) -> Any:
|
| 80 |
+
"""Inspired by uvicorn β dotted paths should include a colon before the final item if that item is not a module.
|
| 81 |
+
(This is necessary to distinguish between a submodule and an attribute when there is a conflict.).
|
| 82 |
+
|
| 83 |
+
If the dotted path does not include a colon and the final item is not a valid module, importing as an attribute
|
| 84 |
+
rather than a submodule will be attempted automatically.
|
| 85 |
+
|
| 86 |
+
So, for example, the following values of `dotted_path` result in the following returned values:
|
| 87 |
+
* 'collections': <module 'collections'>
|
| 88 |
+
* 'collections.abc': <module 'collections.abc'>
|
| 89 |
+
* 'collections.abc:Mapping': <class 'collections.abc.Mapping'>
|
| 90 |
+
* `collections.abc.Mapping`: <class 'collections.abc.Mapping'> (though this is a bit slower than the previous line)
|
| 91 |
+
|
| 92 |
+
An error will be raised under any of the following scenarios:
|
| 93 |
+
* `dotted_path` contains more than one colon (e.g., 'collections:abc:Mapping')
|
| 94 |
+
* the substring of `dotted_path` before the colon is not a valid module in the environment (e.g., '123:Mapping')
|
| 95 |
+
* the substring of `dotted_path` after the colon is not an attribute of the module (e.g., 'collections:abc123')
|
| 96 |
+
"""
|
| 97 |
+
from importlib import import_module
|
| 98 |
+
|
| 99 |
+
components = dotted_path.strip().split(':')
|
| 100 |
+
if len(components) > 2:
|
| 101 |
+
raise ImportError(f"Import strings should have at most one ':'; received {dotted_path!r}")
|
| 102 |
+
attribute = None
|
| 103 |
+
if len(components) == 2:
|
| 104 |
+
attribute = components[1]
|
| 105 |
+
module_path = components[0]
|
| 106 |
+
if not module_path:
|
| 107 |
+
raise ImportError(f'Import strings should have a nonempty module name; received {dotted_path!r}')
|
| 108 |
+
|
| 109 |
+
try:
|
| 110 |
+
module = import_module(module_path)
|
| 111 |
+
except ModuleNotFoundError:
|
| 112 |
+
if attribute is None and '.' in module_path:
|
| 113 |
+
# Try interpreting the final dotted segment as an attribute, not a submodule
|
| 114 |
+
maybe_module_path, maybe_attribute = module_path.rsplit('.', 1)
|
| 115 |
+
|
| 116 |
+
try:
|
| 117 |
+
return _import_string_logic(f'{maybe_module_path}:{maybe_attribute}')
|
| 118 |
+
except ImportError:
|
| 119 |
+
pass
|
| 120 |
+
raise
|
| 121 |
+
|
| 122 |
+
if attribute is not None:
|
| 123 |
+
try:
|
| 124 |
+
return getattr(module, attribute)
|
| 125 |
+
except AttributeError as e:
|
| 126 |
+
raise ImportError(f'cannot import name {attribute!r} from {module_path!r}') from e
|
| 127 |
+
else:
|
| 128 |
+
return module
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def pattern_either_validator(input_value: Any, /) -> re.Pattern[Any]:
|
| 132 |
+
if isinstance(input_value, re.Pattern):
|
| 133 |
+
return input_value
|
| 134 |
+
elif isinstance(input_value, (str, bytes)):
|
| 135 |
+
# todo strict mode
|
| 136 |
+
return compile_pattern(input_value) # type: ignore
|
| 137 |
+
else:
|
| 138 |
+
raise PydanticCustomError('pattern_type', 'Input should be a valid pattern')
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def pattern_str_validator(input_value: Any, /) -> re.Pattern[str]:
|
| 142 |
+
if isinstance(input_value, re.Pattern):
|
| 143 |
+
if isinstance(input_value.pattern, str):
|
| 144 |
+
return input_value
|
| 145 |
+
else:
|
| 146 |
+
raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern')
|
| 147 |
+
elif isinstance(input_value, str):
|
| 148 |
+
return compile_pattern(input_value)
|
| 149 |
+
elif isinstance(input_value, bytes):
|
| 150 |
+
raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern')
|
| 151 |
+
else:
|
| 152 |
+
raise PydanticCustomError('pattern_type', 'Input should be a valid pattern')
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def pattern_bytes_validator(input_value: Any, /) -> re.Pattern[bytes]:
|
| 156 |
+
if isinstance(input_value, re.Pattern):
|
| 157 |
+
if isinstance(input_value.pattern, bytes):
|
| 158 |
+
return input_value
|
| 159 |
+
else:
|
| 160 |
+
raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern')
|
| 161 |
+
elif isinstance(input_value, bytes):
|
| 162 |
+
return compile_pattern(input_value)
|
| 163 |
+
elif isinstance(input_value, str):
|
| 164 |
+
raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern')
|
| 165 |
+
else:
|
| 166 |
+
raise PydanticCustomError('pattern_type', 'Input should be a valid pattern')
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
PatternType = TypeVar('PatternType', str, bytes)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def compile_pattern(pattern: PatternType) -> re.Pattern[PatternType]:
|
| 173 |
+
try:
|
| 174 |
+
return re.compile(pattern)
|
| 175 |
+
except re.error:
|
| 176 |
+
raise PydanticCustomError('pattern_regex', 'Input should be a valid regular expression')
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def ip_v4_address_validator(input_value: Any, /) -> IPv4Address:
|
| 180 |
+
if isinstance(input_value, IPv4Address):
|
| 181 |
+
return input_value
|
| 182 |
+
|
| 183 |
+
try:
|
| 184 |
+
return IPv4Address(input_value)
|
| 185 |
+
except ValueError:
|
| 186 |
+
raise PydanticCustomError('ip_v4_address', 'Input is not a valid IPv4 address')
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def ip_v6_address_validator(input_value: Any, /) -> IPv6Address:
|
| 190 |
+
if isinstance(input_value, IPv6Address):
|
| 191 |
+
return input_value
|
| 192 |
+
|
| 193 |
+
try:
|
| 194 |
+
return IPv6Address(input_value)
|
| 195 |
+
except ValueError:
|
| 196 |
+
raise PydanticCustomError('ip_v6_address', 'Input is not a valid IPv6 address')
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def ip_v4_network_validator(input_value: Any, /) -> IPv4Network:
|
| 200 |
+
"""Assume IPv4Network initialised with a default `strict` argument.
|
| 201 |
+
|
| 202 |
+
See more:
|
| 203 |
+
https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network
|
| 204 |
+
"""
|
| 205 |
+
if isinstance(input_value, IPv4Network):
|
| 206 |
+
return input_value
|
| 207 |
+
|
| 208 |
+
try:
|
| 209 |
+
return IPv4Network(input_value)
|
| 210 |
+
except ValueError:
|
| 211 |
+
raise PydanticCustomError('ip_v4_network', 'Input is not a valid IPv4 network')
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def ip_v6_network_validator(input_value: Any, /) -> IPv6Network:
|
| 215 |
+
"""Assume IPv6Network initialised with a default `strict` argument.
|
| 216 |
+
|
| 217 |
+
See more:
|
| 218 |
+
https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network
|
| 219 |
+
"""
|
| 220 |
+
if isinstance(input_value, IPv6Network):
|
| 221 |
+
return input_value
|
| 222 |
+
|
| 223 |
+
try:
|
| 224 |
+
return IPv6Network(input_value)
|
| 225 |
+
except ValueError:
|
| 226 |
+
raise PydanticCustomError('ip_v6_network', 'Input is not a valid IPv6 network')
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def ip_v4_interface_validator(input_value: Any, /) -> IPv4Interface:
|
| 230 |
+
if isinstance(input_value, IPv4Interface):
|
| 231 |
+
return input_value
|
| 232 |
+
|
| 233 |
+
try:
|
| 234 |
+
return IPv4Interface(input_value)
|
| 235 |
+
except ValueError:
|
| 236 |
+
raise PydanticCustomError('ip_v4_interface', 'Input is not a valid IPv4 interface')
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def ip_v6_interface_validator(input_value: Any, /) -> IPv6Interface:
|
| 240 |
+
if isinstance(input_value, IPv6Interface):
|
| 241 |
+
return input_value
|
| 242 |
+
|
| 243 |
+
try:
|
| 244 |
+
return IPv6Interface(input_value)
|
| 245 |
+
except ValueError:
|
| 246 |
+
raise PydanticCustomError('ip_v6_interface', 'Input is not a valid IPv6 interface')
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def fraction_validator(input_value: Any, /) -> Fraction:
|
| 250 |
+
if isinstance(input_value, Fraction):
|
| 251 |
+
return input_value
|
| 252 |
+
|
| 253 |
+
try:
|
| 254 |
+
return Fraction(input_value)
|
| 255 |
+
except ValueError:
|
| 256 |
+
raise PydanticCustomError('fraction_parsing', 'Input is not a valid fraction')
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
def forbid_inf_nan_check(x: Any) -> Any:
|
| 260 |
+
if not math.isfinite(x):
|
| 261 |
+
raise PydanticKnownError('finite_number')
|
| 262 |
+
return x
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def _safe_repr(v: Any) -> int | float | str:
|
| 266 |
+
"""The context argument for `PydanticKnownError` requires a number or str type, so we do a simple repr() coercion for types like timedelta.
|
| 267 |
+
|
| 268 |
+
See tests/test_types.py::test_annotated_metadata_any_order for some context.
|
| 269 |
+
"""
|
| 270 |
+
if isinstance(v, (int, float, str)):
|
| 271 |
+
return v
|
| 272 |
+
return repr(v)
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def greater_than_validator(x: Any, gt: Any) -> Any:
|
| 276 |
+
try:
|
| 277 |
+
if not (x > gt):
|
| 278 |
+
raise PydanticKnownError('greater_than', {'gt': _safe_repr(gt)})
|
| 279 |
+
return x
|
| 280 |
+
except TypeError:
|
| 281 |
+
raise TypeError(f"Unable to apply constraint 'gt' to supplied value {x}")
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def greater_than_or_equal_validator(x: Any, ge: Any) -> Any:
|
| 285 |
+
try:
|
| 286 |
+
if not (x >= ge):
|
| 287 |
+
raise PydanticKnownError('greater_than_equal', {'ge': _safe_repr(ge)})
|
| 288 |
+
return x
|
| 289 |
+
except TypeError:
|
| 290 |
+
raise TypeError(f"Unable to apply constraint 'ge' to supplied value {x}")
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def less_than_validator(x: Any, lt: Any) -> Any:
|
| 294 |
+
try:
|
| 295 |
+
if not (x < lt):
|
| 296 |
+
raise PydanticKnownError('less_than', {'lt': _safe_repr(lt)})
|
| 297 |
+
return x
|
| 298 |
+
except TypeError:
|
| 299 |
+
raise TypeError(f"Unable to apply constraint 'lt' to supplied value {x}")
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def less_than_or_equal_validator(x: Any, le: Any) -> Any:
|
| 303 |
+
try:
|
| 304 |
+
if not (x <= le):
|
| 305 |
+
raise PydanticKnownError('less_than_equal', {'le': _safe_repr(le)})
|
| 306 |
+
return x
|
| 307 |
+
except TypeError:
|
| 308 |
+
raise TypeError(f"Unable to apply constraint 'le' to supplied value {x}")
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
def multiple_of_validator(x: Any, multiple_of: Any) -> Any:
|
| 312 |
+
try:
|
| 313 |
+
if x % multiple_of:
|
| 314 |
+
raise PydanticKnownError('multiple_of', {'multiple_of': _safe_repr(multiple_of)})
|
| 315 |
+
return x
|
| 316 |
+
except TypeError:
|
| 317 |
+
raise TypeError(f"Unable to apply constraint 'multiple_of' to supplied value {x}")
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def min_length_validator(x: Any, min_length: Any) -> Any:
|
| 321 |
+
try:
|
| 322 |
+
if not (len(x) >= min_length):
|
| 323 |
+
raise PydanticKnownError(
|
| 324 |
+
'too_short', {'field_type': 'Value', 'min_length': min_length, 'actual_length': len(x)}
|
| 325 |
+
)
|
| 326 |
+
return x
|
| 327 |
+
except TypeError:
|
| 328 |
+
raise TypeError(f"Unable to apply constraint 'min_length' to supplied value {x}")
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def max_length_validator(x: Any, max_length: Any) -> Any:
|
| 332 |
+
try:
|
| 333 |
+
if len(x) > max_length:
|
| 334 |
+
raise PydanticKnownError(
|
| 335 |
+
'too_long',
|
| 336 |
+
{'field_type': 'Value', 'max_length': max_length, 'actual_length': len(x)},
|
| 337 |
+
)
|
| 338 |
+
return x
|
| 339 |
+
except TypeError:
|
| 340 |
+
raise TypeError(f"Unable to apply constraint 'max_length' to supplied value {x}")
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
def _extract_decimal_digits_info(decimal: Decimal) -> tuple[int, int]:
|
| 344 |
+
"""Compute the total number of digits and decimal places for a given [`Decimal`][decimal.Decimal] instance.
|
| 345 |
+
|
| 346 |
+
This function handles both normalized and non-normalized Decimal instances.
|
| 347 |
+
Example: Decimal('1.230') -> 4 digits, 3 decimal places
|
| 348 |
+
|
| 349 |
+
Args:
|
| 350 |
+
decimal (Decimal): The decimal number to analyze.
|
| 351 |
+
|
| 352 |
+
Returns:
|
| 353 |
+
tuple[int, int]: A tuple containing the number of decimal places and total digits.
|
| 354 |
+
|
| 355 |
+
Though this could be divided into two separate functions, the logic is easier to follow if we couple the computation
|
| 356 |
+
of the number of decimals and digits together.
|
| 357 |
+
"""
|
| 358 |
+
try:
|
| 359 |
+
decimal_tuple = decimal.as_tuple()
|
| 360 |
+
|
| 361 |
+
assert isinstance(decimal_tuple.exponent, int)
|
| 362 |
+
|
| 363 |
+
exponent = decimal_tuple.exponent
|
| 364 |
+
num_digits = len(decimal_tuple.digits)
|
| 365 |
+
|
| 366 |
+
if exponent >= 0:
|
| 367 |
+
# A positive exponent adds that many trailing zeros
|
| 368 |
+
# Ex: digit_tuple=(1, 2, 3), exponent=2 -> 12300 -> 0 decimal places, 5 digits
|
| 369 |
+
num_digits += exponent
|
| 370 |
+
decimal_places = 0
|
| 371 |
+
else:
|
| 372 |
+
# If the absolute value of the negative exponent is larger than the
|
| 373 |
+
# number of digits, then it's the same as the number of digits,
|
| 374 |
+
# because it'll consume all the digits in digit_tuple and then
|
| 375 |
+
# add abs(exponent) - len(digit_tuple) leading zeros after the decimal point.
|
| 376 |
+
# Ex: digit_tuple=(1, 2, 3), exponent=-2 -> 1.23 -> 2 decimal places, 3 digits
|
| 377 |
+
# Ex: digit_tuple=(1, 2, 3), exponent=-4 -> 0.0123 -> 4 decimal places, 4 digits
|
| 378 |
+
decimal_places = abs(exponent)
|
| 379 |
+
num_digits = max(num_digits, decimal_places)
|
| 380 |
+
|
| 381 |
+
return decimal_places, num_digits
|
| 382 |
+
except (AssertionError, AttributeError):
|
| 383 |
+
raise TypeError(f'Unable to extract decimal digits info from supplied value {decimal}')
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def max_digits_validator(x: Any, max_digits: Any) -> Any:
|
| 387 |
+
try:
|
| 388 |
+
_, num_digits = _extract_decimal_digits_info(x)
|
| 389 |
+
_, normalized_num_digits = _extract_decimal_digits_info(x.normalize())
|
| 390 |
+
if (num_digits > max_digits) and (normalized_num_digits > max_digits):
|
| 391 |
+
raise PydanticKnownError(
|
| 392 |
+
'decimal_max_digits',
|
| 393 |
+
{'max_digits': max_digits},
|
| 394 |
+
)
|
| 395 |
+
return x
|
| 396 |
+
except TypeError:
|
| 397 |
+
raise TypeError(f"Unable to apply constraint 'max_digits' to supplied value {x}")
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def decimal_places_validator(x: Any, decimal_places: Any) -> Any:
|
| 401 |
+
try:
|
| 402 |
+
decimal_places_, _ = _extract_decimal_digits_info(x)
|
| 403 |
+
if decimal_places_ > decimal_places:
|
| 404 |
+
normalized_decimal_places, _ = _extract_decimal_digits_info(x.normalize())
|
| 405 |
+
if normalized_decimal_places > decimal_places:
|
| 406 |
+
raise PydanticKnownError(
|
| 407 |
+
'decimal_max_places',
|
| 408 |
+
{'decimal_places': decimal_places},
|
| 409 |
+
)
|
| 410 |
+
return x
|
| 411 |
+
except TypeError:
|
| 412 |
+
raise TypeError(f"Unable to apply constraint 'decimal_places' to supplied value {x}")
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
def deque_validator(input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> collections.deque[Any]:
|
| 416 |
+
return collections.deque(handler(input_value), maxlen=getattr(input_value, 'maxlen', None))
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def defaultdict_validator(
|
| 420 |
+
input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, default_default_factory: Callable[[], Any]
|
| 421 |
+
) -> collections.defaultdict[Any, Any]:
|
| 422 |
+
if isinstance(input_value, collections.defaultdict):
|
| 423 |
+
default_factory = input_value.default_factory
|
| 424 |
+
return collections.defaultdict(default_factory, handler(input_value))
|
| 425 |
+
else:
|
| 426 |
+
return collections.defaultdict(default_default_factory, handler(input_value))
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
def get_defaultdict_default_default_factory(values_source_type: Any) -> Callable[[], Any]:
|
| 430 |
+
FieldInfo = import_cached_field_info()
|
| 431 |
+
|
| 432 |
+
values_type_origin = get_origin(values_source_type)
|
| 433 |
+
|
| 434 |
+
def infer_default() -> Callable[[], Any]:
|
| 435 |
+
allowed_default_types: dict[Any, Any] = {
|
| 436 |
+
tuple: tuple,
|
| 437 |
+
collections.abc.Sequence: tuple,
|
| 438 |
+
collections.abc.MutableSequence: list,
|
| 439 |
+
list: list,
|
| 440 |
+
typing.Sequence: list,
|
| 441 |
+
set: set,
|
| 442 |
+
typing.MutableSet: set,
|
| 443 |
+
collections.abc.MutableSet: set,
|
| 444 |
+
collections.abc.Set: frozenset,
|
| 445 |
+
typing.MutableMapping: dict,
|
| 446 |
+
typing.Mapping: dict,
|
| 447 |
+
collections.abc.Mapping: dict,
|
| 448 |
+
collections.abc.MutableMapping: dict,
|
| 449 |
+
float: float,
|
| 450 |
+
int: int,
|
| 451 |
+
str: str,
|
| 452 |
+
bool: bool,
|
| 453 |
+
}
|
| 454 |
+
values_type = values_type_origin or values_source_type
|
| 455 |
+
instructions = 'set using `DefaultDict[..., Annotated[..., Field(default_factory=...)]]`'
|
| 456 |
+
if typing_objects.is_typevar(values_type):
|
| 457 |
+
|
| 458 |
+
def type_var_default_factory() -> None:
|
| 459 |
+
raise RuntimeError(
|
| 460 |
+
'Generic defaultdict cannot be used without a concrete value type or an'
|
| 461 |
+
' explicit default factory, ' + instructions
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
return type_var_default_factory
|
| 465 |
+
elif values_type not in allowed_default_types:
|
| 466 |
+
# a somewhat subjective set of types that have reasonable default values
|
| 467 |
+
allowed_msg = ', '.join([t.__name__ for t in set(allowed_default_types.values())])
|
| 468 |
+
raise PydanticSchemaGenerationError(
|
| 469 |
+
f'Unable to infer a default factory for keys of type {values_source_type}.'
|
| 470 |
+
f' Only {allowed_msg} are supported, other types require an explicit default factory'
|
| 471 |
+
' ' + instructions
|
| 472 |
+
)
|
| 473 |
+
return allowed_default_types[values_type]
|
| 474 |
+
|
| 475 |
+
# Assume Annotated[..., Field(...)]
|
| 476 |
+
if typing_objects.is_annotated(values_type_origin):
|
| 477 |
+
field_info = next((v for v in get_args(values_source_type) if isinstance(v, FieldInfo)), None)
|
| 478 |
+
else:
|
| 479 |
+
field_info = None
|
| 480 |
+
if field_info and field_info.default_factory:
|
| 481 |
+
# Assume the default factory does not take any argument:
|
| 482 |
+
default_default_factory = cast(Callable[[], Any], field_info.default_factory)
|
| 483 |
+
else:
|
| 484 |
+
default_default_factory = infer_default()
|
| 485 |
+
return default_default_factory
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
def validate_str_is_valid_iana_tz(value: Any, /) -> ZoneInfo:
|
| 489 |
+
if isinstance(value, ZoneInfo):
|
| 490 |
+
return value
|
| 491 |
+
try:
|
| 492 |
+
return ZoneInfo(value)
|
| 493 |
+
except (ZoneInfoNotFoundError, ValueError, TypeError):
|
| 494 |
+
raise PydanticCustomError('zoneinfo_str', 'invalid timezone: {value}', {'value': value})
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
NUMERIC_VALIDATOR_LOOKUP: dict[str, Callable] = {
|
| 498 |
+
'gt': greater_than_validator,
|
| 499 |
+
'ge': greater_than_or_equal_validator,
|
| 500 |
+
'lt': less_than_validator,
|
| 501 |
+
'le': less_than_or_equal_validator,
|
| 502 |
+
'multiple_of': multiple_of_validator,
|
| 503 |
+
'min_length': min_length_validator,
|
| 504 |
+
'max_length': max_length_validator,
|
| 505 |
+
'max_digits': max_digits_validator,
|
| 506 |
+
'decimal_places': decimal_places_validator,
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
IpType = Union[IPv4Address, IPv6Address, IPv4Network, IPv6Network, IPv4Interface, IPv6Interface]
|
| 510 |
+
|
| 511 |
+
IP_VALIDATOR_LOOKUP: dict[type[IpType], Callable] = {
|
| 512 |
+
IPv4Address: ip_v4_address_validator,
|
| 513 |
+
IPv6Address: ip_v6_address_validator,
|
| 514 |
+
IPv4Network: ip_v4_network_validator,
|
| 515 |
+
IPv6Network: ip_v6_network_validator,
|
| 516 |
+
IPv4Interface: ip_v4_interface_validator,
|
| 517 |
+
IPv6Interface: ip_v6_interface_validator,
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
MAPPING_ORIGIN_MAP: dict[Any, Any] = {
|
| 521 |
+
typing.DefaultDict: collections.defaultdict, # noqa: UP006
|
| 522 |
+
collections.defaultdict: collections.defaultdict,
|
| 523 |
+
typing.OrderedDict: collections.OrderedDict, # noqa: UP006
|
| 524 |
+
collections.OrderedDict: collections.OrderedDict,
|
| 525 |
+
typing_extensions.OrderedDict: collections.OrderedDict,
|
| 526 |
+
typing.Counter: collections.Counter,
|
| 527 |
+
collections.Counter: collections.Counter,
|
| 528 |
+
# this doesn't handle subclasses of these
|
| 529 |
+
typing.Mapping: dict,
|
| 530 |
+
typing.MutableMapping: dict,
|
| 531 |
+
# parametrized typing.{Mutable}Mapping creates one of these
|
| 532 |
+
collections.abc.Mapping: dict,
|
| 533 |
+
collections.abc.MutableMapping: dict,
|
| 534 |
+
}
|
.mcp-venv/lib/python3.13/site-packages/pydantic/_migration.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from typing import Any, Callable
|
| 3 |
+
|
| 4 |
+
from pydantic.warnings import PydanticDeprecatedSince20
|
| 5 |
+
|
| 6 |
+
from .version import version_short
|
| 7 |
+
|
| 8 |
+
MOVED_IN_V2 = {
|
| 9 |
+
'pydantic.utils:version_info': 'pydantic.version:version_info',
|
| 10 |
+
'pydantic.error_wrappers:ValidationError': 'pydantic:ValidationError',
|
| 11 |
+
'pydantic.utils:to_camel': 'pydantic.alias_generators:to_pascal',
|
| 12 |
+
'pydantic.utils:to_lower_camel': 'pydantic.alias_generators:to_camel',
|
| 13 |
+
'pydantic:PyObject': 'pydantic.types:ImportString',
|
| 14 |
+
'pydantic.types:PyObject': 'pydantic.types:ImportString',
|
| 15 |
+
'pydantic.generics:GenericModel': 'pydantic.BaseModel',
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
DEPRECATED_MOVED_IN_V2 = {
|
| 19 |
+
'pydantic.tools:schema_of': 'pydantic.deprecated.tools:schema_of',
|
| 20 |
+
'pydantic.tools:parse_obj_as': 'pydantic.deprecated.tools:parse_obj_as',
|
| 21 |
+
'pydantic.tools:schema_json_of': 'pydantic.deprecated.tools:schema_json_of',
|
| 22 |
+
'pydantic.json:pydantic_encoder': 'pydantic.deprecated.json:pydantic_encoder',
|
| 23 |
+
'pydantic:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments',
|
| 24 |
+
'pydantic.json:custom_pydantic_encoder': 'pydantic.deprecated.json:custom_pydantic_encoder',
|
| 25 |
+
'pydantic.json:timedelta_isoformat': 'pydantic.deprecated.json:timedelta_isoformat',
|
| 26 |
+
'pydantic.decorator:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments',
|
| 27 |
+
'pydantic.class_validators:validator': 'pydantic.deprecated.class_validators:validator',
|
| 28 |
+
'pydantic.class_validators:root_validator': 'pydantic.deprecated.class_validators:root_validator',
|
| 29 |
+
'pydantic.config:BaseConfig': 'pydantic.deprecated.config:BaseConfig',
|
| 30 |
+
'pydantic.config:Extra': 'pydantic.deprecated.config:Extra',
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
REDIRECT_TO_V1 = {
|
| 34 |
+
f'pydantic.utils:{obj}': f'pydantic.v1.utils:{obj}'
|
| 35 |
+
for obj in (
|
| 36 |
+
'deep_update',
|
| 37 |
+
'GetterDict',
|
| 38 |
+
'lenient_issubclass',
|
| 39 |
+
'lenient_isinstance',
|
| 40 |
+
'is_valid_field',
|
| 41 |
+
'update_not_none',
|
| 42 |
+
'import_string',
|
| 43 |
+
'Representation',
|
| 44 |
+
'ROOT_KEY',
|
| 45 |
+
'smart_deepcopy',
|
| 46 |
+
'sequence_like',
|
| 47 |
+
)
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
REMOVED_IN_V2 = {
|
| 52 |
+
'pydantic:ConstrainedBytes',
|
| 53 |
+
'pydantic:ConstrainedDate',
|
| 54 |
+
'pydantic:ConstrainedDecimal',
|
| 55 |
+
'pydantic:ConstrainedFloat',
|
| 56 |
+
'pydantic:ConstrainedFrozenSet',
|
| 57 |
+
'pydantic:ConstrainedInt',
|
| 58 |
+
'pydantic:ConstrainedList',
|
| 59 |
+
'pydantic:ConstrainedSet',
|
| 60 |
+
'pydantic:ConstrainedStr',
|
| 61 |
+
'pydantic:JsonWrapper',
|
| 62 |
+
'pydantic:NoneBytes',
|
| 63 |
+
'pydantic:NoneStr',
|
| 64 |
+
'pydantic:NoneStrBytes',
|
| 65 |
+
'pydantic:Protocol',
|
| 66 |
+
'pydantic:Required',
|
| 67 |
+
'pydantic:StrBytes',
|
| 68 |
+
'pydantic:compiled',
|
| 69 |
+
'pydantic.config:get_config',
|
| 70 |
+
'pydantic.config:inherit_config',
|
| 71 |
+
'pydantic.config:prepare_config',
|
| 72 |
+
'pydantic:create_model_from_namedtuple',
|
| 73 |
+
'pydantic:create_model_from_typeddict',
|
| 74 |
+
'pydantic.dataclasses:create_pydantic_model_from_dataclass',
|
| 75 |
+
'pydantic.dataclasses:make_dataclass_validator',
|
| 76 |
+
'pydantic.dataclasses:set_validation',
|
| 77 |
+
'pydantic.datetime_parse:parse_date',
|
| 78 |
+
'pydantic.datetime_parse:parse_time',
|
| 79 |
+
'pydantic.datetime_parse:parse_datetime',
|
| 80 |
+
'pydantic.datetime_parse:parse_duration',
|
| 81 |
+
'pydantic.error_wrappers:ErrorWrapper',
|
| 82 |
+
'pydantic.errors:AnyStrMaxLengthError',
|
| 83 |
+
'pydantic.errors:AnyStrMinLengthError',
|
| 84 |
+
'pydantic.errors:ArbitraryTypeError',
|
| 85 |
+
'pydantic.errors:BoolError',
|
| 86 |
+
'pydantic.errors:BytesError',
|
| 87 |
+
'pydantic.errors:CallableError',
|
| 88 |
+
'pydantic.errors:ClassError',
|
| 89 |
+
'pydantic.errors:ColorError',
|
| 90 |
+
'pydantic.errors:ConfigError',
|
| 91 |
+
'pydantic.errors:DataclassTypeError',
|
| 92 |
+
'pydantic.errors:DateError',
|
| 93 |
+
'pydantic.errors:DateNotInTheFutureError',
|
| 94 |
+
'pydantic.errors:DateNotInThePastError',
|
| 95 |
+
'pydantic.errors:DateTimeError',
|
| 96 |
+
'pydantic.errors:DecimalError',
|
| 97 |
+
'pydantic.errors:DecimalIsNotFiniteError',
|
| 98 |
+
'pydantic.errors:DecimalMaxDigitsError',
|
| 99 |
+
'pydantic.errors:DecimalMaxPlacesError',
|
| 100 |
+
'pydantic.errors:DecimalWholeDigitsError',
|
| 101 |
+
'pydantic.errors:DictError',
|
| 102 |
+
'pydantic.errors:DurationError',
|
| 103 |
+
'pydantic.errors:EmailError',
|
| 104 |
+
'pydantic.errors:EnumError',
|
| 105 |
+
'pydantic.errors:EnumMemberError',
|
| 106 |
+
'pydantic.errors:ExtraError',
|
| 107 |
+
'pydantic.errors:FloatError',
|
| 108 |
+
'pydantic.errors:FrozenSetError',
|
| 109 |
+
'pydantic.errors:FrozenSetMaxLengthError',
|
| 110 |
+
'pydantic.errors:FrozenSetMinLengthError',
|
| 111 |
+
'pydantic.errors:HashableError',
|
| 112 |
+
'pydantic.errors:IPv4AddressError',
|
| 113 |
+
'pydantic.errors:IPv4InterfaceError',
|
| 114 |
+
'pydantic.errors:IPv4NetworkError',
|
| 115 |
+
'pydantic.errors:IPv6AddressError',
|
| 116 |
+
'pydantic.errors:IPv6InterfaceError',
|
| 117 |
+
'pydantic.errors:IPv6NetworkError',
|
| 118 |
+
'pydantic.errors:IPvAnyAddressError',
|
| 119 |
+
'pydantic.errors:IPvAnyInterfaceError',
|
| 120 |
+
'pydantic.errors:IPvAnyNetworkError',
|
| 121 |
+
'pydantic.errors:IntEnumError',
|
| 122 |
+
'pydantic.errors:IntegerError',
|
| 123 |
+
'pydantic.errors:InvalidByteSize',
|
| 124 |
+
'pydantic.errors:InvalidByteSizeUnit',
|
| 125 |
+
'pydantic.errors:InvalidDiscriminator',
|
| 126 |
+
'pydantic.errors:InvalidLengthForBrand',
|
| 127 |
+
'pydantic.errors:JsonError',
|
| 128 |
+
'pydantic.errors:JsonTypeError',
|
| 129 |
+
'pydantic.errors:ListError',
|
| 130 |
+
'pydantic.errors:ListMaxLengthError',
|
| 131 |
+
'pydantic.errors:ListMinLengthError',
|
| 132 |
+
'pydantic.errors:ListUniqueItemsError',
|
| 133 |
+
'pydantic.errors:LuhnValidationError',
|
| 134 |
+
'pydantic.errors:MissingDiscriminator',
|
| 135 |
+
'pydantic.errors:MissingError',
|
| 136 |
+
'pydantic.errors:NoneIsAllowedError',
|
| 137 |
+
'pydantic.errors:NoneIsNotAllowedError',
|
| 138 |
+
'pydantic.errors:NotDigitError',
|
| 139 |
+
'pydantic.errors:NotNoneError',
|
| 140 |
+
'pydantic.errors:NumberNotGeError',
|
| 141 |
+
'pydantic.errors:NumberNotGtError',
|
| 142 |
+
'pydantic.errors:NumberNotLeError',
|
| 143 |
+
'pydantic.errors:NumberNotLtError',
|
| 144 |
+
'pydantic.errors:NumberNotMultipleError',
|
| 145 |
+
'pydantic.errors:PathError',
|
| 146 |
+
'pydantic.errors:PathNotADirectoryError',
|
| 147 |
+
'pydantic.errors:PathNotAFileError',
|
| 148 |
+
'pydantic.errors:PathNotExistsError',
|
| 149 |
+
'pydantic.errors:PatternError',
|
| 150 |
+
'pydantic.errors:PyObjectError',
|
| 151 |
+
'pydantic.errors:PydanticTypeError',
|
| 152 |
+
'pydantic.errors:PydanticValueError',
|
| 153 |
+
'pydantic.errors:SequenceError',
|
| 154 |
+
'pydantic.errors:SetError',
|
| 155 |
+
'pydantic.errors:SetMaxLengthError',
|
| 156 |
+
'pydantic.errors:SetMinLengthError',
|
| 157 |
+
'pydantic.errors:StrError',
|
| 158 |
+
'pydantic.errors:StrRegexError',
|
| 159 |
+
'pydantic.errors:StrictBoolError',
|
| 160 |
+
'pydantic.errors:SubclassError',
|
| 161 |
+
'pydantic.errors:TimeError',
|
| 162 |
+
'pydantic.errors:TupleError',
|
| 163 |
+
'pydantic.errors:TupleLengthError',
|
| 164 |
+
'pydantic.errors:UUIDError',
|
| 165 |
+
'pydantic.errors:UUIDVersionError',
|
| 166 |
+
'pydantic.errors:UrlError',
|
| 167 |
+
'pydantic.errors:UrlExtraError',
|
| 168 |
+
'pydantic.errors:UrlHostError',
|
| 169 |
+
'pydantic.errors:UrlHostTldError',
|
| 170 |
+
'pydantic.errors:UrlPortError',
|
| 171 |
+
'pydantic.errors:UrlSchemeError',
|
| 172 |
+
'pydantic.errors:UrlSchemePermittedError',
|
| 173 |
+
'pydantic.errors:UrlUserInfoError',
|
| 174 |
+
'pydantic.errors:WrongConstantError',
|
| 175 |
+
'pydantic.main:validate_model',
|
| 176 |
+
'pydantic.networks:stricturl',
|
| 177 |
+
'pydantic:parse_file_as',
|
| 178 |
+
'pydantic:parse_raw_as',
|
| 179 |
+
'pydantic:stricturl',
|
| 180 |
+
'pydantic.tools:parse_file_as',
|
| 181 |
+
'pydantic.tools:parse_raw_as',
|
| 182 |
+
'pydantic.types:ConstrainedBytes',
|
| 183 |
+
'pydantic.types:ConstrainedDate',
|
| 184 |
+
'pydantic.types:ConstrainedDecimal',
|
| 185 |
+
'pydantic.types:ConstrainedFloat',
|
| 186 |
+
'pydantic.types:ConstrainedFrozenSet',
|
| 187 |
+
'pydantic.types:ConstrainedInt',
|
| 188 |
+
'pydantic.types:ConstrainedList',
|
| 189 |
+
'pydantic.types:ConstrainedSet',
|
| 190 |
+
'pydantic.types:ConstrainedStr',
|
| 191 |
+
'pydantic.types:JsonWrapper',
|
| 192 |
+
'pydantic.types:NoneBytes',
|
| 193 |
+
'pydantic.types:NoneStr',
|
| 194 |
+
'pydantic.types:NoneStrBytes',
|
| 195 |
+
'pydantic.types:StrBytes',
|
| 196 |
+
'pydantic.typing:evaluate_forwardref',
|
| 197 |
+
'pydantic.typing:AbstractSetIntStr',
|
| 198 |
+
'pydantic.typing:AnyCallable',
|
| 199 |
+
'pydantic.typing:AnyClassMethod',
|
| 200 |
+
'pydantic.typing:CallableGenerator',
|
| 201 |
+
'pydantic.typing:DictAny',
|
| 202 |
+
'pydantic.typing:DictIntStrAny',
|
| 203 |
+
'pydantic.typing:DictStrAny',
|
| 204 |
+
'pydantic.typing:IntStr',
|
| 205 |
+
'pydantic.typing:ListStr',
|
| 206 |
+
'pydantic.typing:MappingIntStrAny',
|
| 207 |
+
'pydantic.typing:NoArgAnyCallable',
|
| 208 |
+
'pydantic.typing:NoneType',
|
| 209 |
+
'pydantic.typing:ReprArgs',
|
| 210 |
+
'pydantic.typing:SetStr',
|
| 211 |
+
'pydantic.typing:StrPath',
|
| 212 |
+
'pydantic.typing:TupleGenerator',
|
| 213 |
+
'pydantic.typing:WithArgsTypes',
|
| 214 |
+
'pydantic.typing:all_literal_values',
|
| 215 |
+
'pydantic.typing:display_as_type',
|
| 216 |
+
'pydantic.typing:get_all_type_hints',
|
| 217 |
+
'pydantic.typing:get_args',
|
| 218 |
+
'pydantic.typing:get_origin',
|
| 219 |
+
'pydantic.typing:get_sub_types',
|
| 220 |
+
'pydantic.typing:is_callable_type',
|
| 221 |
+
'pydantic.typing:is_classvar',
|
| 222 |
+
'pydantic.typing:is_finalvar',
|
| 223 |
+
'pydantic.typing:is_literal_type',
|
| 224 |
+
'pydantic.typing:is_namedtuple',
|
| 225 |
+
'pydantic.typing:is_new_type',
|
| 226 |
+
'pydantic.typing:is_none_type',
|
| 227 |
+
'pydantic.typing:is_typeddict',
|
| 228 |
+
'pydantic.typing:is_typeddict_special',
|
| 229 |
+
'pydantic.typing:is_union',
|
| 230 |
+
'pydantic.typing:new_type_supertype',
|
| 231 |
+
'pydantic.typing:resolve_annotations',
|
| 232 |
+
'pydantic.typing:typing_base',
|
| 233 |
+
'pydantic.typing:update_field_forward_refs',
|
| 234 |
+
'pydantic.typing:update_model_forward_refs',
|
| 235 |
+
'pydantic.utils:ClassAttribute',
|
| 236 |
+
'pydantic.utils:DUNDER_ATTRIBUTES',
|
| 237 |
+
'pydantic.utils:PyObjectStr',
|
| 238 |
+
'pydantic.utils:ValueItems',
|
| 239 |
+
'pydantic.utils:almost_equal_floats',
|
| 240 |
+
'pydantic.utils:get_discriminator_alias_and_values',
|
| 241 |
+
'pydantic.utils:get_model',
|
| 242 |
+
'pydantic.utils:get_unique_discriminator_alias',
|
| 243 |
+
'pydantic.utils:in_ipython',
|
| 244 |
+
'pydantic.utils:is_valid_identifier',
|
| 245 |
+
'pydantic.utils:path_type',
|
| 246 |
+
'pydantic.utils:validate_field_name',
|
| 247 |
+
'pydantic:validate_model',
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def getattr_migration(module: str) -> Callable[[str], Any]:
|
| 252 |
+
"""Implement PEP 562 for objects that were either moved or removed on the migration
|
| 253 |
+
to V2.
|
| 254 |
+
|
| 255 |
+
Args:
|
| 256 |
+
module: The module name.
|
| 257 |
+
|
| 258 |
+
Returns:
|
| 259 |
+
A callable that will raise an error if the object is not found.
|
| 260 |
+
"""
|
| 261 |
+
# This avoids circular import with errors.py.
|
| 262 |
+
from .errors import PydanticImportError
|
| 263 |
+
|
| 264 |
+
def wrapper(name: str) -> object:
|
| 265 |
+
"""Raise an error if the object is not found, or warn if it was moved.
|
| 266 |
+
|
| 267 |
+
In case it was moved, it still returns the object.
|
| 268 |
+
|
| 269 |
+
Args:
|
| 270 |
+
name: The object name.
|
| 271 |
+
|
| 272 |
+
Returns:
|
| 273 |
+
The object.
|
| 274 |
+
"""
|
| 275 |
+
if name == '__path__':
|
| 276 |
+
raise AttributeError(f'module {module!r} has no attribute {name!r}')
|
| 277 |
+
|
| 278 |
+
import warnings
|
| 279 |
+
|
| 280 |
+
from ._internal._validators import import_string
|
| 281 |
+
|
| 282 |
+
import_path = f'{module}:{name}'
|
| 283 |
+
if import_path in MOVED_IN_V2.keys():
|
| 284 |
+
new_location = MOVED_IN_V2[import_path]
|
| 285 |
+
warnings.warn(
|
| 286 |
+
f'`{import_path}` has been moved to `{new_location}`.',
|
| 287 |
+
category=PydanticDeprecatedSince20,
|
| 288 |
+
stacklevel=2,
|
| 289 |
+
)
|
| 290 |
+
return import_string(MOVED_IN_V2[import_path])
|
| 291 |
+
if import_path in DEPRECATED_MOVED_IN_V2:
|
| 292 |
+
# skip the warning here because a deprecation warning will be raised elsewhere
|
| 293 |
+
return import_string(DEPRECATED_MOVED_IN_V2[import_path])
|
| 294 |
+
if import_path in REDIRECT_TO_V1:
|
| 295 |
+
new_location = REDIRECT_TO_V1[import_path]
|
| 296 |
+
warnings.warn(
|
| 297 |
+
f'`{import_path}` has been removed. We are importing from `{new_location}` instead.'
|
| 298 |
+
'See the migration guide for more details: https://docs.pydantic.dev/latest/migration/',
|
| 299 |
+
category=PydanticDeprecatedSince20,
|
| 300 |
+
stacklevel=2,
|
| 301 |
+
)
|
| 302 |
+
return import_string(REDIRECT_TO_V1[import_path])
|
| 303 |
+
if import_path == 'pydantic:BaseSettings':
|
| 304 |
+
raise PydanticImportError(
|
| 305 |
+
'`BaseSettings` has been moved to the `pydantic-settings` package. '
|
| 306 |
+
f'See https://docs.pydantic.dev/{version_short()}/migration/#basesettings-has-moved-to-pydantic-settings '
|
| 307 |
+
'for more details.'
|
| 308 |
+
)
|
| 309 |
+
if import_path in REMOVED_IN_V2:
|
| 310 |
+
raise PydanticImportError(f'`{import_path}` has been removed in V2.')
|
| 311 |
+
globals: dict[str, Any] = sys.modules[module].__dict__
|
| 312 |
+
if name in globals:
|
| 313 |
+
return globals[name]
|
| 314 |
+
raise AttributeError(f'module {module!r} has no attribute {name!r}')
|
| 315 |
+
|
| 316 |
+
return wrapper
|
.mcp-venv/lib/python3.13/site-packages/pydantic/alias_generators.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Alias generators for converting between different capitalization conventions."""
|
| 2 |
+
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
__all__ = ('to_pascal', 'to_camel', 'to_snake')
|
| 6 |
+
|
| 7 |
+
# TODO: in V3, change the argument names to be more descriptive
|
| 8 |
+
# Generally, don't only convert from snake_case, or name the functions
|
| 9 |
+
# more specifically like snake_to_camel.
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def to_pascal(snake: str) -> str:
|
| 13 |
+
"""Convert a snake_case string to PascalCase.
|
| 14 |
+
|
| 15 |
+
Args:
|
| 16 |
+
snake: The string to convert.
|
| 17 |
+
|
| 18 |
+
Returns:
|
| 19 |
+
The PascalCase string.
|
| 20 |
+
"""
|
| 21 |
+
camel = snake.title()
|
| 22 |
+
return re.sub('([0-9A-Za-z])_(?=[0-9A-Z])', lambda m: m.group(1), camel)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def to_camel(snake: str) -> str:
|
| 26 |
+
"""Convert a snake_case string to camelCase.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
snake: The string to convert.
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
The converted camelCase string.
|
| 33 |
+
"""
|
| 34 |
+
# If the string is already in camelCase and does not contain a digit followed
|
| 35 |
+
# by a lowercase letter, return it as it is
|
| 36 |
+
if re.match('^[a-z]+[A-Za-z0-9]*$', snake) and not re.search(r'\d[a-z]', snake):
|
| 37 |
+
return snake
|
| 38 |
+
|
| 39 |
+
camel = to_pascal(snake)
|
| 40 |
+
return re.sub('(^_*[A-Z])', lambda m: m.group(1).lower(), camel)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def to_snake(camel: str) -> str:
|
| 44 |
+
"""Convert a PascalCase, camelCase, or kebab-case string to snake_case.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
camel: The string to convert.
|
| 48 |
+
|
| 49 |
+
Returns:
|
| 50 |
+
The converted string in snake_case.
|
| 51 |
+
"""
|
| 52 |
+
# Handle the sequence of uppercase letters followed by a lowercase letter
|
| 53 |
+
snake = re.sub(r'([A-Z]+)([A-Z][a-z])', lambda m: f'{m.group(1)}_{m.group(2)}', camel)
|
| 54 |
+
# Insert an underscore between a lowercase letter and an uppercase letter
|
| 55 |
+
snake = re.sub(r'([a-z])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake)
|
| 56 |
+
# Insert an underscore between a digit and an uppercase letter
|
| 57 |
+
snake = re.sub(r'([0-9])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake)
|
| 58 |
+
# Insert an underscore between a lowercase letter and a digit
|
| 59 |
+
snake = re.sub(r'([a-z])([0-9])', lambda m: f'{m.group(1)}_{m.group(2)}', snake)
|
| 60 |
+
# Replace hyphens with underscores to handle kebab-case
|
| 61 |
+
snake = snake.replace('-', '_')
|
| 62 |
+
return snake.lower()
|
.mcp-venv/lib/python3.13/site-packages/pydantic/aliases.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Support for alias configurations."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
from typing import Any, Callable, Literal
|
| 7 |
+
|
| 8 |
+
from pydantic_core import PydanticUndefined
|
| 9 |
+
|
| 10 |
+
from ._internal import _internal_dataclass
|
| 11 |
+
|
| 12 |
+
__all__ = ('AliasGenerator', 'AliasPath', 'AliasChoices')
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 16 |
+
class AliasPath:
|
| 17 |
+
"""!!! abstract "Usage Documentation"
|
| 18 |
+
[`AliasPath` and `AliasChoices`](../concepts/alias.md#aliaspath-and-aliaschoices)
|
| 19 |
+
|
| 20 |
+
A data class used by `validation_alias` as a convenience to create aliases.
|
| 21 |
+
|
| 22 |
+
Attributes:
|
| 23 |
+
path: A list of string or integer aliases.
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
path: list[int | str]
|
| 27 |
+
|
| 28 |
+
def __init__(self, first_arg: str, *args: str | int) -> None:
|
| 29 |
+
self.path = [first_arg] + list(args)
|
| 30 |
+
|
| 31 |
+
def convert_to_aliases(self) -> list[str | int]:
|
| 32 |
+
"""Converts arguments to a list of string or integer aliases.
|
| 33 |
+
|
| 34 |
+
Returns:
|
| 35 |
+
The list of aliases.
|
| 36 |
+
"""
|
| 37 |
+
return self.path
|
| 38 |
+
|
| 39 |
+
def search_dict_for_path(self, d: dict) -> Any:
|
| 40 |
+
"""Searches a dictionary for the path specified by the alias.
|
| 41 |
+
|
| 42 |
+
Returns:
|
| 43 |
+
The value at the specified path, or `PydanticUndefined` if the path is not found.
|
| 44 |
+
"""
|
| 45 |
+
v = d
|
| 46 |
+
for k in self.path:
|
| 47 |
+
if isinstance(v, str):
|
| 48 |
+
# disallow indexing into a str, like for AliasPath('x', 0) and x='abc'
|
| 49 |
+
return PydanticUndefined
|
| 50 |
+
try:
|
| 51 |
+
v = v[k]
|
| 52 |
+
except (KeyError, IndexError, TypeError):
|
| 53 |
+
return PydanticUndefined
|
| 54 |
+
return v
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 58 |
+
class AliasChoices:
|
| 59 |
+
"""!!! abstract "Usage Documentation"
|
| 60 |
+
[`AliasPath` and `AliasChoices`](../concepts/alias.md#aliaspath-and-aliaschoices)
|
| 61 |
+
|
| 62 |
+
A data class used by `validation_alias` as a convenience to create aliases.
|
| 63 |
+
|
| 64 |
+
Attributes:
|
| 65 |
+
choices: A list containing a string or `AliasPath`.
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
choices: list[str | AliasPath]
|
| 69 |
+
|
| 70 |
+
def __init__(self, first_choice: str | AliasPath, *choices: str | AliasPath) -> None:
|
| 71 |
+
self.choices = [first_choice] + list(choices)
|
| 72 |
+
|
| 73 |
+
def convert_to_aliases(self) -> list[list[str | int]]:
|
| 74 |
+
"""Converts arguments to a list of lists containing string or integer aliases.
|
| 75 |
+
|
| 76 |
+
Returns:
|
| 77 |
+
The list of aliases.
|
| 78 |
+
"""
|
| 79 |
+
aliases: list[list[str | int]] = []
|
| 80 |
+
for c in self.choices:
|
| 81 |
+
if isinstance(c, AliasPath):
|
| 82 |
+
aliases.append(c.convert_to_aliases())
|
| 83 |
+
else:
|
| 84 |
+
aliases.append([c])
|
| 85 |
+
return aliases
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 89 |
+
class AliasGenerator:
|
| 90 |
+
"""!!! abstract "Usage Documentation"
|
| 91 |
+
[Using an `AliasGenerator`](../concepts/alias.md#using-an-aliasgenerator)
|
| 92 |
+
|
| 93 |
+
A data class used by `alias_generator` as a convenience to create various aliases.
|
| 94 |
+
|
| 95 |
+
Attributes:
|
| 96 |
+
alias: A callable that takes a field name and returns an alias for it.
|
| 97 |
+
validation_alias: A callable that takes a field name and returns a validation alias for it.
|
| 98 |
+
serialization_alias: A callable that takes a field name and returns a serialization alias for it.
|
| 99 |
+
"""
|
| 100 |
+
|
| 101 |
+
alias: Callable[[str], str] | None = None
|
| 102 |
+
validation_alias: Callable[[str], str | AliasPath | AliasChoices] | None = None
|
| 103 |
+
serialization_alias: Callable[[str], str] | None = None
|
| 104 |
+
|
| 105 |
+
def _generate_alias(
|
| 106 |
+
self,
|
| 107 |
+
alias_kind: Literal['alias', 'validation_alias', 'serialization_alias'],
|
| 108 |
+
allowed_types: tuple[type[str] | type[AliasPath] | type[AliasChoices], ...],
|
| 109 |
+
field_name: str,
|
| 110 |
+
) -> str | AliasPath | AliasChoices | None:
|
| 111 |
+
"""Generate an alias of the specified kind. Returns None if the alias generator is None.
|
| 112 |
+
|
| 113 |
+
Raises:
|
| 114 |
+
TypeError: If the alias generator produces an invalid type.
|
| 115 |
+
"""
|
| 116 |
+
alias = None
|
| 117 |
+
if alias_generator := getattr(self, alias_kind):
|
| 118 |
+
alias = alias_generator(field_name)
|
| 119 |
+
if alias and not isinstance(alias, allowed_types):
|
| 120 |
+
raise TypeError(
|
| 121 |
+
f'Invalid `{alias_kind}` type. `{alias_kind}` generator must produce one of `{allowed_types}`'
|
| 122 |
+
)
|
| 123 |
+
return alias
|
| 124 |
+
|
| 125 |
+
def generate_aliases(self, field_name: str) -> tuple[str | None, str | AliasPath | AliasChoices | None, str | None]:
|
| 126 |
+
"""Generate `alias`, `validation_alias`, and `serialization_alias` for a field.
|
| 127 |
+
|
| 128 |
+
Returns:
|
| 129 |
+
A tuple of three aliases - validation, alias, and serialization.
|
| 130 |
+
"""
|
| 131 |
+
alias = self._generate_alias('alias', (str,), field_name)
|
| 132 |
+
validation_alias = self._generate_alias('validation_alias', (str, AliasChoices, AliasPath), field_name)
|
| 133 |
+
serialization_alias = self._generate_alias('serialization_alias', (str,), field_name)
|
| 134 |
+
|
| 135 |
+
return alias, validation_alias, serialization_alias # type: ignore
|
.mcp-venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Type annotations to use with `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__`."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING, Any, Union
|
| 6 |
+
|
| 7 |
+
from pydantic_core import core_schema
|
| 8 |
+
|
| 9 |
+
if TYPE_CHECKING:
|
| 10 |
+
from ._internal._namespace_utils import NamespacesTuple
|
| 11 |
+
from .json_schema import JsonSchemaMode, JsonSchemaValue
|
| 12 |
+
|
| 13 |
+
CoreSchemaOrField = Union[
|
| 14 |
+
core_schema.CoreSchema,
|
| 15 |
+
core_schema.ModelField,
|
| 16 |
+
core_schema.DataclassField,
|
| 17 |
+
core_schema.TypedDictField,
|
| 18 |
+
core_schema.ComputedField,
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
__all__ = 'GetJsonSchemaHandler', 'GetCoreSchemaHandler'
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class GetJsonSchemaHandler:
|
| 25 |
+
"""Handler to call into the next JSON schema generation function.
|
| 26 |
+
|
| 27 |
+
Attributes:
|
| 28 |
+
mode: Json schema mode, can be `validation` or `serialization`.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
mode: JsonSchemaMode
|
| 32 |
+
|
| 33 |
+
def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:
|
| 34 |
+
"""Call the inner handler and get the JsonSchemaValue it returns.
|
| 35 |
+
This will call the next JSON schema modifying function up until it calls
|
| 36 |
+
into `pydantic.json_schema.GenerateJsonSchema`, which will raise a
|
| 37 |
+
`pydantic.errors.PydanticInvalidForJsonSchema` error if it cannot generate
|
| 38 |
+
a JSON schema.
|
| 39 |
+
|
| 40 |
+
Args:
|
| 41 |
+
core_schema: A `pydantic_core.core_schema.CoreSchema`.
|
| 42 |
+
|
| 43 |
+
Returns:
|
| 44 |
+
JsonSchemaValue: The JSON schema generated by the inner JSON schema modify
|
| 45 |
+
functions.
|
| 46 |
+
"""
|
| 47 |
+
raise NotImplementedError
|
| 48 |
+
|
| 49 |
+
def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue, /) -> JsonSchemaValue:
|
| 50 |
+
"""Get the real schema for a `{"$ref": ...}` schema.
|
| 51 |
+
If the schema given is not a `$ref` schema, it will be returned as is.
|
| 52 |
+
This means you don't have to check before calling this function.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
maybe_ref_json_schema: A JsonSchemaValue which may be a `$ref` schema.
|
| 56 |
+
|
| 57 |
+
Raises:
|
| 58 |
+
LookupError: If the ref is not found.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
JsonSchemaValue: A JsonSchemaValue that has no `$ref`.
|
| 62 |
+
"""
|
| 63 |
+
raise NotImplementedError
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class GetCoreSchemaHandler:
|
| 67 |
+
"""Handler to call into the next CoreSchema schema generation function."""
|
| 68 |
+
|
| 69 |
+
def __call__(self, source_type: Any, /) -> core_schema.CoreSchema:
|
| 70 |
+
"""Call the inner handler and get the CoreSchema it returns.
|
| 71 |
+
This will call the next CoreSchema modifying function up until it calls
|
| 72 |
+
into Pydantic's internal schema generation machinery, which will raise a
|
| 73 |
+
`pydantic.errors.PydanticSchemaGenerationError` error if it cannot generate
|
| 74 |
+
a CoreSchema for the given source type.
|
| 75 |
+
|
| 76 |
+
Args:
|
| 77 |
+
source_type: The input type.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
CoreSchema: The `pydantic-core` CoreSchema generated.
|
| 81 |
+
"""
|
| 82 |
+
raise NotImplementedError
|
| 83 |
+
|
| 84 |
+
def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema:
|
| 85 |
+
"""Generate a schema unrelated to the current context.
|
| 86 |
+
Use this function if e.g. you are handling schema generation for a sequence
|
| 87 |
+
and want to generate a schema for its items.
|
| 88 |
+
Otherwise, you may end up doing something like applying a `min_length` constraint
|
| 89 |
+
that was intended for the sequence itself to its items!
|
| 90 |
+
|
| 91 |
+
Args:
|
| 92 |
+
source_type: The input type.
|
| 93 |
+
|
| 94 |
+
Returns:
|
| 95 |
+
CoreSchema: The `pydantic-core` CoreSchema generated.
|
| 96 |
+
"""
|
| 97 |
+
raise NotImplementedError
|
| 98 |
+
|
| 99 |
+
def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema, /) -> core_schema.CoreSchema:
|
| 100 |
+
"""Get the real schema for a `definition-ref` schema.
|
| 101 |
+
If the schema given is not a `definition-ref` schema, it will be returned as is.
|
| 102 |
+
This means you don't have to check before calling this function.
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
maybe_ref_schema: A `CoreSchema`, `ref`-based or not.
|
| 106 |
+
|
| 107 |
+
Raises:
|
| 108 |
+
LookupError: If the `ref` is not found.
|
| 109 |
+
|
| 110 |
+
Returns:
|
| 111 |
+
A concrete `CoreSchema`.
|
| 112 |
+
"""
|
| 113 |
+
raise NotImplementedError
|
| 114 |
+
|
| 115 |
+
@property
|
| 116 |
+
def field_name(self) -> str | None:
|
| 117 |
+
"""Get the name of the closest field to this validator."""
|
| 118 |
+
raise NotImplementedError
|
| 119 |
+
|
| 120 |
+
def _get_types_namespace(self) -> NamespacesTuple:
|
| 121 |
+
"""Internal method used during type resolution for serializer annotations."""
|
| 122 |
+
raise NotImplementedError
|
.mcp-venv/lib/python3.13/site-packages/pydantic/class_validators.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""`class_validators` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/color.py
ADDED
|
@@ -0,0 +1,604 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Color definitions are used as per the CSS3
|
| 2 |
+
[CSS Color Module Level 3](http://www.w3.org/TR/css3-color/#svg-color) specification.
|
| 3 |
+
|
| 4 |
+
A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`.
|
| 5 |
+
|
| 6 |
+
In these cases the _last_ color when sorted alphabetically takes preferences,
|
| 7 |
+
eg. `Color((0, 255, 255)).as_named() == 'cyan'` because "cyan" comes after "aqua".
|
| 8 |
+
|
| 9 |
+
Warning: Deprecated
|
| 10 |
+
The `Color` class is deprecated, use `pydantic_extra_types` instead.
|
| 11 |
+
See [`pydantic-extra-types.Color`](../usage/types/extra_types/color_types.md)
|
| 12 |
+
for more information.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import math
|
| 16 |
+
import re
|
| 17 |
+
from colorsys import hls_to_rgb, rgb_to_hls
|
| 18 |
+
from typing import Any, Callable, Optional, Union, cast
|
| 19 |
+
|
| 20 |
+
from pydantic_core import CoreSchema, PydanticCustomError, core_schema
|
| 21 |
+
from typing_extensions import deprecated
|
| 22 |
+
|
| 23 |
+
from ._internal import _repr
|
| 24 |
+
from ._internal._schema_generation_shared import GetJsonSchemaHandler as _GetJsonSchemaHandler
|
| 25 |
+
from .json_schema import JsonSchemaValue
|
| 26 |
+
from .warnings import PydanticDeprecatedSince20
|
| 27 |
+
|
| 28 |
+
ColorTuple = Union[tuple[int, int, int], tuple[int, int, int, float]]
|
| 29 |
+
ColorType = Union[ColorTuple, str]
|
| 30 |
+
HslColorTuple = Union[tuple[float, float, float], tuple[float, float, float, float]]
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class RGBA:
|
| 34 |
+
"""Internal use only as a representation of a color."""
|
| 35 |
+
|
| 36 |
+
__slots__ = 'r', 'g', 'b', 'alpha', '_tuple'
|
| 37 |
+
|
| 38 |
+
def __init__(self, r: float, g: float, b: float, alpha: Optional[float]):
|
| 39 |
+
self.r = r
|
| 40 |
+
self.g = g
|
| 41 |
+
self.b = b
|
| 42 |
+
self.alpha = alpha
|
| 43 |
+
|
| 44 |
+
self._tuple: tuple[float, float, float, Optional[float]] = (r, g, b, alpha)
|
| 45 |
+
|
| 46 |
+
def __getitem__(self, item: Any) -> Any:
|
| 47 |
+
return self._tuple[item]
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached
|
| 51 |
+
_r_255 = r'(\d{1,3}(?:\.\d+)?)'
|
| 52 |
+
_r_comma = r'\s*,\s*'
|
| 53 |
+
_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)'
|
| 54 |
+
_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?'
|
| 55 |
+
_r_sl = r'(\d{1,3}(?:\.\d+)?)%'
|
| 56 |
+
r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*'
|
| 57 |
+
r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*'
|
| 58 |
+
# CSS3 RGB examples: rgb(0, 0, 0), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 50%)
|
| 59 |
+
r_rgb = rf'\s*rgba?\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}(?:{_r_comma}{_r_alpha})?\s*\)\s*'
|
| 60 |
+
# CSS3 HSL examples: hsl(270, 60%, 50%), hsla(270, 60%, 50%, 0.5), hsla(270, 60%, 50%, 50%)
|
| 61 |
+
r_hsl = rf'\s*hsla?\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}(?:{_r_comma}{_r_alpha})?\s*\)\s*'
|
| 62 |
+
# CSS4 RGB examples: rgb(0 0 0), rgb(0 0 0 / 0.5), rgb(0 0 0 / 50%), rgba(0 0 0 / 50%)
|
| 63 |
+
r_rgb_v4_style = rf'\s*rgba?\(\s*{_r_255}\s+{_r_255}\s+{_r_255}(?:\s*/\s*{_r_alpha})?\s*\)\s*'
|
| 64 |
+
# CSS4 HSL examples: hsl(270 60% 50%), hsl(270 60% 50% / 0.5), hsl(270 60% 50% / 50%), hsla(270 60% 50% / 50%)
|
| 65 |
+
r_hsl_v4_style = rf'\s*hsla?\(\s*{_r_h}\s+{_r_sl}\s+{_r_sl}(?:\s*/\s*{_r_alpha})?\s*\)\s*'
|
| 66 |
+
|
| 67 |
+
# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used
|
| 68 |
+
repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'}
|
| 69 |
+
rads = 2 * math.pi
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@deprecated(
|
| 73 |
+
'The `Color` class is deprecated, use `pydantic_extra_types` instead. '
|
| 74 |
+
'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/.',
|
| 75 |
+
category=PydanticDeprecatedSince20,
|
| 76 |
+
)
|
| 77 |
+
class Color(_repr.Representation):
|
| 78 |
+
"""Represents a color."""
|
| 79 |
+
|
| 80 |
+
__slots__ = '_original', '_rgba'
|
| 81 |
+
|
| 82 |
+
def __init__(self, value: ColorType) -> None:
|
| 83 |
+
self._rgba: RGBA
|
| 84 |
+
self._original: ColorType
|
| 85 |
+
if isinstance(value, (tuple, list)):
|
| 86 |
+
self._rgba = parse_tuple(value)
|
| 87 |
+
elif isinstance(value, str):
|
| 88 |
+
self._rgba = parse_str(value)
|
| 89 |
+
elif isinstance(value, Color):
|
| 90 |
+
self._rgba = value._rgba
|
| 91 |
+
value = value._original
|
| 92 |
+
else:
|
| 93 |
+
raise PydanticCustomError(
|
| 94 |
+
'color_error', 'value is not a valid color: value must be a tuple, list or string'
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
# if we've got here value must be a valid color
|
| 98 |
+
self._original = value
|
| 99 |
+
|
| 100 |
+
@classmethod
|
| 101 |
+
def __get_pydantic_json_schema__(
|
| 102 |
+
cls, core_schema: core_schema.CoreSchema, handler: _GetJsonSchemaHandler
|
| 103 |
+
) -> JsonSchemaValue:
|
| 104 |
+
field_schema = {}
|
| 105 |
+
field_schema.update(type='string', format='color')
|
| 106 |
+
return field_schema
|
| 107 |
+
|
| 108 |
+
def original(self) -> ColorType:
|
| 109 |
+
"""Original value passed to `Color`."""
|
| 110 |
+
return self._original
|
| 111 |
+
|
| 112 |
+
def as_named(self, *, fallback: bool = False) -> str:
|
| 113 |
+
"""Returns the name of the color if it can be found in `COLORS_BY_VALUE` dictionary,
|
| 114 |
+
otherwise returns the hexadecimal representation of the color or raises `ValueError`.
|
| 115 |
+
|
| 116 |
+
Args:
|
| 117 |
+
fallback: If True, falls back to returning the hexadecimal representation of
|
| 118 |
+
the color instead of raising a ValueError when no named color is found.
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
The name of the color, or the hexadecimal representation of the color.
|
| 122 |
+
|
| 123 |
+
Raises:
|
| 124 |
+
ValueError: When no named color is found and fallback is `False`.
|
| 125 |
+
"""
|
| 126 |
+
if self._rgba.alpha is None:
|
| 127 |
+
rgb = cast(tuple[int, int, int], self.as_rgb_tuple())
|
| 128 |
+
try:
|
| 129 |
+
return COLORS_BY_VALUE[rgb]
|
| 130 |
+
except KeyError as e:
|
| 131 |
+
if fallback:
|
| 132 |
+
return self.as_hex()
|
| 133 |
+
else:
|
| 134 |
+
raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e
|
| 135 |
+
else:
|
| 136 |
+
return self.as_hex()
|
| 137 |
+
|
| 138 |
+
def as_hex(self) -> str:
|
| 139 |
+
"""Returns the hexadecimal representation of the color.
|
| 140 |
+
|
| 141 |
+
Hex string representing the color can be 3, 4, 6, or 8 characters depending on whether the string
|
| 142 |
+
a "short" representation of the color is possible and whether there's an alpha channel.
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
The hexadecimal representation of the color.
|
| 146 |
+
"""
|
| 147 |
+
values = [float_to_255(c) for c in self._rgba[:3]]
|
| 148 |
+
if self._rgba.alpha is not None:
|
| 149 |
+
values.append(float_to_255(self._rgba.alpha))
|
| 150 |
+
|
| 151 |
+
as_hex = ''.join(f'{v:02x}' for v in values)
|
| 152 |
+
if all(c in repeat_colors for c in values):
|
| 153 |
+
as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2))
|
| 154 |
+
return '#' + as_hex
|
| 155 |
+
|
| 156 |
+
def as_rgb(self) -> str:
|
| 157 |
+
"""Color as an `rgb(<r>, <g>, <b>)` or `rgba(<r>, <g>, <b>, <a>)` string."""
|
| 158 |
+
if self._rgba.alpha is None:
|
| 159 |
+
return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})'
|
| 160 |
+
else:
|
| 161 |
+
return (
|
| 162 |
+
f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, '
|
| 163 |
+
f'{round(self._alpha_float(), 2)})'
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple:
|
| 167 |
+
"""Returns the color as an RGB or RGBA tuple.
|
| 168 |
+
|
| 169 |
+
Args:
|
| 170 |
+
alpha: Whether to include the alpha channel. There are three options for this input:
|
| 171 |
+
|
| 172 |
+
- `None` (default): Include alpha only if it's set. (e.g. not `None`)
|
| 173 |
+
- `True`: Always include alpha.
|
| 174 |
+
- `False`: Always omit alpha.
|
| 175 |
+
|
| 176 |
+
Returns:
|
| 177 |
+
A tuple that contains the values of the red, green, and blue channels in the range 0 to 255.
|
| 178 |
+
If alpha is included, it is in the range 0 to 1.
|
| 179 |
+
"""
|
| 180 |
+
r, g, b = (float_to_255(c) for c in self._rgba[:3])
|
| 181 |
+
if alpha is None:
|
| 182 |
+
if self._rgba.alpha is None:
|
| 183 |
+
return r, g, b
|
| 184 |
+
else:
|
| 185 |
+
return r, g, b, self._alpha_float()
|
| 186 |
+
elif alpha:
|
| 187 |
+
return r, g, b, self._alpha_float()
|
| 188 |
+
else:
|
| 189 |
+
# alpha is False
|
| 190 |
+
return r, g, b
|
| 191 |
+
|
| 192 |
+
def as_hsl(self) -> str:
|
| 193 |
+
"""Color as an `hsl(<h>, <s>, <l>)` or `hsl(<h>, <s>, <l>, <a>)` string."""
|
| 194 |
+
if self._rgba.alpha is None:
|
| 195 |
+
h, s, li = self.as_hsl_tuple(alpha=False) # type: ignore
|
| 196 |
+
return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})'
|
| 197 |
+
else:
|
| 198 |
+
h, s, li, a = self.as_hsl_tuple(alpha=True) # type: ignore
|
| 199 |
+
return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})'
|
| 200 |
+
|
| 201 |
+
def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple:
|
| 202 |
+
"""Returns the color as an HSL or HSLA tuple.
|
| 203 |
+
|
| 204 |
+
Args:
|
| 205 |
+
alpha: Whether to include the alpha channel.
|
| 206 |
+
|
| 207 |
+
- `None` (default): Include the alpha channel only if it's set (e.g. not `None`).
|
| 208 |
+
- `True`: Always include alpha.
|
| 209 |
+
- `False`: Always omit alpha.
|
| 210 |
+
|
| 211 |
+
Returns:
|
| 212 |
+
The color as a tuple of hue, saturation, lightness, and alpha (if included).
|
| 213 |
+
All elements are in the range 0 to 1.
|
| 214 |
+
|
| 215 |
+
Note:
|
| 216 |
+
This is HSL as used in HTML and most other places, not HLS as used in Python's `colorsys`.
|
| 217 |
+
"""
|
| 218 |
+
h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b) # noqa: E741
|
| 219 |
+
if alpha is None:
|
| 220 |
+
if self._rgba.alpha is None:
|
| 221 |
+
return h, s, l
|
| 222 |
+
else:
|
| 223 |
+
return h, s, l, self._alpha_float()
|
| 224 |
+
if alpha:
|
| 225 |
+
return h, s, l, self._alpha_float()
|
| 226 |
+
else:
|
| 227 |
+
# alpha is False
|
| 228 |
+
return h, s, l
|
| 229 |
+
|
| 230 |
+
def _alpha_float(self) -> float:
|
| 231 |
+
return 1 if self._rgba.alpha is None else self._rgba.alpha
|
| 232 |
+
|
| 233 |
+
@classmethod
|
| 234 |
+
def __get_pydantic_core_schema__(
|
| 235 |
+
cls, source: type[Any], handler: Callable[[Any], CoreSchema]
|
| 236 |
+
) -> core_schema.CoreSchema:
|
| 237 |
+
return core_schema.with_info_plain_validator_function(
|
| 238 |
+
cls._validate, serialization=core_schema.to_string_ser_schema()
|
| 239 |
+
)
|
| 240 |
+
|
| 241 |
+
@classmethod
|
| 242 |
+
def _validate(cls, __input_value: Any, _: Any) -> 'Color':
|
| 243 |
+
return cls(__input_value)
|
| 244 |
+
|
| 245 |
+
def __str__(self) -> str:
|
| 246 |
+
return self.as_named(fallback=True)
|
| 247 |
+
|
| 248 |
+
def __repr_args__(self) -> '_repr.ReprArgs':
|
| 249 |
+
return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())]
|
| 250 |
+
|
| 251 |
+
def __eq__(self, other: Any) -> bool:
|
| 252 |
+
return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple()
|
| 253 |
+
|
| 254 |
+
def __hash__(self) -> int:
|
| 255 |
+
return hash(self.as_rgb_tuple())
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def parse_tuple(value: tuple[Any, ...]) -> RGBA:
|
| 259 |
+
"""Parse a tuple or list to get RGBA values.
|
| 260 |
+
|
| 261 |
+
Args:
|
| 262 |
+
value: A tuple or list.
|
| 263 |
+
|
| 264 |
+
Returns:
|
| 265 |
+
An `RGBA` tuple parsed from the input tuple.
|
| 266 |
+
|
| 267 |
+
Raises:
|
| 268 |
+
PydanticCustomError: If tuple is not valid.
|
| 269 |
+
"""
|
| 270 |
+
if len(value) == 3:
|
| 271 |
+
r, g, b = (parse_color_value(v) for v in value)
|
| 272 |
+
return RGBA(r, g, b, None)
|
| 273 |
+
elif len(value) == 4:
|
| 274 |
+
r, g, b = (parse_color_value(v) for v in value[:3])
|
| 275 |
+
return RGBA(r, g, b, parse_float_alpha(value[3]))
|
| 276 |
+
else:
|
| 277 |
+
raise PydanticCustomError('color_error', 'value is not a valid color: tuples must have length 3 or 4')
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
def parse_str(value: str) -> RGBA:
|
| 281 |
+
"""Parse a string representing a color to an RGBA tuple.
|
| 282 |
+
|
| 283 |
+
Possible formats for the input string include:
|
| 284 |
+
|
| 285 |
+
* named color, see `COLORS_BY_NAME`
|
| 286 |
+
* hex short eg. `<prefix>fff` (prefix can be `#`, `0x` or nothing)
|
| 287 |
+
* hex long eg. `<prefix>ffffff` (prefix can be `#`, `0x` or nothing)
|
| 288 |
+
* `rgb(<r>, <g>, <b>)`
|
| 289 |
+
* `rgba(<r>, <g>, <b>, <a>)`
|
| 290 |
+
|
| 291 |
+
Args:
|
| 292 |
+
value: A string representing a color.
|
| 293 |
+
|
| 294 |
+
Returns:
|
| 295 |
+
An `RGBA` tuple parsed from the input string.
|
| 296 |
+
|
| 297 |
+
Raises:
|
| 298 |
+
ValueError: If the input string cannot be parsed to an RGBA tuple.
|
| 299 |
+
"""
|
| 300 |
+
value_lower = value.lower()
|
| 301 |
+
try:
|
| 302 |
+
r, g, b = COLORS_BY_NAME[value_lower]
|
| 303 |
+
except KeyError:
|
| 304 |
+
pass
|
| 305 |
+
else:
|
| 306 |
+
return ints_to_rgba(r, g, b, None)
|
| 307 |
+
|
| 308 |
+
m = re.fullmatch(r_hex_short, value_lower)
|
| 309 |
+
if m:
|
| 310 |
+
*rgb, a = m.groups()
|
| 311 |
+
r, g, b = (int(v * 2, 16) for v in rgb)
|
| 312 |
+
if a:
|
| 313 |
+
alpha: Optional[float] = int(a * 2, 16) / 255
|
| 314 |
+
else:
|
| 315 |
+
alpha = None
|
| 316 |
+
return ints_to_rgba(r, g, b, alpha)
|
| 317 |
+
|
| 318 |
+
m = re.fullmatch(r_hex_long, value_lower)
|
| 319 |
+
if m:
|
| 320 |
+
*rgb, a = m.groups()
|
| 321 |
+
r, g, b = (int(v, 16) for v in rgb)
|
| 322 |
+
if a:
|
| 323 |
+
alpha = int(a, 16) / 255
|
| 324 |
+
else:
|
| 325 |
+
alpha = None
|
| 326 |
+
return ints_to_rgba(r, g, b, alpha)
|
| 327 |
+
|
| 328 |
+
m = re.fullmatch(r_rgb, value_lower) or re.fullmatch(r_rgb_v4_style, value_lower)
|
| 329 |
+
if m:
|
| 330 |
+
return ints_to_rgba(*m.groups()) # type: ignore
|
| 331 |
+
|
| 332 |
+
m = re.fullmatch(r_hsl, value_lower) or re.fullmatch(r_hsl_v4_style, value_lower)
|
| 333 |
+
if m:
|
| 334 |
+
return parse_hsl(*m.groups()) # type: ignore
|
| 335 |
+
|
| 336 |
+
raise PydanticCustomError('color_error', 'value is not a valid color: string not recognised as a valid color')
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float] = None) -> RGBA:
|
| 340 |
+
"""Converts integer or string values for RGB color and an optional alpha value to an `RGBA` object.
|
| 341 |
+
|
| 342 |
+
Args:
|
| 343 |
+
r: An integer or string representing the red color value.
|
| 344 |
+
g: An integer or string representing the green color value.
|
| 345 |
+
b: An integer or string representing the blue color value.
|
| 346 |
+
alpha: A float representing the alpha value. Defaults to None.
|
| 347 |
+
|
| 348 |
+
Returns:
|
| 349 |
+
An instance of the `RGBA` class with the corresponding color and alpha values.
|
| 350 |
+
"""
|
| 351 |
+
return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha))
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def parse_color_value(value: Union[int, str], max_val: int = 255) -> float:
|
| 355 |
+
"""Parse the color value provided and return a number between 0 and 1.
|
| 356 |
+
|
| 357 |
+
Args:
|
| 358 |
+
value: An integer or string color value.
|
| 359 |
+
max_val: Maximum range value. Defaults to 255.
|
| 360 |
+
|
| 361 |
+
Raises:
|
| 362 |
+
PydanticCustomError: If the value is not a valid color.
|
| 363 |
+
|
| 364 |
+
Returns:
|
| 365 |
+
A number between 0 and 1.
|
| 366 |
+
"""
|
| 367 |
+
try:
|
| 368 |
+
color = float(value)
|
| 369 |
+
except ValueError:
|
| 370 |
+
raise PydanticCustomError('color_error', 'value is not a valid color: color values must be a valid number')
|
| 371 |
+
if 0 <= color <= max_val:
|
| 372 |
+
return color / max_val
|
| 373 |
+
else:
|
| 374 |
+
raise PydanticCustomError(
|
| 375 |
+
'color_error',
|
| 376 |
+
'value is not a valid color: color values must be in the range 0 to {max_val}',
|
| 377 |
+
{'max_val': max_val},
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]:
|
| 382 |
+
"""Parse an alpha value checking it's a valid float in the range 0 to 1.
|
| 383 |
+
|
| 384 |
+
Args:
|
| 385 |
+
value: The input value to parse.
|
| 386 |
+
|
| 387 |
+
Returns:
|
| 388 |
+
The parsed value as a float, or `None` if the value was None or equal 1.
|
| 389 |
+
|
| 390 |
+
Raises:
|
| 391 |
+
PydanticCustomError: If the input value cannot be successfully parsed as a float in the expected range.
|
| 392 |
+
"""
|
| 393 |
+
if value is None:
|
| 394 |
+
return None
|
| 395 |
+
try:
|
| 396 |
+
if isinstance(value, str) and value.endswith('%'):
|
| 397 |
+
alpha = float(value[:-1]) / 100
|
| 398 |
+
else:
|
| 399 |
+
alpha = float(value)
|
| 400 |
+
except ValueError:
|
| 401 |
+
raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be a valid float')
|
| 402 |
+
|
| 403 |
+
if math.isclose(alpha, 1):
|
| 404 |
+
return None
|
| 405 |
+
elif 0 <= alpha <= 1:
|
| 406 |
+
return alpha
|
| 407 |
+
else:
|
| 408 |
+
raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be in the range 0 to 1')
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA:
|
| 412 |
+
"""Parse raw hue, saturation, lightness, and alpha values and convert to RGBA.
|
| 413 |
+
|
| 414 |
+
Args:
|
| 415 |
+
h: The hue value.
|
| 416 |
+
h_units: The unit for hue value.
|
| 417 |
+
sat: The saturation value.
|
| 418 |
+
light: The lightness value.
|
| 419 |
+
alpha: Alpha value.
|
| 420 |
+
|
| 421 |
+
Returns:
|
| 422 |
+
An instance of `RGBA`.
|
| 423 |
+
"""
|
| 424 |
+
s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100)
|
| 425 |
+
|
| 426 |
+
h_value = float(h)
|
| 427 |
+
if h_units in {None, 'deg'}:
|
| 428 |
+
h_value = h_value % 360 / 360
|
| 429 |
+
elif h_units == 'rad':
|
| 430 |
+
h_value = h_value % rads / rads
|
| 431 |
+
else:
|
| 432 |
+
# turns
|
| 433 |
+
h_value = h_value % 1
|
| 434 |
+
|
| 435 |
+
r, g, b = hls_to_rgb(h_value, l_value, s_value)
|
| 436 |
+
return RGBA(r, g, b, parse_float_alpha(alpha))
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
def float_to_255(c: float) -> int:
|
| 440 |
+
"""Converts a float value between 0 and 1 (inclusive) to an integer between 0 and 255 (inclusive).
|
| 441 |
+
|
| 442 |
+
Args:
|
| 443 |
+
c: The float value to be converted. Must be between 0 and 1 (inclusive).
|
| 444 |
+
|
| 445 |
+
Returns:
|
| 446 |
+
The integer equivalent of the given float value rounded to the nearest whole number.
|
| 447 |
+
|
| 448 |
+
Raises:
|
| 449 |
+
ValueError: If the given float value is outside the acceptable range of 0 to 1 (inclusive).
|
| 450 |
+
"""
|
| 451 |
+
return int(round(c * 255))
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
COLORS_BY_NAME = {
|
| 455 |
+
'aliceblue': (240, 248, 255),
|
| 456 |
+
'antiquewhite': (250, 235, 215),
|
| 457 |
+
'aqua': (0, 255, 255),
|
| 458 |
+
'aquamarine': (127, 255, 212),
|
| 459 |
+
'azure': (240, 255, 255),
|
| 460 |
+
'beige': (245, 245, 220),
|
| 461 |
+
'bisque': (255, 228, 196),
|
| 462 |
+
'black': (0, 0, 0),
|
| 463 |
+
'blanchedalmond': (255, 235, 205),
|
| 464 |
+
'blue': (0, 0, 255),
|
| 465 |
+
'blueviolet': (138, 43, 226),
|
| 466 |
+
'brown': (165, 42, 42),
|
| 467 |
+
'burlywood': (222, 184, 135),
|
| 468 |
+
'cadetblue': (95, 158, 160),
|
| 469 |
+
'chartreuse': (127, 255, 0),
|
| 470 |
+
'chocolate': (210, 105, 30),
|
| 471 |
+
'coral': (255, 127, 80),
|
| 472 |
+
'cornflowerblue': (100, 149, 237),
|
| 473 |
+
'cornsilk': (255, 248, 220),
|
| 474 |
+
'crimson': (220, 20, 60),
|
| 475 |
+
'cyan': (0, 255, 255),
|
| 476 |
+
'darkblue': (0, 0, 139),
|
| 477 |
+
'darkcyan': (0, 139, 139),
|
| 478 |
+
'darkgoldenrod': (184, 134, 11),
|
| 479 |
+
'darkgray': (169, 169, 169),
|
| 480 |
+
'darkgreen': (0, 100, 0),
|
| 481 |
+
'darkgrey': (169, 169, 169),
|
| 482 |
+
'darkkhaki': (189, 183, 107),
|
| 483 |
+
'darkmagenta': (139, 0, 139),
|
| 484 |
+
'darkolivegreen': (85, 107, 47),
|
| 485 |
+
'darkorange': (255, 140, 0),
|
| 486 |
+
'darkorchid': (153, 50, 204),
|
| 487 |
+
'darkred': (139, 0, 0),
|
| 488 |
+
'darksalmon': (233, 150, 122),
|
| 489 |
+
'darkseagreen': (143, 188, 143),
|
| 490 |
+
'darkslateblue': (72, 61, 139),
|
| 491 |
+
'darkslategray': (47, 79, 79),
|
| 492 |
+
'darkslategrey': (47, 79, 79),
|
| 493 |
+
'darkturquoise': (0, 206, 209),
|
| 494 |
+
'darkviolet': (148, 0, 211),
|
| 495 |
+
'deeppink': (255, 20, 147),
|
| 496 |
+
'deepskyblue': (0, 191, 255),
|
| 497 |
+
'dimgray': (105, 105, 105),
|
| 498 |
+
'dimgrey': (105, 105, 105),
|
| 499 |
+
'dodgerblue': (30, 144, 255),
|
| 500 |
+
'firebrick': (178, 34, 34),
|
| 501 |
+
'floralwhite': (255, 250, 240),
|
| 502 |
+
'forestgreen': (34, 139, 34),
|
| 503 |
+
'fuchsia': (255, 0, 255),
|
| 504 |
+
'gainsboro': (220, 220, 220),
|
| 505 |
+
'ghostwhite': (248, 248, 255),
|
| 506 |
+
'gold': (255, 215, 0),
|
| 507 |
+
'goldenrod': (218, 165, 32),
|
| 508 |
+
'gray': (128, 128, 128),
|
| 509 |
+
'green': (0, 128, 0),
|
| 510 |
+
'greenyellow': (173, 255, 47),
|
| 511 |
+
'grey': (128, 128, 128),
|
| 512 |
+
'honeydew': (240, 255, 240),
|
| 513 |
+
'hotpink': (255, 105, 180),
|
| 514 |
+
'indianred': (205, 92, 92),
|
| 515 |
+
'indigo': (75, 0, 130),
|
| 516 |
+
'ivory': (255, 255, 240),
|
| 517 |
+
'khaki': (240, 230, 140),
|
| 518 |
+
'lavender': (230, 230, 250),
|
| 519 |
+
'lavenderblush': (255, 240, 245),
|
| 520 |
+
'lawngreen': (124, 252, 0),
|
| 521 |
+
'lemonchiffon': (255, 250, 205),
|
| 522 |
+
'lightblue': (173, 216, 230),
|
| 523 |
+
'lightcoral': (240, 128, 128),
|
| 524 |
+
'lightcyan': (224, 255, 255),
|
| 525 |
+
'lightgoldenrodyellow': (250, 250, 210),
|
| 526 |
+
'lightgray': (211, 211, 211),
|
| 527 |
+
'lightgreen': (144, 238, 144),
|
| 528 |
+
'lightgrey': (211, 211, 211),
|
| 529 |
+
'lightpink': (255, 182, 193),
|
| 530 |
+
'lightsalmon': (255, 160, 122),
|
| 531 |
+
'lightseagreen': (32, 178, 170),
|
| 532 |
+
'lightskyblue': (135, 206, 250),
|
| 533 |
+
'lightslategray': (119, 136, 153),
|
| 534 |
+
'lightslategrey': (119, 136, 153),
|
| 535 |
+
'lightsteelblue': (176, 196, 222),
|
| 536 |
+
'lightyellow': (255, 255, 224),
|
| 537 |
+
'lime': (0, 255, 0),
|
| 538 |
+
'limegreen': (50, 205, 50),
|
| 539 |
+
'linen': (250, 240, 230),
|
| 540 |
+
'magenta': (255, 0, 255),
|
| 541 |
+
'maroon': (128, 0, 0),
|
| 542 |
+
'mediumaquamarine': (102, 205, 170),
|
| 543 |
+
'mediumblue': (0, 0, 205),
|
| 544 |
+
'mediumorchid': (186, 85, 211),
|
| 545 |
+
'mediumpurple': (147, 112, 219),
|
| 546 |
+
'mediumseagreen': (60, 179, 113),
|
| 547 |
+
'mediumslateblue': (123, 104, 238),
|
| 548 |
+
'mediumspringgreen': (0, 250, 154),
|
| 549 |
+
'mediumturquoise': (72, 209, 204),
|
| 550 |
+
'mediumvioletred': (199, 21, 133),
|
| 551 |
+
'midnightblue': (25, 25, 112),
|
| 552 |
+
'mintcream': (245, 255, 250),
|
| 553 |
+
'mistyrose': (255, 228, 225),
|
| 554 |
+
'moccasin': (255, 228, 181),
|
| 555 |
+
'navajowhite': (255, 222, 173),
|
| 556 |
+
'navy': (0, 0, 128),
|
| 557 |
+
'oldlace': (253, 245, 230),
|
| 558 |
+
'olive': (128, 128, 0),
|
| 559 |
+
'olivedrab': (107, 142, 35),
|
| 560 |
+
'orange': (255, 165, 0),
|
| 561 |
+
'orangered': (255, 69, 0),
|
| 562 |
+
'orchid': (218, 112, 214),
|
| 563 |
+
'palegoldenrod': (238, 232, 170),
|
| 564 |
+
'palegreen': (152, 251, 152),
|
| 565 |
+
'paleturquoise': (175, 238, 238),
|
| 566 |
+
'palevioletred': (219, 112, 147),
|
| 567 |
+
'papayawhip': (255, 239, 213),
|
| 568 |
+
'peachpuff': (255, 218, 185),
|
| 569 |
+
'peru': (205, 133, 63),
|
| 570 |
+
'pink': (255, 192, 203),
|
| 571 |
+
'plum': (221, 160, 221),
|
| 572 |
+
'powderblue': (176, 224, 230),
|
| 573 |
+
'purple': (128, 0, 128),
|
| 574 |
+
'red': (255, 0, 0),
|
| 575 |
+
'rosybrown': (188, 143, 143),
|
| 576 |
+
'royalblue': (65, 105, 225),
|
| 577 |
+
'saddlebrown': (139, 69, 19),
|
| 578 |
+
'salmon': (250, 128, 114),
|
| 579 |
+
'sandybrown': (244, 164, 96),
|
| 580 |
+
'seagreen': (46, 139, 87),
|
| 581 |
+
'seashell': (255, 245, 238),
|
| 582 |
+
'sienna': (160, 82, 45),
|
| 583 |
+
'silver': (192, 192, 192),
|
| 584 |
+
'skyblue': (135, 206, 235),
|
| 585 |
+
'slateblue': (106, 90, 205),
|
| 586 |
+
'slategray': (112, 128, 144),
|
| 587 |
+
'slategrey': (112, 128, 144),
|
| 588 |
+
'snow': (255, 250, 250),
|
| 589 |
+
'springgreen': (0, 255, 127),
|
| 590 |
+
'steelblue': (70, 130, 180),
|
| 591 |
+
'tan': (210, 180, 140),
|
| 592 |
+
'teal': (0, 128, 128),
|
| 593 |
+
'thistle': (216, 191, 216),
|
| 594 |
+
'tomato': (255, 99, 71),
|
| 595 |
+
'turquoise': (64, 224, 208),
|
| 596 |
+
'violet': (238, 130, 238),
|
| 597 |
+
'wheat': (245, 222, 179),
|
| 598 |
+
'white': (255, 255, 255),
|
| 599 |
+
'whitesmoke': (245, 245, 245),
|
| 600 |
+
'yellow': (255, 255, 0),
|
| 601 |
+
'yellowgreen': (154, 205, 50),
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()}
|
.mcp-venv/lib/python3.13/site-packages/pydantic/config.py
ADDED
|
@@ -0,0 +1,1296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configuration for Pydantic models."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from re import Pattern
|
| 7 |
+
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union, cast, overload
|
| 8 |
+
|
| 9 |
+
from typing_extensions import TypeAlias, TypedDict, Unpack, deprecated
|
| 10 |
+
|
| 11 |
+
from ._migration import getattr_migration
|
| 12 |
+
from .aliases import AliasGenerator
|
| 13 |
+
from .errors import PydanticUserError
|
| 14 |
+
from .warnings import PydanticDeprecatedSince211
|
| 15 |
+
|
| 16 |
+
if TYPE_CHECKING:
|
| 17 |
+
from ._internal._generate_schema import GenerateSchema as _GenerateSchema
|
| 18 |
+
from .fields import ComputedFieldInfo, FieldInfo
|
| 19 |
+
|
| 20 |
+
__all__ = ('ConfigDict', 'with_config')
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
JsonValue: TypeAlias = Union[int, float, str, bool, None, list['JsonValue'], 'JsonDict']
|
| 24 |
+
JsonDict: TypeAlias = dict[str, JsonValue]
|
| 25 |
+
|
| 26 |
+
JsonEncoder = Callable[[Any], Any]
|
| 27 |
+
|
| 28 |
+
JsonSchemaExtraCallable: TypeAlias = Union[
|
| 29 |
+
Callable[[JsonDict], None],
|
| 30 |
+
Callable[[JsonDict, type[Any]], None],
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
ExtraValues = Literal['allow', 'ignore', 'forbid']
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class ConfigDict(TypedDict, total=False):
|
| 37 |
+
"""A TypedDict for configuring Pydantic behaviour."""
|
| 38 |
+
|
| 39 |
+
title: str | None
|
| 40 |
+
"""The title for the generated JSON schema, defaults to the model's name"""
|
| 41 |
+
|
| 42 |
+
model_title_generator: Callable[[type], str] | None
|
| 43 |
+
"""A callable that takes a model class and returns the title for it. Defaults to `None`."""
|
| 44 |
+
|
| 45 |
+
field_title_generator: Callable[[str, FieldInfo | ComputedFieldInfo], str] | None
|
| 46 |
+
"""A callable that takes a field's name and info and returns title for it. Defaults to `None`."""
|
| 47 |
+
|
| 48 |
+
str_to_lower: bool
|
| 49 |
+
"""Whether to convert all characters to lowercase for str types. Defaults to `False`."""
|
| 50 |
+
|
| 51 |
+
str_to_upper: bool
|
| 52 |
+
"""Whether to convert all characters to uppercase for str types. Defaults to `False`."""
|
| 53 |
+
|
| 54 |
+
str_strip_whitespace: bool
|
| 55 |
+
"""Whether to strip leading and trailing whitespace for str types."""
|
| 56 |
+
|
| 57 |
+
str_min_length: int
|
| 58 |
+
"""The minimum length for str types. Defaults to `None`."""
|
| 59 |
+
|
| 60 |
+
str_max_length: int | None
|
| 61 |
+
"""The maximum length for str types. Defaults to `None`."""
|
| 62 |
+
|
| 63 |
+
extra: ExtraValues | None
|
| 64 |
+
'''
|
| 65 |
+
Whether to ignore, allow, or forbid extra data during model initialization. Defaults to `'ignore'`.
|
| 66 |
+
|
| 67 |
+
Three configuration values are available:
|
| 68 |
+
|
| 69 |
+
- `'ignore'`: Providing extra data is ignored (the default):
|
| 70 |
+
```python
|
| 71 |
+
from pydantic import BaseModel, ConfigDict
|
| 72 |
+
|
| 73 |
+
class User(BaseModel):
|
| 74 |
+
model_config = ConfigDict(extra='ignore') # (1)!
|
| 75 |
+
|
| 76 |
+
name: str
|
| 77 |
+
|
| 78 |
+
user = User(name='John Doe', age=20) # (2)!
|
| 79 |
+
print(user)
|
| 80 |
+
#> name='John Doe'
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
1. This is the default behaviour.
|
| 84 |
+
2. The `age` argument is ignored.
|
| 85 |
+
|
| 86 |
+
- `'forbid'`: Providing extra data is not permitted, and a [`ValidationError`][pydantic_core.ValidationError]
|
| 87 |
+
will be raised if this is the case:
|
| 88 |
+
```python
|
| 89 |
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
class Model(BaseModel):
|
| 93 |
+
x: int
|
| 94 |
+
|
| 95 |
+
model_config = ConfigDict(extra='forbid')
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
try:
|
| 99 |
+
Model(x=1, y='a')
|
| 100 |
+
except ValidationError as exc:
|
| 101 |
+
print(exc)
|
| 102 |
+
"""
|
| 103 |
+
1 validation error for Model
|
| 104 |
+
y
|
| 105 |
+
Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str]
|
| 106 |
+
"""
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
- `'allow'`: Providing extra data is allowed and stored in the `__pydantic_extra__` dictionary attribute:
|
| 110 |
+
```python
|
| 111 |
+
from pydantic import BaseModel, ConfigDict
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class Model(BaseModel):
|
| 115 |
+
x: int
|
| 116 |
+
|
| 117 |
+
model_config = ConfigDict(extra='allow')
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
m = Model(x=1, y='a')
|
| 121 |
+
assert m.__pydantic_extra__ == {'y': 'a'}
|
| 122 |
+
```
|
| 123 |
+
By default, no validation will be applied to these extra items, but you can set a type for the values by overriding
|
| 124 |
+
the type annotation for `__pydantic_extra__`:
|
| 125 |
+
```python
|
| 126 |
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class Model(BaseModel):
|
| 130 |
+
__pydantic_extra__: dict[str, int] = Field(init=False) # (1)!
|
| 131 |
+
|
| 132 |
+
x: int
|
| 133 |
+
|
| 134 |
+
model_config = ConfigDict(extra='allow')
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
try:
|
| 138 |
+
Model(x=1, y='a')
|
| 139 |
+
except ValidationError as exc:
|
| 140 |
+
print(exc)
|
| 141 |
+
"""
|
| 142 |
+
1 validation error for Model
|
| 143 |
+
y
|
| 144 |
+
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
|
| 145 |
+
"""
|
| 146 |
+
|
| 147 |
+
m = Model(x=1, y='2')
|
| 148 |
+
assert m.x == 1
|
| 149 |
+
assert m.y == 2
|
| 150 |
+
assert m.model_dump() == {'x': 1, 'y': 2}
|
| 151 |
+
assert m.__pydantic_extra__ == {'y': 2}
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
1. The `= Field(init=False)` does not have any effect at runtime, but prevents the `__pydantic_extra__` field from
|
| 155 |
+
being included as a parameter to the model's `__init__` method by type checkers.
|
| 156 |
+
|
| 157 |
+
As well as specifying an `extra` configuration value on the model, you can also provide it as an argument to the validation methods.
|
| 158 |
+
This will override any `extra` configuration value set on the model:
|
| 159 |
+
```python
|
| 160 |
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
| 161 |
+
|
| 162 |
+
class Model(BaseModel):
|
| 163 |
+
x: int
|
| 164 |
+
model_config = ConfigDict(extra="allow")
|
| 165 |
+
|
| 166 |
+
try:
|
| 167 |
+
# Override model config and forbid extra fields just this time
|
| 168 |
+
Model.model_validate({"x": 1, "y": 2}, extra="forbid")
|
| 169 |
+
except ValidationError as exc:
|
| 170 |
+
print(exc)
|
| 171 |
+
"""
|
| 172 |
+
1 validation error for Model
|
| 173 |
+
y
|
| 174 |
+
Extra inputs are not permitted [type=extra_forbidden, input_value=2, input_type=int]
|
| 175 |
+
"""
|
| 176 |
+
```
|
| 177 |
+
'''
|
| 178 |
+
|
| 179 |
+
frozen: bool
|
| 180 |
+
"""
|
| 181 |
+
Whether models are faux-immutable, i.e. whether `__setattr__` is allowed, and also generates
|
| 182 |
+
a `__hash__()` method for the model. This makes instances of the model potentially hashable if all the
|
| 183 |
+
attributes are hashable. Defaults to `False`.
|
| 184 |
+
|
| 185 |
+
Note:
|
| 186 |
+
On V1, the inverse of this setting was called `allow_mutation`, and was `True` by default.
|
| 187 |
+
"""
|
| 188 |
+
|
| 189 |
+
populate_by_name: bool
|
| 190 |
+
"""
|
| 191 |
+
Whether an aliased field may be populated by its name as given by the model
|
| 192 |
+
attribute, as well as the alias. Defaults to `False`.
|
| 193 |
+
|
| 194 |
+
!!! warning
|
| 195 |
+
`populate_by_name` usage is not recommended in v2.11+ and will be deprecated in v3.
|
| 196 |
+
Instead, you should use the [`validate_by_name`][pydantic.config.ConfigDict.validate_by_name] configuration setting.
|
| 197 |
+
|
| 198 |
+
When `validate_by_name=True` and `validate_by_alias=True`, this is strictly equivalent to the
|
| 199 |
+
previous behavior of `populate_by_name=True`.
|
| 200 |
+
|
| 201 |
+
In v2.11, we also introduced a [`validate_by_alias`][pydantic.config.ConfigDict.validate_by_alias] setting that introduces more fine grained
|
| 202 |
+
control for validation behavior.
|
| 203 |
+
|
| 204 |
+
Here's how you might go about using the new settings to achieve the same behavior:
|
| 205 |
+
|
| 206 |
+
```python
|
| 207 |
+
from pydantic import BaseModel, ConfigDict, Field
|
| 208 |
+
|
| 209 |
+
class Model(BaseModel):
|
| 210 |
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
| 211 |
+
|
| 212 |
+
my_field: str = Field(alias='my_alias') # (1)!
|
| 213 |
+
|
| 214 |
+
m = Model(my_alias='foo') # (2)!
|
| 215 |
+
print(m)
|
| 216 |
+
#> my_field='foo'
|
| 217 |
+
|
| 218 |
+
m = Model(my_field='foo') # (3)!
|
| 219 |
+
print(m)
|
| 220 |
+
#> my_field='foo'
|
| 221 |
+
```
|
| 222 |
+
|
| 223 |
+
1. The field `'my_field'` has an alias `'my_alias'`.
|
| 224 |
+
2. The model is populated by the alias `'my_alias'`.
|
| 225 |
+
3. The model is populated by the attribute name `'my_field'`.
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
use_enum_values: bool
|
| 229 |
+
"""
|
| 230 |
+
Whether to populate models with the `value` property of enums, rather than the raw enum.
|
| 231 |
+
This may be useful if you want to serialize `model.model_dump()` later. Defaults to `False`.
|
| 232 |
+
|
| 233 |
+
!!! note
|
| 234 |
+
If you have an `Optional[Enum]` value that you set a default for, you need to use `validate_default=True`
|
| 235 |
+
for said Field to ensure that the `use_enum_values` flag takes effect on the default, as extracting an
|
| 236 |
+
enum's value occurs during validation, not serialization.
|
| 237 |
+
|
| 238 |
+
```python
|
| 239 |
+
from enum import Enum
|
| 240 |
+
from typing import Optional
|
| 241 |
+
|
| 242 |
+
from pydantic import BaseModel, ConfigDict, Field
|
| 243 |
+
|
| 244 |
+
class SomeEnum(Enum):
|
| 245 |
+
FOO = 'foo'
|
| 246 |
+
BAR = 'bar'
|
| 247 |
+
BAZ = 'baz'
|
| 248 |
+
|
| 249 |
+
class SomeModel(BaseModel):
|
| 250 |
+
model_config = ConfigDict(use_enum_values=True)
|
| 251 |
+
|
| 252 |
+
some_enum: SomeEnum
|
| 253 |
+
another_enum: Optional[SomeEnum] = Field(
|
| 254 |
+
default=SomeEnum.FOO, validate_default=True
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
model1 = SomeModel(some_enum=SomeEnum.BAR)
|
| 258 |
+
print(model1.model_dump())
|
| 259 |
+
#> {'some_enum': 'bar', 'another_enum': 'foo'}
|
| 260 |
+
|
| 261 |
+
model2 = SomeModel(some_enum=SomeEnum.BAR, another_enum=SomeEnum.BAZ)
|
| 262 |
+
print(model2.model_dump())
|
| 263 |
+
#> {'some_enum': 'bar', 'another_enum': 'baz'}
|
| 264 |
+
```
|
| 265 |
+
"""
|
| 266 |
+
|
| 267 |
+
validate_assignment: bool
|
| 268 |
+
"""
|
| 269 |
+
Whether to validate the data when the model is changed. Defaults to `False`.
|
| 270 |
+
|
| 271 |
+
The default behavior of Pydantic is to validate the data when the model is created.
|
| 272 |
+
|
| 273 |
+
In case the user changes the data after the model is created, the model is _not_ revalidated.
|
| 274 |
+
|
| 275 |
+
```python
|
| 276 |
+
from pydantic import BaseModel
|
| 277 |
+
|
| 278 |
+
class User(BaseModel):
|
| 279 |
+
name: str
|
| 280 |
+
|
| 281 |
+
user = User(name='John Doe') # (1)!
|
| 282 |
+
print(user)
|
| 283 |
+
#> name='John Doe'
|
| 284 |
+
user.name = 123 # (1)!
|
| 285 |
+
print(user)
|
| 286 |
+
#> name=123
|
| 287 |
+
```
|
| 288 |
+
|
| 289 |
+
1. The validation happens only when the model is created.
|
| 290 |
+
2. The validation does not happen when the data is changed.
|
| 291 |
+
|
| 292 |
+
In case you want to revalidate the model when the data is changed, you can use `validate_assignment=True`:
|
| 293 |
+
|
| 294 |
+
```python
|
| 295 |
+
from pydantic import BaseModel, ValidationError
|
| 296 |
+
|
| 297 |
+
class User(BaseModel, validate_assignment=True): # (1)!
|
| 298 |
+
name: str
|
| 299 |
+
|
| 300 |
+
user = User(name='John Doe') # (2)!
|
| 301 |
+
print(user)
|
| 302 |
+
#> name='John Doe'
|
| 303 |
+
try:
|
| 304 |
+
user.name = 123 # (3)!
|
| 305 |
+
except ValidationError as e:
|
| 306 |
+
print(e)
|
| 307 |
+
'''
|
| 308 |
+
1 validation error for User
|
| 309 |
+
name
|
| 310 |
+
Input should be a valid string [type=string_type, input_value=123, input_type=int]
|
| 311 |
+
'''
|
| 312 |
+
```
|
| 313 |
+
|
| 314 |
+
1. You can either use class keyword arguments, or `model_config` to set `validate_assignment=True`.
|
| 315 |
+
2. The validation happens when the model is created.
|
| 316 |
+
3. The validation _also_ happens when the data is changed.
|
| 317 |
+
"""
|
| 318 |
+
|
| 319 |
+
arbitrary_types_allowed: bool
|
| 320 |
+
"""
|
| 321 |
+
Whether arbitrary types are allowed for field types. Defaults to `False`.
|
| 322 |
+
|
| 323 |
+
```python
|
| 324 |
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
| 325 |
+
|
| 326 |
+
# This is not a pydantic model, it's an arbitrary class
|
| 327 |
+
class Pet:
|
| 328 |
+
def __init__(self, name: str):
|
| 329 |
+
self.name = name
|
| 330 |
+
|
| 331 |
+
class Model(BaseModel):
|
| 332 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 333 |
+
|
| 334 |
+
pet: Pet
|
| 335 |
+
owner: str
|
| 336 |
+
|
| 337 |
+
pet = Pet(name='Hedwig')
|
| 338 |
+
# A simple check of instance type is used to validate the data
|
| 339 |
+
model = Model(owner='Harry', pet=pet)
|
| 340 |
+
print(model)
|
| 341 |
+
#> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry'
|
| 342 |
+
print(model.pet)
|
| 343 |
+
#> <__main__.Pet object at 0x0123456789ab>
|
| 344 |
+
print(model.pet.name)
|
| 345 |
+
#> Hedwig
|
| 346 |
+
print(type(model.pet))
|
| 347 |
+
#> <class '__main__.Pet'>
|
| 348 |
+
try:
|
| 349 |
+
# If the value is not an instance of the type, it's invalid
|
| 350 |
+
Model(owner='Harry', pet='Hedwig')
|
| 351 |
+
except ValidationError as e:
|
| 352 |
+
print(e)
|
| 353 |
+
'''
|
| 354 |
+
1 validation error for Model
|
| 355 |
+
pet
|
| 356 |
+
Input should be an instance of Pet [type=is_instance_of, input_value='Hedwig', input_type=str]
|
| 357 |
+
'''
|
| 358 |
+
|
| 359 |
+
# Nothing in the instance of the arbitrary type is checked
|
| 360 |
+
# Here name probably should have been a str, but it's not validated
|
| 361 |
+
pet2 = Pet(name=42)
|
| 362 |
+
model2 = Model(owner='Harry', pet=pet2)
|
| 363 |
+
print(model2)
|
| 364 |
+
#> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry'
|
| 365 |
+
print(model2.pet)
|
| 366 |
+
#> <__main__.Pet object at 0x0123456789ab>
|
| 367 |
+
print(model2.pet.name)
|
| 368 |
+
#> 42
|
| 369 |
+
print(type(model2.pet))
|
| 370 |
+
#> <class '__main__.Pet'>
|
| 371 |
+
```
|
| 372 |
+
"""
|
| 373 |
+
|
| 374 |
+
from_attributes: bool
|
| 375 |
+
"""
|
| 376 |
+
Whether to build models and look up discriminators of tagged unions using python object attributes.
|
| 377 |
+
"""
|
| 378 |
+
|
| 379 |
+
loc_by_alias: bool
|
| 380 |
+
"""Whether to use the actual key provided in the data (e.g. alias) for error `loc`s rather than the field's name. Defaults to `True`."""
|
| 381 |
+
|
| 382 |
+
alias_generator: Callable[[str], str] | AliasGenerator | None
|
| 383 |
+
"""
|
| 384 |
+
A callable that takes a field name and returns an alias for it
|
| 385 |
+
or an instance of [`AliasGenerator`][pydantic.aliases.AliasGenerator]. Defaults to `None`.
|
| 386 |
+
|
| 387 |
+
When using a callable, the alias generator is used for both validation and serialization.
|
| 388 |
+
If you want to use different alias generators for validation and serialization, you can use
|
| 389 |
+
[`AliasGenerator`][pydantic.aliases.AliasGenerator] instead.
|
| 390 |
+
|
| 391 |
+
If data source field names do not match your code style (e.g. CamelCase fields),
|
| 392 |
+
you can automatically generate aliases using `alias_generator`. Here's an example with
|
| 393 |
+
a basic callable:
|
| 394 |
+
|
| 395 |
+
```python
|
| 396 |
+
from pydantic import BaseModel, ConfigDict
|
| 397 |
+
from pydantic.alias_generators import to_pascal
|
| 398 |
+
|
| 399 |
+
class Voice(BaseModel):
|
| 400 |
+
model_config = ConfigDict(alias_generator=to_pascal)
|
| 401 |
+
|
| 402 |
+
name: str
|
| 403 |
+
language_code: str
|
| 404 |
+
|
| 405 |
+
voice = Voice(Name='Filiz', LanguageCode='tr-TR')
|
| 406 |
+
print(voice.language_code)
|
| 407 |
+
#> tr-TR
|
| 408 |
+
print(voice.model_dump(by_alias=True))
|
| 409 |
+
#> {'Name': 'Filiz', 'LanguageCode': 'tr-TR'}
|
| 410 |
+
```
|
| 411 |
+
|
| 412 |
+
If you want to use different alias generators for validation and serialization, you can use
|
| 413 |
+
[`AliasGenerator`][pydantic.aliases.AliasGenerator].
|
| 414 |
+
|
| 415 |
+
```python
|
| 416 |
+
from pydantic import AliasGenerator, BaseModel, ConfigDict
|
| 417 |
+
from pydantic.alias_generators import to_camel, to_pascal
|
| 418 |
+
|
| 419 |
+
class Athlete(BaseModel):
|
| 420 |
+
first_name: str
|
| 421 |
+
last_name: str
|
| 422 |
+
sport: str
|
| 423 |
+
|
| 424 |
+
model_config = ConfigDict(
|
| 425 |
+
alias_generator=AliasGenerator(
|
| 426 |
+
validation_alias=to_camel,
|
| 427 |
+
serialization_alias=to_pascal,
|
| 428 |
+
)
|
| 429 |
+
)
|
| 430 |
+
|
| 431 |
+
athlete = Athlete(firstName='John', lastName='Doe', sport='track')
|
| 432 |
+
print(athlete.model_dump(by_alias=True))
|
| 433 |
+
#> {'FirstName': 'John', 'LastName': 'Doe', 'Sport': 'track'}
|
| 434 |
+
```
|
| 435 |
+
|
| 436 |
+
Note:
|
| 437 |
+
Pydantic offers three built-in alias generators: [`to_pascal`][pydantic.alias_generators.to_pascal],
|
| 438 |
+
[`to_camel`][pydantic.alias_generators.to_camel], and [`to_snake`][pydantic.alias_generators.to_snake].
|
| 439 |
+
"""
|
| 440 |
+
|
| 441 |
+
ignored_types: tuple[type, ...]
|
| 442 |
+
"""A tuple of types that may occur as values of class attributes without annotations. This is
|
| 443 |
+
typically used for custom descriptors (classes that behave like `property`). If an attribute is set on a
|
| 444 |
+
class without an annotation and has a type that is not in this tuple (or otherwise recognized by
|
| 445 |
+
_pydantic_), an error will be raised. Defaults to `()`.
|
| 446 |
+
"""
|
| 447 |
+
|
| 448 |
+
allow_inf_nan: bool
|
| 449 |
+
"""Whether to allow infinity (`+inf` an `-inf`) and NaN values to float and decimal fields. Defaults to `True`."""
|
| 450 |
+
|
| 451 |
+
json_schema_extra: JsonDict | JsonSchemaExtraCallable | None
|
| 452 |
+
"""A dict or callable to provide extra JSON schema properties. Defaults to `None`."""
|
| 453 |
+
|
| 454 |
+
json_encoders: dict[type[object], JsonEncoder] | None
|
| 455 |
+
"""
|
| 456 |
+
A `dict` of custom JSON encoders for specific types. Defaults to `None`.
|
| 457 |
+
|
| 458 |
+
/// version-deprecated | v2
|
| 459 |
+
This configuration option is a carryover from v1. We originally planned to remove it in v2 but didn't have a 1:1 replacement
|
| 460 |
+
so we are keeping it for now. It is still deprecated and will likely be removed in the future.
|
| 461 |
+
///
|
| 462 |
+
"""
|
| 463 |
+
|
| 464 |
+
# new in V2
|
| 465 |
+
strict: bool
|
| 466 |
+
"""
|
| 467 |
+
Whether strict validation is applied to all fields on the model.
|
| 468 |
+
|
| 469 |
+
By default, Pydantic attempts to coerce values to the correct type, when possible.
|
| 470 |
+
|
| 471 |
+
There are situations in which you may want to disable this behavior, and instead raise an error if a value's type
|
| 472 |
+
does not match the field's type annotation.
|
| 473 |
+
|
| 474 |
+
To configure strict mode for all fields on a model, you can set `strict=True` on the model.
|
| 475 |
+
|
| 476 |
+
```python
|
| 477 |
+
from pydantic import BaseModel, ConfigDict
|
| 478 |
+
|
| 479 |
+
class Model(BaseModel):
|
| 480 |
+
model_config = ConfigDict(strict=True)
|
| 481 |
+
|
| 482 |
+
name: str
|
| 483 |
+
age: int
|
| 484 |
+
```
|
| 485 |
+
|
| 486 |
+
See [Strict Mode](../concepts/strict_mode.md) for more details.
|
| 487 |
+
|
| 488 |
+
See the [Conversion Table](../concepts/conversion_table.md) for more details on how Pydantic converts data in both
|
| 489 |
+
strict and lax modes.
|
| 490 |
+
|
| 491 |
+
/// version-added | v2
|
| 492 |
+
///
|
| 493 |
+
"""
|
| 494 |
+
# whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never'
|
| 495 |
+
revalidate_instances: Literal['always', 'never', 'subclass-instances']
|
| 496 |
+
"""
|
| 497 |
+
When and how to revalidate models and dataclasses during validation. Can be one of:
|
| 498 |
+
|
| 499 |
+
- `'never'`: will *not* revalidate models and dataclasses during validation
|
| 500 |
+
- `'always'`: will revalidate models and dataclasses during validation
|
| 501 |
+
- `'subclass-instances'`: will revalidate models and dataclasses during validation if the instance is a
|
| 502 |
+
subclass of the model or dataclass
|
| 503 |
+
|
| 504 |
+
The default is `'never'` (no revalidation).
|
| 505 |
+
|
| 506 |
+
This configuration only affects *the current model* it is applied on, and does *not* propagate to the models
|
| 507 |
+
referenced in fields.
|
| 508 |
+
|
| 509 |
+
```python
|
| 510 |
+
from pydantic import BaseModel
|
| 511 |
+
|
| 512 |
+
class User(BaseModel, revalidate_instances='never'): # (1)!
|
| 513 |
+
name: str
|
| 514 |
+
|
| 515 |
+
class Transaction(BaseModel):
|
| 516 |
+
user: User
|
| 517 |
+
|
| 518 |
+
my_user = User(name='John')
|
| 519 |
+
t = Transaction(user=my_user)
|
| 520 |
+
|
| 521 |
+
my_user.name = 1 # (2)!
|
| 522 |
+
t = Transaction(user=my_user) # (3)!
|
| 523 |
+
print(t)
|
| 524 |
+
#> user=User(name=1)
|
| 525 |
+
```
|
| 526 |
+
|
| 527 |
+
1. This is the default behavior.
|
| 528 |
+
2. The assignment is *not* validated, unless you set [`validate_assignment`][pydantic.ConfigDict.validate_assignment] in the configuration.
|
| 529 |
+
3. Since `revalidate_instances` is set to `'never'`, the user instance is not revalidated.
|
| 530 |
+
|
| 531 |
+
Here is an example demonstrating the behavior of `'subclass-instances'`:
|
| 532 |
+
|
| 533 |
+
```python
|
| 534 |
+
from pydantic import BaseModel
|
| 535 |
+
|
| 536 |
+
class User(BaseModel, revalidate_instances='subclass-instances'):
|
| 537 |
+
name: str
|
| 538 |
+
|
| 539 |
+
class SubUser(User):
|
| 540 |
+
age: int
|
| 541 |
+
|
| 542 |
+
class Transaction(BaseModel):
|
| 543 |
+
user: User
|
| 544 |
+
|
| 545 |
+
my_user = User(name='John')
|
| 546 |
+
my_user.name = 1 # (1)!
|
| 547 |
+
t = Transaction(user=my_user) # (2)!
|
| 548 |
+
print(t)
|
| 549 |
+
#> user=User(name=1)
|
| 550 |
+
|
| 551 |
+
my_sub_user = SubUser(name='John', age=20)
|
| 552 |
+
t = Transaction(user=my_sub_user)
|
| 553 |
+
print(t) # (3)!
|
| 554 |
+
#> user=User(name='John')
|
| 555 |
+
```
|
| 556 |
+
|
| 557 |
+
1. The assignment is *not* validated, unless you set [`validate_assignment`][pydantic.ConfigDict.validate_assignment] in the configuration.
|
| 558 |
+
2. Because `my_user` is a "direct" instance of `User`, it is *not* being revalidated. It would have been the case if
|
| 559 |
+
`revalidate_instances` was set to `'always'`.
|
| 560 |
+
3. Because `my_sub_user` is an instance of a `User` subclass, it is being revalidated. In this case, Pydantic coerces `my_sub_user` to the defined
|
| 561 |
+
`User` class defined on `Transaction`. If one of its fields had an invalid value, a validation error would have been raised.
|
| 562 |
+
|
| 563 |
+
/// version-added | v2
|
| 564 |
+
///
|
| 565 |
+
"""
|
| 566 |
+
|
| 567 |
+
ser_json_timedelta: Literal['iso8601', 'float']
|
| 568 |
+
"""
|
| 569 |
+
The format of JSON serialized timedeltas. Accepts the string values of `'iso8601'` and
|
| 570 |
+
`'float'`. Defaults to `'iso8601'`.
|
| 571 |
+
|
| 572 |
+
- `'iso8601'` will serialize timedeltas to [ISO 8601 text format](https://en.wikipedia.org/wiki/ISO_8601#Durations).
|
| 573 |
+
- `'float'` will serialize timedeltas to the total number of seconds.
|
| 574 |
+
|
| 575 |
+
/// version-changed | v2.12
|
| 576 |
+
It is now recommended to use the [`ser_json_temporal`][pydantic.config.ConfigDict.ser_json_temporal]
|
| 577 |
+
setting. `ser_json_timedelta` will be deprecated in v3.
|
| 578 |
+
///
|
| 579 |
+
"""
|
| 580 |
+
|
| 581 |
+
ser_json_temporal: Literal['iso8601', 'seconds', 'milliseconds']
|
| 582 |
+
"""
|
| 583 |
+
The format of JSON serialized temporal types from the [`datetime`][] module. This includes:
|
| 584 |
+
|
| 585 |
+
- [`datetime.datetime`][]
|
| 586 |
+
- [`datetime.date`][]
|
| 587 |
+
- [`datetime.time`][]
|
| 588 |
+
- [`datetime.timedelta`][]
|
| 589 |
+
|
| 590 |
+
Can be one of:
|
| 591 |
+
|
| 592 |
+
- `'iso8601'` will serialize date-like types to [ISO 8601 text format](https://en.wikipedia.org/wiki/ISO_8601#Durations).
|
| 593 |
+
- `'milliseconds'` will serialize date-like types to a floating point number of milliseconds since the epoch.
|
| 594 |
+
- `'seconds'` will serialize date-like types to a floating point number of seconds since the epoch.
|
| 595 |
+
|
| 596 |
+
Defaults to `'iso8601'`.
|
| 597 |
+
|
| 598 |
+
/// version-added | v2.12
|
| 599 |
+
This setting replaces [`ser_json_timedelta`][pydantic.config.ConfigDict.ser_json_timedelta],
|
| 600 |
+
which will be deprecated in v3. `ser_json_temporal` adds more configurability for the other temporal types.
|
| 601 |
+
///
|
| 602 |
+
"""
|
| 603 |
+
|
| 604 |
+
val_temporal_unit: Literal['seconds', 'milliseconds', 'infer']
|
| 605 |
+
"""
|
| 606 |
+
The unit to assume for validating numeric input for datetime-like types ([`datetime.datetime`][] and [`datetime.date`][]). Can be one of:
|
| 607 |
+
|
| 608 |
+
- `'seconds'` will validate date or time numeric inputs as seconds since the [epoch].
|
| 609 |
+
- `'milliseconds'` will validate date or time numeric inputs as milliseconds since the [epoch].
|
| 610 |
+
- `'infer'` will infer the unit from the string numeric input on unix time as:
|
| 611 |
+
|
| 612 |
+
* seconds since the [epoch] if $-2^{10} <= v <= 2^{10}$
|
| 613 |
+
* milliseconds since the [epoch] (if $v < -2^{10}$ or $v > 2^{10}$).
|
| 614 |
+
|
| 615 |
+
Defaults to `'infer'`.
|
| 616 |
+
|
| 617 |
+
/// version-added | v2.12
|
| 618 |
+
///
|
| 619 |
+
|
| 620 |
+
[epoch]: https://en.wikipedia.org/wiki/Unix_time
|
| 621 |
+
"""
|
| 622 |
+
|
| 623 |
+
ser_json_bytes: Literal['utf8', 'base64', 'hex']
|
| 624 |
+
"""
|
| 625 |
+
The encoding of JSON serialized bytes. Defaults to `'utf8'`.
|
| 626 |
+
Set equal to `val_json_bytes` to get back an equal value after serialization round trip.
|
| 627 |
+
|
| 628 |
+
- `'utf8'` will serialize bytes to UTF-8 strings.
|
| 629 |
+
- `'base64'` will serialize bytes to URL safe base64 strings.
|
| 630 |
+
- `'hex'` will serialize bytes to hexadecimal strings.
|
| 631 |
+
"""
|
| 632 |
+
|
| 633 |
+
val_json_bytes: Literal['utf8', 'base64', 'hex']
|
| 634 |
+
"""
|
| 635 |
+
/// version-added | v2.9
|
| 636 |
+
///
|
| 637 |
+
|
| 638 |
+
The encoding of JSON serialized bytes to decode. Defaults to `'utf8'`.
|
| 639 |
+
Set equal to `ser_json_bytes` to get back an equal value after serialization round trip.
|
| 640 |
+
|
| 641 |
+
- `'utf8'` will deserialize UTF-8 strings to bytes.
|
| 642 |
+
- `'base64'` will deserialize URL safe base64 strings to bytes.
|
| 643 |
+
- `'hex'` will deserialize hexadecimal strings to bytes.
|
| 644 |
+
"""
|
| 645 |
+
|
| 646 |
+
ser_json_inf_nan: Literal['null', 'constants', 'strings']
|
| 647 |
+
"""
|
| 648 |
+
The encoding of JSON serialized infinity and NaN float values. Defaults to `'null'`.
|
| 649 |
+
|
| 650 |
+
- `'null'` will serialize infinity and NaN values as `null`.
|
| 651 |
+
- `'constants'` will serialize infinity and NaN values as `Infinity` and `NaN`.
|
| 652 |
+
- `'strings'` will serialize infinity as string `"Infinity"` and NaN as string `"NaN"`.
|
| 653 |
+
"""
|
| 654 |
+
|
| 655 |
+
# whether to validate default values during validation, default False
|
| 656 |
+
validate_default: bool
|
| 657 |
+
"""Whether to validate default values during validation. Defaults to `False`."""
|
| 658 |
+
|
| 659 |
+
validate_return: bool
|
| 660 |
+
"""Whether to validate the return value from call validators. Defaults to `False`."""
|
| 661 |
+
|
| 662 |
+
protected_namespaces: tuple[str | Pattern[str], ...]
|
| 663 |
+
"""
|
| 664 |
+
A tuple of strings and/or regex patterns that prevent models from having fields with names that conflict with its existing members/methods.
|
| 665 |
+
|
| 666 |
+
Strings are matched on a prefix basis. For instance, with `'dog'`, having a field named `'dog_name'` will be disallowed.
|
| 667 |
+
|
| 668 |
+
Regex patterns are matched on the entire field name. For instance, with the pattern `'^dog$'`, having a field named `'dog'` will be disallowed,
|
| 669 |
+
but `'dog_name'` will be accepted.
|
| 670 |
+
|
| 671 |
+
Defaults to `('model_validate', 'model_dump')`. This default is used to prevent collisions with the existing (and possibly future)
|
| 672 |
+
[validation](../concepts/models.md#validating-data) and [serialization](../concepts/serialization.md#serializing-data) methods.
|
| 673 |
+
|
| 674 |
+
```python
|
| 675 |
+
import warnings
|
| 676 |
+
|
| 677 |
+
from pydantic import BaseModel
|
| 678 |
+
|
| 679 |
+
warnings.filterwarnings('error') # Raise warnings as errors
|
| 680 |
+
|
| 681 |
+
try:
|
| 682 |
+
|
| 683 |
+
class Model(BaseModel):
|
| 684 |
+
model_dump_something: str
|
| 685 |
+
|
| 686 |
+
except UserWarning as e:
|
| 687 |
+
print(e)
|
| 688 |
+
'''
|
| 689 |
+
Field 'model_dump_something' in 'Model' conflicts with protected namespace 'model_dump'.
|
| 690 |
+
|
| 691 |
+
You may be able to solve this by setting the 'protected_namespaces' configuration to ('model_validate',).
|
| 692 |
+
'''
|
| 693 |
+
```
|
| 694 |
+
|
| 695 |
+
You can customize this behavior using the `protected_namespaces` setting:
|
| 696 |
+
|
| 697 |
+
```python {test="skip"}
|
| 698 |
+
import re
|
| 699 |
+
import warnings
|
| 700 |
+
|
| 701 |
+
from pydantic import BaseModel, ConfigDict
|
| 702 |
+
|
| 703 |
+
with warnings.catch_warnings(record=True) as caught_warnings:
|
| 704 |
+
warnings.simplefilter('always') # Catch all warnings
|
| 705 |
+
|
| 706 |
+
class Model(BaseModel):
|
| 707 |
+
safe_field: str
|
| 708 |
+
also_protect_field: str
|
| 709 |
+
protect_this: str
|
| 710 |
+
|
| 711 |
+
model_config = ConfigDict(
|
| 712 |
+
protected_namespaces=(
|
| 713 |
+
'protect_me_',
|
| 714 |
+
'also_protect_',
|
| 715 |
+
re.compile('^protect_this$'),
|
| 716 |
+
)
|
| 717 |
+
)
|
| 718 |
+
|
| 719 |
+
for warning in caught_warnings:
|
| 720 |
+
print(f'{warning.message}')
|
| 721 |
+
'''
|
| 722 |
+
Field 'also_protect_field' in 'Model' conflicts with protected namespace 'also_protect_'.
|
| 723 |
+
You may be able to solve this by setting the 'protected_namespaces' configuration to ('protect_me_', re.compile('^protect_this$'))`.
|
| 724 |
+
|
| 725 |
+
Field 'protect_this' in 'Model' conflicts with protected namespace 're.compile('^protect_this$')'.
|
| 726 |
+
You may be able to solve this by setting the 'protected_namespaces' configuration to ('protect_me_', 'also_protect_')`.
|
| 727 |
+
'''
|
| 728 |
+
```
|
| 729 |
+
|
| 730 |
+
While Pydantic will only emit a warning when an item is in a protected namespace but does not actually have a collision,
|
| 731 |
+
an error _is_ raised if there is an actual collision with an existing attribute:
|
| 732 |
+
|
| 733 |
+
```python
|
| 734 |
+
from pydantic import BaseModel, ConfigDict
|
| 735 |
+
|
| 736 |
+
try:
|
| 737 |
+
|
| 738 |
+
class Model(BaseModel):
|
| 739 |
+
model_validate: str
|
| 740 |
+
|
| 741 |
+
model_config = ConfigDict(protected_namespaces=('model_',))
|
| 742 |
+
|
| 743 |
+
except ValueError as e:
|
| 744 |
+
print(e)
|
| 745 |
+
'''
|
| 746 |
+
Field 'model_validate' conflicts with member <bound method BaseModel.model_validate of <class 'pydantic.main.BaseModel'>> of protected namespace 'model_'.
|
| 747 |
+
'''
|
| 748 |
+
```
|
| 749 |
+
|
| 750 |
+
/// version-changed | v2.10
|
| 751 |
+
The default protected namespaces was changed from `('model_',)` to `('model_validate', 'model_dump')`, to allow
|
| 752 |
+
for fields like `model_id`, `model_name` to be used.
|
| 753 |
+
///
|
| 754 |
+
"""
|
| 755 |
+
|
| 756 |
+
hide_input_in_errors: bool
|
| 757 |
+
"""
|
| 758 |
+
Whether to hide inputs when printing errors. Defaults to `False`.
|
| 759 |
+
|
| 760 |
+
Pydantic shows the input value and type when it raises `ValidationError` during the validation.
|
| 761 |
+
|
| 762 |
+
```python
|
| 763 |
+
from pydantic import BaseModel, ValidationError
|
| 764 |
+
|
| 765 |
+
class Model(BaseModel):
|
| 766 |
+
a: str
|
| 767 |
+
|
| 768 |
+
try:
|
| 769 |
+
Model(a=123)
|
| 770 |
+
except ValidationError as e:
|
| 771 |
+
print(e)
|
| 772 |
+
'''
|
| 773 |
+
1 validation error for Model
|
| 774 |
+
a
|
| 775 |
+
Input should be a valid string [type=string_type, input_value=123, input_type=int]
|
| 776 |
+
'''
|
| 777 |
+
```
|
| 778 |
+
|
| 779 |
+
You can hide the input value and type by setting the `hide_input_in_errors` config to `True`.
|
| 780 |
+
|
| 781 |
+
```python
|
| 782 |
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
| 783 |
+
|
| 784 |
+
class Model(BaseModel):
|
| 785 |
+
a: str
|
| 786 |
+
model_config = ConfigDict(hide_input_in_errors=True)
|
| 787 |
+
|
| 788 |
+
try:
|
| 789 |
+
Model(a=123)
|
| 790 |
+
except ValidationError as e:
|
| 791 |
+
print(e)
|
| 792 |
+
'''
|
| 793 |
+
1 validation error for Model
|
| 794 |
+
a
|
| 795 |
+
Input should be a valid string [type=string_type]
|
| 796 |
+
'''
|
| 797 |
+
```
|
| 798 |
+
"""
|
| 799 |
+
|
| 800 |
+
defer_build: bool
|
| 801 |
+
"""
|
| 802 |
+
Whether to defer model validator and serializer construction until the first model validation. Defaults to False.
|
| 803 |
+
|
| 804 |
+
This can be useful to avoid the overhead of building models which are only
|
| 805 |
+
used nested within other models, or when you want to manually define type namespace via
|
| 806 |
+
[`Model.model_rebuild(_types_namespace=...)`][pydantic.BaseModel.model_rebuild].
|
| 807 |
+
|
| 808 |
+
/// version-changed | v2.10
|
| 809 |
+
The setting also applies to [Pydantic dataclasses](../concepts/dataclasses.md) and [type adapters](../concepts/type_adapter.md).
|
| 810 |
+
///
|
| 811 |
+
"""
|
| 812 |
+
|
| 813 |
+
plugin_settings: dict[str, object] | None
|
| 814 |
+
"""A `dict` of settings for plugins. Defaults to `None`."""
|
| 815 |
+
|
| 816 |
+
schema_generator: type[_GenerateSchema] | None
|
| 817 |
+
"""
|
| 818 |
+
The `GenerateSchema` class to use during core schema generation.
|
| 819 |
+
|
| 820 |
+
/// version-deprecated | v2.10
|
| 821 |
+
The `GenerateSchema` class is private and highly subject to change.
|
| 822 |
+
///
|
| 823 |
+
"""
|
| 824 |
+
|
| 825 |
+
json_schema_serialization_defaults_required: bool
|
| 826 |
+
"""
|
| 827 |
+
Whether fields with default values should be marked as required in the serialization schema. Defaults to `False`.
|
| 828 |
+
|
| 829 |
+
This ensures that the serialization schema will reflect the fact a field with a default will always be present
|
| 830 |
+
when serializing the model, even though it is not required for validation.
|
| 831 |
+
|
| 832 |
+
However, there are scenarios where this may be undesirable β in particular, if you want to share the schema
|
| 833 |
+
between validation and serialization, and don't mind fields with defaults being marked as not required during
|
| 834 |
+
serialization. See [#7209](https://github.com/pydantic/pydantic/issues/7209) for more details.
|
| 835 |
+
|
| 836 |
+
```python
|
| 837 |
+
from pydantic import BaseModel, ConfigDict
|
| 838 |
+
|
| 839 |
+
class Model(BaseModel):
|
| 840 |
+
a: str = 'a'
|
| 841 |
+
|
| 842 |
+
model_config = ConfigDict(json_schema_serialization_defaults_required=True)
|
| 843 |
+
|
| 844 |
+
print(Model.model_json_schema(mode='validation'))
|
| 845 |
+
'''
|
| 846 |
+
{
|
| 847 |
+
'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}},
|
| 848 |
+
'title': 'Model',
|
| 849 |
+
'type': 'object',
|
| 850 |
+
}
|
| 851 |
+
'''
|
| 852 |
+
print(Model.model_json_schema(mode='serialization'))
|
| 853 |
+
'''
|
| 854 |
+
{
|
| 855 |
+
'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}},
|
| 856 |
+
'required': ['a'],
|
| 857 |
+
'title': 'Model',
|
| 858 |
+
'type': 'object',
|
| 859 |
+
}
|
| 860 |
+
'''
|
| 861 |
+
```
|
| 862 |
+
|
| 863 |
+
/// version-added | v2.4
|
| 864 |
+
///
|
| 865 |
+
"""
|
| 866 |
+
|
| 867 |
+
json_schema_mode_override: Literal['validation', 'serialization', None]
|
| 868 |
+
"""
|
| 869 |
+
If not `None`, the specified mode will be used to generate the JSON schema regardless of what `mode` was passed to
|
| 870 |
+
the function call. Defaults to `None`.
|
| 871 |
+
|
| 872 |
+
This provides a way to force the JSON schema generation to reflect a specific mode, e.g., to always use the
|
| 873 |
+
validation schema.
|
| 874 |
+
|
| 875 |
+
It can be useful when using frameworks (such as FastAPI) that may generate different schemas for validation
|
| 876 |
+
and serialization that must both be referenced from the same schema; when this happens, we automatically append
|
| 877 |
+
`-Input` to the definition reference for the validation schema and `-Output` to the definition reference for the
|
| 878 |
+
serialization schema. By specifying a `json_schema_mode_override` though, this prevents the conflict between
|
| 879 |
+
the validation and serialization schemas (since both will use the specified schema), and so prevents the suffixes
|
| 880 |
+
from being added to the definition references.
|
| 881 |
+
|
| 882 |
+
```python
|
| 883 |
+
from pydantic import BaseModel, ConfigDict, Json
|
| 884 |
+
|
| 885 |
+
class Model(BaseModel):
|
| 886 |
+
a: Json[int] # requires a string to validate, but will dump an int
|
| 887 |
+
|
| 888 |
+
print(Model.model_json_schema(mode='serialization'))
|
| 889 |
+
'''
|
| 890 |
+
{
|
| 891 |
+
'properties': {'a': {'title': 'A', 'type': 'integer'}},
|
| 892 |
+
'required': ['a'],
|
| 893 |
+
'title': 'Model',
|
| 894 |
+
'type': 'object',
|
| 895 |
+
}
|
| 896 |
+
'''
|
| 897 |
+
|
| 898 |
+
class ForceInputModel(Model):
|
| 899 |
+
# the following ensures that even with mode='serialization', we
|
| 900 |
+
# will get the schema that would be generated for validation.
|
| 901 |
+
model_config = ConfigDict(json_schema_mode_override='validation')
|
| 902 |
+
|
| 903 |
+
print(ForceInputModel.model_json_schema(mode='serialization'))
|
| 904 |
+
'''
|
| 905 |
+
{
|
| 906 |
+
'properties': {
|
| 907 |
+
'a': {
|
| 908 |
+
'contentMediaType': 'application/json',
|
| 909 |
+
'contentSchema': {'type': 'integer'},
|
| 910 |
+
'title': 'A',
|
| 911 |
+
'type': 'string',
|
| 912 |
+
}
|
| 913 |
+
},
|
| 914 |
+
'required': ['a'],
|
| 915 |
+
'title': 'ForceInputModel',
|
| 916 |
+
'type': 'object',
|
| 917 |
+
}
|
| 918 |
+
'''
|
| 919 |
+
```
|
| 920 |
+
|
| 921 |
+
/// version-added | v2.4
|
| 922 |
+
///
|
| 923 |
+
"""
|
| 924 |
+
|
| 925 |
+
coerce_numbers_to_str: bool
|
| 926 |
+
"""
|
| 927 |
+
If `True`, enables automatic coercion of any `Number` type to `str` in "lax" (non-strict) mode. Defaults to `False`.
|
| 928 |
+
|
| 929 |
+
Pydantic doesn't allow number types (`int`, `float`, `Decimal`) to be coerced as type `str` by default.
|
| 930 |
+
|
| 931 |
+
```python
|
| 932 |
+
from decimal import Decimal
|
| 933 |
+
|
| 934 |
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
| 935 |
+
|
| 936 |
+
class Model(BaseModel):
|
| 937 |
+
value: str
|
| 938 |
+
|
| 939 |
+
try:
|
| 940 |
+
print(Model(value=42))
|
| 941 |
+
except ValidationError as e:
|
| 942 |
+
print(e)
|
| 943 |
+
'''
|
| 944 |
+
1 validation error for Model
|
| 945 |
+
value
|
| 946 |
+
Input should be a valid string [type=string_type, input_value=42, input_type=int]
|
| 947 |
+
'''
|
| 948 |
+
|
| 949 |
+
class Model(BaseModel):
|
| 950 |
+
model_config = ConfigDict(coerce_numbers_to_str=True)
|
| 951 |
+
|
| 952 |
+
value: str
|
| 953 |
+
|
| 954 |
+
repr(Model(value=42).value)
|
| 955 |
+
#> "42"
|
| 956 |
+
repr(Model(value=42.13).value)
|
| 957 |
+
#> "42.13"
|
| 958 |
+
repr(Model(value=Decimal('42.13')).value)
|
| 959 |
+
#> "42.13"
|
| 960 |
+
```
|
| 961 |
+
"""
|
| 962 |
+
|
| 963 |
+
regex_engine: Literal['rust-regex', 'python-re']
|
| 964 |
+
"""
|
| 965 |
+
The regex engine to be used for pattern validation.
|
| 966 |
+
Defaults to `'rust-regex'`.
|
| 967 |
+
|
| 968 |
+
- `'rust-regex'` uses the [`regex`](https://docs.rs/regex) Rust crate,
|
| 969 |
+
which is non-backtracking and therefore more DDoS resistant, but does not support all regex features.
|
| 970 |
+
- `'python-re'` use the [`re`][] module, which supports all regex features, but may be slower.
|
| 971 |
+
|
| 972 |
+
!!! note
|
| 973 |
+
If you use a compiled regex pattern, the `'python-re'` engine will be used regardless of this setting.
|
| 974 |
+
This is so that flags such as [`re.IGNORECASE`][] are respected.
|
| 975 |
+
|
| 976 |
+
```python
|
| 977 |
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
| 978 |
+
|
| 979 |
+
class Model(BaseModel):
|
| 980 |
+
model_config = ConfigDict(regex_engine='python-re')
|
| 981 |
+
|
| 982 |
+
value: str = Field(pattern=r'^abc(?=def)')
|
| 983 |
+
|
| 984 |
+
print(Model(value='abcdef').value)
|
| 985 |
+
#> abcdef
|
| 986 |
+
|
| 987 |
+
try:
|
| 988 |
+
print(Model(value='abxyzcdef'))
|
| 989 |
+
except ValidationError as e:
|
| 990 |
+
print(e)
|
| 991 |
+
'''
|
| 992 |
+
1 validation error for Model
|
| 993 |
+
value
|
| 994 |
+
String should match pattern '^abc(?=def)' [type=string_pattern_mismatch, input_value='abxyzcdef', input_type=str]
|
| 995 |
+
'''
|
| 996 |
+
```
|
| 997 |
+
|
| 998 |
+
/// version-added | v2.5
|
| 999 |
+
///
|
| 1000 |
+
"""
|
| 1001 |
+
|
| 1002 |
+
validation_error_cause: bool
|
| 1003 |
+
"""
|
| 1004 |
+
If `True`, Python exceptions that were part of a validation failure will be shown as an exception group as a cause. Can be useful for debugging. Defaults to `False`.
|
| 1005 |
+
|
| 1006 |
+
Note:
|
| 1007 |
+
Python 3.10 and older don't support exception groups natively. <=3.10, backport must be installed: `pip install exceptiongroup`.
|
| 1008 |
+
|
| 1009 |
+
Note:
|
| 1010 |
+
The structure of validation errors are likely to change in future Pydantic versions. Pydantic offers no guarantees about their structure. Should be used for visual traceback debugging only.
|
| 1011 |
+
|
| 1012 |
+
/// version-added | v2.5
|
| 1013 |
+
///
|
| 1014 |
+
"""
|
| 1015 |
+
|
| 1016 |
+
use_attribute_docstrings: bool
|
| 1017 |
+
'''
|
| 1018 |
+
Whether docstrings of attributes (bare string literals immediately following the attribute declaration)
|
| 1019 |
+
should be used for field descriptions. Defaults to `False`.
|
| 1020 |
+
|
| 1021 |
+
```python
|
| 1022 |
+
from pydantic import BaseModel, ConfigDict, Field
|
| 1023 |
+
|
| 1024 |
+
|
| 1025 |
+
class Model(BaseModel):
|
| 1026 |
+
model_config = ConfigDict(use_attribute_docstrings=True)
|
| 1027 |
+
|
| 1028 |
+
x: str
|
| 1029 |
+
"""
|
| 1030 |
+
Example of an attribute docstring
|
| 1031 |
+
"""
|
| 1032 |
+
|
| 1033 |
+
y: int = Field(description="Description in Field")
|
| 1034 |
+
"""
|
| 1035 |
+
Description in Field overrides attribute docstring
|
| 1036 |
+
"""
|
| 1037 |
+
|
| 1038 |
+
|
| 1039 |
+
print(Model.model_fields["x"].description)
|
| 1040 |
+
# > Example of an attribute docstring
|
| 1041 |
+
print(Model.model_fields["y"].description)
|
| 1042 |
+
# > Description in Field
|
| 1043 |
+
```
|
| 1044 |
+
This requires the source code of the class to be available at runtime (and so won't work in the interactive interpreter shell).
|
| 1045 |
+
|
| 1046 |
+
!!! warning "Usage with `TypedDict` and stdlib dataclasses"
|
| 1047 |
+
Due to current limitations, attribute docstrings detection may not work as expected when using
|
| 1048 |
+
[`TypedDict`][typing.TypedDict] and stdlib dataclasses, in particular when:
|
| 1049 |
+
|
| 1050 |
+
- inheritance is being used.
|
| 1051 |
+
- multiple classes have the same name in the same source file (unless Python 3.13 or greater is used).
|
| 1052 |
+
|
| 1053 |
+
/// version-added | v2.7
|
| 1054 |
+
///
|
| 1055 |
+
'''
|
| 1056 |
+
|
| 1057 |
+
cache_strings: bool | Literal['all', 'keys', 'none']
|
| 1058 |
+
"""
|
| 1059 |
+
Whether to cache strings to avoid constructing new Python objects. Defaults to True.
|
| 1060 |
+
|
| 1061 |
+
Enabling this setting should significantly improve validation performance while increasing memory usage slightly.
|
| 1062 |
+
|
| 1063 |
+
- `True` or `'all'` (the default): cache all strings
|
| 1064 |
+
- `'keys'`: cache only dictionary keys
|
| 1065 |
+
- `False` or `'none'`: no caching
|
| 1066 |
+
|
| 1067 |
+
!!! note
|
| 1068 |
+
`True` or `'all'` is required to cache strings during general validation because
|
| 1069 |
+
validators don't know if they're in a key or a value.
|
| 1070 |
+
|
| 1071 |
+
!!! tip
|
| 1072 |
+
If repeated strings are rare, it's recommended to use `'keys'` or `'none'` to reduce memory usage,
|
| 1073 |
+
as the performance difference is minimal if repeated strings are rare.
|
| 1074 |
+
|
| 1075 |
+
/// version-added | v2.7
|
| 1076 |
+
///
|
| 1077 |
+
"""
|
| 1078 |
+
|
| 1079 |
+
validate_by_alias: bool
|
| 1080 |
+
"""
|
| 1081 |
+
Whether an aliased field may be populated by its alias. Defaults to `True`.
|
| 1082 |
+
|
| 1083 |
+
Here's an example of disabling validation by alias:
|
| 1084 |
+
|
| 1085 |
+
```py
|
| 1086 |
+
from pydantic import BaseModel, ConfigDict, Field
|
| 1087 |
+
|
| 1088 |
+
class Model(BaseModel):
|
| 1089 |
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=False)
|
| 1090 |
+
|
| 1091 |
+
my_field: str = Field(validation_alias='my_alias') # (1)!
|
| 1092 |
+
|
| 1093 |
+
m = Model(my_field='foo') # (2)!
|
| 1094 |
+
print(m)
|
| 1095 |
+
#> my_field='foo'
|
| 1096 |
+
```
|
| 1097 |
+
|
| 1098 |
+
1. The field `'my_field'` has an alias `'my_alias'`.
|
| 1099 |
+
2. The model can only be populated by the attribute name `'my_field'`.
|
| 1100 |
+
|
| 1101 |
+
!!! warning
|
| 1102 |
+
You cannot set both `validate_by_alias` and `validate_by_name` to `False`.
|
| 1103 |
+
This would make it impossible to populate an attribute.
|
| 1104 |
+
|
| 1105 |
+
See [usage errors](../errors/usage_errors.md#validate-by-alias-and-name-false) for an example.
|
| 1106 |
+
|
| 1107 |
+
If you set `validate_by_alias` to `False`, under the hood, Pydantic dynamically sets
|
| 1108 |
+
`validate_by_name` to `True` to ensure that validation can still occur.
|
| 1109 |
+
|
| 1110 |
+
/// version-added | v2.11
|
| 1111 |
+
This setting was introduced in conjunction with [`validate_by_name`][pydantic.ConfigDict.validate_by_name]
|
| 1112 |
+
to empower users with more fine grained validation control.
|
| 1113 |
+
///
|
| 1114 |
+
"""
|
| 1115 |
+
|
| 1116 |
+
validate_by_name: bool
|
| 1117 |
+
"""
|
| 1118 |
+
Whether an aliased field may be populated by its name as given by the model
|
| 1119 |
+
attribute. Defaults to `False`.
|
| 1120 |
+
|
| 1121 |
+
```python
|
| 1122 |
+
from pydantic import BaseModel, ConfigDict, Field
|
| 1123 |
+
|
| 1124 |
+
class Model(BaseModel):
|
| 1125 |
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
| 1126 |
+
|
| 1127 |
+
my_field: str = Field(validation_alias='my_alias') # (1)!
|
| 1128 |
+
|
| 1129 |
+
m = Model(my_alias='foo') # (2)!
|
| 1130 |
+
print(m)
|
| 1131 |
+
#> my_field='foo'
|
| 1132 |
+
|
| 1133 |
+
m = Model(my_field='foo') # (3)!
|
| 1134 |
+
print(m)
|
| 1135 |
+
#> my_field='foo'
|
| 1136 |
+
```
|
| 1137 |
+
|
| 1138 |
+
1. The field `'my_field'` has an alias `'my_alias'`.
|
| 1139 |
+
2. The model is populated by the alias `'my_alias'`.
|
| 1140 |
+
3. The model is populated by the attribute name `'my_field'`.
|
| 1141 |
+
|
| 1142 |
+
!!! warning
|
| 1143 |
+
You cannot set both `validate_by_alias` and `validate_by_name` to `False`.
|
| 1144 |
+
This would make it impossible to populate an attribute.
|
| 1145 |
+
|
| 1146 |
+
See [usage errors](../errors/usage_errors.md#validate-by-alias-and-name-false) for an example.
|
| 1147 |
+
|
| 1148 |
+
/// version-added | v2.11
|
| 1149 |
+
This setting was introduced in conjunction with [`validate_by_alias`][pydantic.ConfigDict.validate_by_alias]
|
| 1150 |
+
to empower users with more fine grained validation control. It is an alternative to [`populate_by_name`][pydantic.ConfigDict.populate_by_name],
|
| 1151 |
+
that enables validation by name **and** by alias.
|
| 1152 |
+
///
|
| 1153 |
+
"""
|
| 1154 |
+
|
| 1155 |
+
serialize_by_alias: bool
|
| 1156 |
+
"""
|
| 1157 |
+
Whether an aliased field should be serialized by its alias. Defaults to `False`.
|
| 1158 |
+
|
| 1159 |
+
Note: In v2.11, `serialize_by_alias` was introduced to address the
|
| 1160 |
+
[popular request](https://github.com/pydantic/pydantic/issues/8379)
|
| 1161 |
+
for consistency with alias behavior for validation and serialization settings.
|
| 1162 |
+
In v3, the default value is expected to change to `True` for consistency with the validation default.
|
| 1163 |
+
|
| 1164 |
+
```python
|
| 1165 |
+
from pydantic import BaseModel, ConfigDict, Field
|
| 1166 |
+
|
| 1167 |
+
class Model(BaseModel):
|
| 1168 |
+
model_config = ConfigDict(serialize_by_alias=True)
|
| 1169 |
+
|
| 1170 |
+
my_field: str = Field(serialization_alias='my_alias') # (1)!
|
| 1171 |
+
|
| 1172 |
+
m = Model(my_field='foo')
|
| 1173 |
+
print(m.model_dump()) # (2)!
|
| 1174 |
+
#> {'my_alias': 'foo'}
|
| 1175 |
+
```
|
| 1176 |
+
|
| 1177 |
+
1. The field `'my_field'` has an alias `'my_alias'`.
|
| 1178 |
+
2. The model is serialized using the alias `'my_alias'` for the `'my_field'` attribute.
|
| 1179 |
+
|
| 1180 |
+
|
| 1181 |
+
/// version-added | v2.11
|
| 1182 |
+
This setting was introduced to address the [popular request](https://github.com/pydantic/pydantic/issues/8379)
|
| 1183 |
+
for consistency with alias behavior for validation and serialization.
|
| 1184 |
+
|
| 1185 |
+
In v3, the default value is expected to change to `True` for consistency with the validation default.
|
| 1186 |
+
///
|
| 1187 |
+
"""
|
| 1188 |
+
|
| 1189 |
+
url_preserve_empty_path: bool
|
| 1190 |
+
"""
|
| 1191 |
+
Whether to preserve empty URL paths when validating values for a URL type. Defaults to `False`.
|
| 1192 |
+
|
| 1193 |
+
```python
|
| 1194 |
+
from pydantic import AnyUrl, BaseModel, ConfigDict
|
| 1195 |
+
|
| 1196 |
+
class Model(BaseModel):
|
| 1197 |
+
model_config = ConfigDict(url_preserve_empty_path=True)
|
| 1198 |
+
|
| 1199 |
+
url: AnyUrl
|
| 1200 |
+
|
| 1201 |
+
m = Model(url='http://example.com')
|
| 1202 |
+
print(m.url)
|
| 1203 |
+
#> http://example.com
|
| 1204 |
+
```
|
| 1205 |
+
|
| 1206 |
+
/// version-added | v2.12
|
| 1207 |
+
///
|
| 1208 |
+
"""
|
| 1209 |
+
|
| 1210 |
+
polymorphic_serialization: bool
|
| 1211 |
+
"""
|
| 1212 |
+
Whether to use polymorphic serialization for subclasses of the model or Pydantic dataclass. Defaults to `False`.
|
| 1213 |
+
"""
|
| 1214 |
+
|
| 1215 |
+
|
| 1216 |
+
_TypeT = TypeVar('_TypeT', bound=type)
|
| 1217 |
+
|
| 1218 |
+
|
| 1219 |
+
@overload
|
| 1220 |
+
@deprecated('Passing `config` as a keyword argument is deprecated. Pass `config` as a positional argument instead.')
|
| 1221 |
+
def with_config(*, config: ConfigDict) -> Callable[[_TypeT], _TypeT]: ...
|
| 1222 |
+
|
| 1223 |
+
|
| 1224 |
+
@overload
|
| 1225 |
+
def with_config(config: ConfigDict, /) -> Callable[[_TypeT], _TypeT]: ...
|
| 1226 |
+
|
| 1227 |
+
|
| 1228 |
+
@overload
|
| 1229 |
+
def with_config(**config: Unpack[ConfigDict]) -> Callable[[_TypeT], _TypeT]: ...
|
| 1230 |
+
|
| 1231 |
+
|
| 1232 |
+
def with_config(config: ConfigDict | None = None, /, **kwargs: Any) -> Callable[[_TypeT], _TypeT]:
|
| 1233 |
+
"""!!! abstract "Usage Documentation"
|
| 1234 |
+
[Configuration with other types](../concepts/config.md#configuration-on-other-supported-types)
|
| 1235 |
+
|
| 1236 |
+
A convenience decorator to set a [Pydantic configuration](config.md) on a `TypedDict` or a `dataclass` from the standard library.
|
| 1237 |
+
|
| 1238 |
+
Although the configuration can be set using the `__pydantic_config__` attribute, it does not play well with type checkers,
|
| 1239 |
+
especially with `TypedDict`.
|
| 1240 |
+
|
| 1241 |
+
!!! example "Usage"
|
| 1242 |
+
|
| 1243 |
+
```python
|
| 1244 |
+
from typing_extensions import TypedDict
|
| 1245 |
+
|
| 1246 |
+
from pydantic import ConfigDict, TypeAdapter, with_config
|
| 1247 |
+
|
| 1248 |
+
@with_config(ConfigDict(str_to_lower=True))
|
| 1249 |
+
class TD(TypedDict):
|
| 1250 |
+
x: str
|
| 1251 |
+
|
| 1252 |
+
ta = TypeAdapter(TD)
|
| 1253 |
+
|
| 1254 |
+
print(ta.validate_python({'x': 'ABC'}))
|
| 1255 |
+
#> {'x': 'abc'}
|
| 1256 |
+
```
|
| 1257 |
+
|
| 1258 |
+
/// deprecated-removed | v2.11 v3
|
| 1259 |
+
Passing `config` as a keyword argument.
|
| 1260 |
+
///
|
| 1261 |
+
|
| 1262 |
+
/// version-changed | v2.11
|
| 1263 |
+
Keyword arguments can be provided directly instead of a config dictionary.
|
| 1264 |
+
///
|
| 1265 |
+
"""
|
| 1266 |
+
if config is not None and kwargs:
|
| 1267 |
+
raise ValueError('Cannot specify both `config` and keyword arguments')
|
| 1268 |
+
|
| 1269 |
+
if len(kwargs) == 1 and (kwargs_conf := kwargs.get('config')) is not None:
|
| 1270 |
+
warnings.warn(
|
| 1271 |
+
'Passing `config` as a keyword argument is deprecated. Pass `config` as a positional argument instead',
|
| 1272 |
+
category=PydanticDeprecatedSince211,
|
| 1273 |
+
stacklevel=2,
|
| 1274 |
+
)
|
| 1275 |
+
final_config = cast(ConfigDict, kwargs_conf)
|
| 1276 |
+
else:
|
| 1277 |
+
final_config = config if config is not None else cast(ConfigDict, kwargs)
|
| 1278 |
+
|
| 1279 |
+
def inner(class_: _TypeT, /) -> _TypeT:
|
| 1280 |
+
# Ideally, we would check for `class_` to either be a `TypedDict` or a stdlib dataclass.
|
| 1281 |
+
# However, the `@with_config` decorator can be applied *after* `@dataclass`. To avoid
|
| 1282 |
+
# common mistakes, we at least check for `class_` to not be a Pydantic model.
|
| 1283 |
+
from ._internal._utils import is_model_class
|
| 1284 |
+
|
| 1285 |
+
if is_model_class(class_):
|
| 1286 |
+
raise PydanticUserError(
|
| 1287 |
+
f'Cannot use `with_config` on {class_.__name__} as it is a Pydantic model',
|
| 1288 |
+
code='with-config-on-model',
|
| 1289 |
+
)
|
| 1290 |
+
class_.__pydantic_config__ = final_config
|
| 1291 |
+
return class_
|
| 1292 |
+
|
| 1293 |
+
return inner
|
| 1294 |
+
|
| 1295 |
+
|
| 1296 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/dataclasses.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Provide an enhanced dataclass that performs validation."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
import functools
|
| 7 |
+
import sys
|
| 8 |
+
import types
|
| 9 |
+
from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, overload
|
| 10 |
+
from warnings import warn
|
| 11 |
+
|
| 12 |
+
from typing_extensions import TypeGuard, dataclass_transform
|
| 13 |
+
|
| 14 |
+
from ._internal import _config, _decorators, _mock_val_ser, _namespace_utils, _typing_extra
|
| 15 |
+
from ._internal import _dataclasses as _pydantic_dataclasses
|
| 16 |
+
from ._migration import getattr_migration
|
| 17 |
+
from .config import ConfigDict
|
| 18 |
+
from .errors import PydanticUserError
|
| 19 |
+
from .fields import Field, FieldInfo, PrivateAttr
|
| 20 |
+
|
| 21 |
+
if TYPE_CHECKING:
|
| 22 |
+
from ._internal._dataclasses import PydanticDataclass
|
| 23 |
+
from ._internal._namespace_utils import MappingNamespace
|
| 24 |
+
|
| 25 |
+
__all__ = 'dataclass', 'rebuild_dataclass'
|
| 26 |
+
|
| 27 |
+
_T = TypeVar('_T')
|
| 28 |
+
|
| 29 |
+
if sys.version_info >= (3, 10):
|
| 30 |
+
|
| 31 |
+
@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
|
| 32 |
+
@overload
|
| 33 |
+
def dataclass(
|
| 34 |
+
*,
|
| 35 |
+
init: Literal[False] = False,
|
| 36 |
+
repr: bool = True,
|
| 37 |
+
eq: bool = True,
|
| 38 |
+
order: bool = False,
|
| 39 |
+
unsafe_hash: bool = False,
|
| 40 |
+
frozen: bool = False,
|
| 41 |
+
config: ConfigDict | type[object] | None = None,
|
| 42 |
+
validate_on_init: bool | None = None,
|
| 43 |
+
kw_only: bool = ...,
|
| 44 |
+
slots: bool = ...,
|
| 45 |
+
) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore
|
| 46 |
+
...
|
| 47 |
+
|
| 48 |
+
@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
|
| 49 |
+
@overload
|
| 50 |
+
def dataclass(
|
| 51 |
+
_cls: type[_T], # type: ignore
|
| 52 |
+
*,
|
| 53 |
+
init: Literal[False] = False,
|
| 54 |
+
repr: bool = True,
|
| 55 |
+
eq: bool = True,
|
| 56 |
+
order: bool = False,
|
| 57 |
+
unsafe_hash: bool = False,
|
| 58 |
+
frozen: bool | None = None,
|
| 59 |
+
config: ConfigDict | type[object] | None = None,
|
| 60 |
+
validate_on_init: bool | None = None,
|
| 61 |
+
kw_only: bool = ...,
|
| 62 |
+
slots: bool = ...,
|
| 63 |
+
) -> type[PydanticDataclass]: ...
|
| 64 |
+
|
| 65 |
+
else:
|
| 66 |
+
|
| 67 |
+
@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
|
| 68 |
+
@overload
|
| 69 |
+
def dataclass(
|
| 70 |
+
*,
|
| 71 |
+
init: Literal[False] = False,
|
| 72 |
+
repr: bool = True,
|
| 73 |
+
eq: bool = True,
|
| 74 |
+
order: bool = False,
|
| 75 |
+
unsafe_hash: bool = False,
|
| 76 |
+
frozen: bool | None = None,
|
| 77 |
+
config: ConfigDict | type[object] | None = None,
|
| 78 |
+
validate_on_init: bool | None = None,
|
| 79 |
+
) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore
|
| 80 |
+
...
|
| 81 |
+
|
| 82 |
+
@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
|
| 83 |
+
@overload
|
| 84 |
+
def dataclass(
|
| 85 |
+
_cls: type[_T], # type: ignore
|
| 86 |
+
*,
|
| 87 |
+
init: Literal[False] = False,
|
| 88 |
+
repr: bool = True,
|
| 89 |
+
eq: bool = True,
|
| 90 |
+
order: bool = False,
|
| 91 |
+
unsafe_hash: bool = False,
|
| 92 |
+
frozen: bool | None = None,
|
| 93 |
+
config: ConfigDict | type[object] | None = None,
|
| 94 |
+
validate_on_init: bool | None = None,
|
| 95 |
+
) -> type[PydanticDataclass]: ...
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
|
| 99 |
+
def dataclass(
|
| 100 |
+
_cls: type[_T] | None = None,
|
| 101 |
+
*,
|
| 102 |
+
init: Literal[False] = False,
|
| 103 |
+
repr: bool = True,
|
| 104 |
+
eq: bool = True,
|
| 105 |
+
order: bool = False,
|
| 106 |
+
unsafe_hash: bool = False,
|
| 107 |
+
frozen: bool | None = None,
|
| 108 |
+
config: ConfigDict | type[object] | None = None,
|
| 109 |
+
validate_on_init: bool | None = None,
|
| 110 |
+
kw_only: bool = False,
|
| 111 |
+
slots: bool = False,
|
| 112 |
+
) -> Callable[[type[_T]], type[PydanticDataclass]] | type[PydanticDataclass]:
|
| 113 |
+
"""!!! abstract "Usage Documentation"
|
| 114 |
+
[`dataclasses`](../concepts/dataclasses.md)
|
| 115 |
+
|
| 116 |
+
A decorator used to create a Pydantic-enhanced dataclass, similar to the standard Python `dataclass`,
|
| 117 |
+
but with added validation.
|
| 118 |
+
|
| 119 |
+
This function should be used similarly to `dataclasses.dataclass`.
|
| 120 |
+
|
| 121 |
+
Args:
|
| 122 |
+
_cls: The target `dataclass`.
|
| 123 |
+
init: Included for signature compatibility with `dataclasses.dataclass`, and is passed through to
|
| 124 |
+
`dataclasses.dataclass` when appropriate. If specified, must be set to `False`, as pydantic inserts its
|
| 125 |
+
own `__init__` function.
|
| 126 |
+
repr: A boolean indicating whether to include the field in the `__repr__` output.
|
| 127 |
+
eq: Determines if a `__eq__` method should be generated for the class.
|
| 128 |
+
order: Determines if comparison magic methods should be generated, such as `__lt__`, but not `__eq__`.
|
| 129 |
+
unsafe_hash: Determines if a `__hash__` method should be included in the class, as in `dataclasses.dataclass`.
|
| 130 |
+
frozen: Determines if the generated class should be a 'frozen' `dataclass`, which does not allow its
|
| 131 |
+
attributes to be modified after it has been initialized. If not set, the value from the provided `config` argument will be used (and will default to `False` otherwise).
|
| 132 |
+
config: The Pydantic config to use for the `dataclass`.
|
| 133 |
+
validate_on_init: A deprecated parameter included for backwards compatibility; in V2, all Pydantic dataclasses
|
| 134 |
+
are validated on init.
|
| 135 |
+
kw_only: Determines if `__init__` method parameters must be specified by keyword only. Defaults to `False`.
|
| 136 |
+
slots: Determines if the generated class should be a 'slots' `dataclass`, which does not allow the addition of
|
| 137 |
+
new attributes after instantiation.
|
| 138 |
+
|
| 139 |
+
Returns:
|
| 140 |
+
A decorator that accepts a class as its argument and returns a Pydantic `dataclass`.
|
| 141 |
+
|
| 142 |
+
Raises:
|
| 143 |
+
AssertionError: Raised if `init` is not `False` or `validate_on_init` is `False`.
|
| 144 |
+
"""
|
| 145 |
+
assert init is False, 'pydantic.dataclasses.dataclass only supports init=False'
|
| 146 |
+
assert validate_on_init is not False, 'validate_on_init=False is no longer supported'
|
| 147 |
+
|
| 148 |
+
if sys.version_info >= (3, 10):
|
| 149 |
+
kwargs = {'kw_only': kw_only, 'slots': slots}
|
| 150 |
+
else:
|
| 151 |
+
kwargs = {}
|
| 152 |
+
|
| 153 |
+
def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
|
| 154 |
+
"""Create a Pydantic dataclass from a regular dataclass.
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
cls: The class to create the Pydantic dataclass from.
|
| 158 |
+
|
| 159 |
+
Returns:
|
| 160 |
+
A Pydantic dataclass.
|
| 161 |
+
"""
|
| 162 |
+
from ._internal._utils import is_model_class
|
| 163 |
+
|
| 164 |
+
if is_model_class(cls):
|
| 165 |
+
raise PydanticUserError(
|
| 166 |
+
f'Cannot create a Pydantic dataclass from {cls.__name__} as it is already a Pydantic model',
|
| 167 |
+
code='dataclass-on-model',
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
original_cls = cls
|
| 171 |
+
|
| 172 |
+
# we warn on conflicting config specifications, but only if the class doesn't have a dataclass base
|
| 173 |
+
# because a dataclass base might provide a __pydantic_config__ attribute that we don't want to warn about
|
| 174 |
+
has_dataclass_base = any(dataclasses.is_dataclass(base) for base in cls.__bases__)
|
| 175 |
+
if not has_dataclass_base and config is not None and hasattr(cls, '__pydantic_config__'):
|
| 176 |
+
warn(
|
| 177 |
+
f'`config` is set via both the `dataclass` decorator and `__pydantic_config__` for dataclass {cls.__name__}. '
|
| 178 |
+
f'The `config` specification from `dataclass` decorator will take priority.',
|
| 179 |
+
category=UserWarning,
|
| 180 |
+
stacklevel=2,
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
# if config is not explicitly provided, try to read it from the type
|
| 184 |
+
config_dict = config if config is not None else getattr(cls, '__pydantic_config__', None)
|
| 185 |
+
config_wrapper = _config.ConfigWrapper(config_dict)
|
| 186 |
+
decorators = _decorators.DecoratorInfos.build(cls, replace_wrapped_methods=True)
|
| 187 |
+
decorators.update_from_config(config_wrapper)
|
| 188 |
+
|
| 189 |
+
# Keep track of the original __doc__ so that we can restore it after applying the dataclasses decorator
|
| 190 |
+
# Otherwise, classes with no __doc__ will have their signature added into the JSON schema description,
|
| 191 |
+
# since dataclasses.dataclass will set this as the __doc__
|
| 192 |
+
original_doc = cls.__doc__
|
| 193 |
+
|
| 194 |
+
if _pydantic_dataclasses.is_stdlib_dataclass(cls):
|
| 195 |
+
# Vanilla dataclasses include a default docstring (representing the class signature),
|
| 196 |
+
# which we don't want to preserve.
|
| 197 |
+
original_doc = None
|
| 198 |
+
|
| 199 |
+
# We don't want to add validation to the existing std lib dataclass, so we will subclass it
|
| 200 |
+
# If the class is generic, we need to make sure the subclass also inherits from Generic
|
| 201 |
+
# with all the same parameters.
|
| 202 |
+
bases = (cls,)
|
| 203 |
+
if issubclass(cls, Generic):
|
| 204 |
+
generic_base = Generic[cls.__parameters__] # type: ignore
|
| 205 |
+
bases = bases + (generic_base,)
|
| 206 |
+
cls = types.new_class(cls.__name__, bases)
|
| 207 |
+
|
| 208 |
+
# Respect frozen setting from dataclass constructor and fallback to config setting if not provided
|
| 209 |
+
if frozen is not None:
|
| 210 |
+
frozen_ = frozen
|
| 211 |
+
if config_wrapper.frozen:
|
| 212 |
+
# It's not recommended to define both, as the setting from the dataclass decorator will take priority.
|
| 213 |
+
warn(
|
| 214 |
+
f'`frozen` is set via both the `dataclass` decorator and `config` for dataclass {cls.__name__!r}.'
|
| 215 |
+
'This is not recommended. The `frozen` specification on `dataclass` will take priority.',
|
| 216 |
+
category=UserWarning,
|
| 217 |
+
stacklevel=2,
|
| 218 |
+
)
|
| 219 |
+
else:
|
| 220 |
+
frozen_ = config_wrapper.frozen or False
|
| 221 |
+
|
| 222 |
+
# Make Pydantic's `Field()` function compatible with stdlib dataclasses. As we'll decorate
|
| 223 |
+
# `cls` with the stdlib `@dataclass` decorator first, there are two attributes, `kw_only` and
|
| 224 |
+
# `repr` that need to be understood *during* the stdlib creation. We do so in two steps:
|
| 225 |
+
|
| 226 |
+
# 1. On the decorated class, wrap `Field()` assignment with `dataclass.field()`, with the
|
| 227 |
+
# two attributes set (done in `as_dataclass_field()`)
|
| 228 |
+
cls_anns = _typing_extra.safe_get_annotations(cls)
|
| 229 |
+
for field_name in cls_anns:
|
| 230 |
+
# We should look for assignments in `__dict__` instead, but for now we follow
|
| 231 |
+
# the same behavior as stdlib dataclasses (see https://github.com/python/cpython/issues/88609)
|
| 232 |
+
field_value = getattr(cls, field_name, None)
|
| 233 |
+
if isinstance(field_value, FieldInfo):
|
| 234 |
+
setattr(cls, field_name, _pydantic_dataclasses.as_dataclass_field(field_value))
|
| 235 |
+
|
| 236 |
+
# 2. For bases of `cls` that are stdlib dataclasses, we temporarily patch their fields
|
| 237 |
+
# (see the docstring of the context manager):
|
| 238 |
+
with _pydantic_dataclasses.patch_base_fields(cls):
|
| 239 |
+
cls = dataclasses.dataclass( # pyright: ignore[reportCallIssue]
|
| 240 |
+
cls,
|
| 241 |
+
# the value of init here doesn't affect anything except that it makes it easier to generate a signature
|
| 242 |
+
init=True,
|
| 243 |
+
repr=repr,
|
| 244 |
+
eq=eq,
|
| 245 |
+
order=order,
|
| 246 |
+
unsafe_hash=unsafe_hash,
|
| 247 |
+
frozen=frozen_,
|
| 248 |
+
**kwargs,
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
if config_wrapper.validate_assignment:
|
| 252 |
+
original_setattr = cls.__setattr__
|
| 253 |
+
|
| 254 |
+
@functools.wraps(cls.__setattr__)
|
| 255 |
+
def validated_setattr(instance: PydanticDataclass, name: str, value: Any, /) -> None:
|
| 256 |
+
if frozen_:
|
| 257 |
+
return original_setattr(instance, name, value) # pyright: ignore[reportCallIssue]
|
| 258 |
+
inst_cls = type(instance)
|
| 259 |
+
attr = getattr(inst_cls, name, None)
|
| 260 |
+
|
| 261 |
+
if isinstance(attr, property):
|
| 262 |
+
attr.__set__(instance, value)
|
| 263 |
+
elif isinstance(attr, functools.cached_property):
|
| 264 |
+
instance.__dict__.__setitem__(name, value)
|
| 265 |
+
else:
|
| 266 |
+
inst_cls.__pydantic_validator__.validate_assignment(instance, name, value)
|
| 267 |
+
|
| 268 |
+
cls.__setattr__ = validated_setattr.__get__(None, cls) # type: ignore
|
| 269 |
+
|
| 270 |
+
if slots and not hasattr(cls, '__setstate__'):
|
| 271 |
+
# If slots is set, `pickle` (relied on by `copy.copy()`) will use
|
| 272 |
+
# `__setattr__()` to reconstruct the dataclass. However, the custom
|
| 273 |
+
# `__setattr__()` set above relies on `validate_assignment()`, which
|
| 274 |
+
# in turn expects all the field values to be already present on the
|
| 275 |
+
# instance, resulting in attribute errors.
|
| 276 |
+
# As such, we make use of `object.__setattr__()` instead.
|
| 277 |
+
# Note that we do so only if `__setstate__()` isn't already set (this is the
|
| 278 |
+
# case if on top of `slots`, `frozen` is used).
|
| 279 |
+
|
| 280 |
+
# Taken from `dataclasses._dataclass_get/setstate()`:
|
| 281 |
+
def _dataclass_getstate(self: Any) -> list[Any]:
|
| 282 |
+
return [getattr(self, f.name) for f in dataclasses.fields(self)]
|
| 283 |
+
|
| 284 |
+
def _dataclass_setstate(self: Any, state: list[Any]) -> None:
|
| 285 |
+
for field, value in zip(dataclasses.fields(self), state):
|
| 286 |
+
object.__setattr__(self, field.name, value)
|
| 287 |
+
|
| 288 |
+
cls.__getstate__ = _dataclass_getstate # pyright: ignore[reportAttributeAccessIssue]
|
| 289 |
+
cls.__setstate__ = _dataclass_setstate # pyright: ignore[reportAttributeAccessIssue]
|
| 290 |
+
|
| 291 |
+
# This is an undocumented attribute to distinguish stdlib/Pydantic dataclasses.
|
| 292 |
+
# It should be set as early as possible:
|
| 293 |
+
cls.__is_pydantic_dataclass__ = True
|
| 294 |
+
cls.__pydantic_decorators__ = decorators # type: ignore
|
| 295 |
+
cls.__doc__ = original_doc
|
| 296 |
+
# Can be non-existent for dynamically created classes:
|
| 297 |
+
firstlineno = getattr(original_cls, '__firstlineno__', None)
|
| 298 |
+
cls.__module__ = original_cls.__module__
|
| 299 |
+
if sys.version_info >= (3, 13) and firstlineno is not None:
|
| 300 |
+
# As per https://docs.python.org/3/reference/datamodel.html#type.__firstlineno__:
|
| 301 |
+
# Setting the `__module__` attribute removes the `__firstlineno__` item from the typeβs dictionary.
|
| 302 |
+
original_cls.__firstlineno__ = firstlineno
|
| 303 |
+
cls.__firstlineno__ = firstlineno
|
| 304 |
+
cls.__qualname__ = original_cls.__qualname__
|
| 305 |
+
cls.__pydantic_fields_complete__ = classmethod(_pydantic_fields_complete)
|
| 306 |
+
cls.__pydantic_complete__ = False # `complete_dataclass` will set it to `True` if successful.
|
| 307 |
+
# TODO `parent_namespace` is currently None, but we could do the same thing as Pydantic models:
|
| 308 |
+
# fetch the parent ns using `parent_frame_namespace` (if the dataclass was defined in a function),
|
| 309 |
+
# and possibly cache it (see the `__pydantic_parent_namespace__` logic for models).
|
| 310 |
+
_pydantic_dataclasses.complete_dataclass(cls, config_wrapper, raise_errors=False)
|
| 311 |
+
return cls
|
| 312 |
+
|
| 313 |
+
return create_dataclass if _cls is None else create_dataclass(_cls)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def _pydantic_fields_complete(cls: type[PydanticDataclass]) -> bool:
|
| 317 |
+
"""Return whether the fields were successfully collected (i.e. type hints were successfully resolved).
|
| 318 |
+
|
| 319 |
+
This is a private helper, not meant to be used outside Pydantic.
|
| 320 |
+
"""
|
| 321 |
+
return all(field_info._complete for field_info in cls.__pydantic_fields__.values())
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
__getattr__ = getattr_migration(__name__)
|
| 325 |
+
|
| 326 |
+
if sys.version_info < (3, 11):
|
| 327 |
+
# Monkeypatch dataclasses.InitVar so that typing doesn't error if it occurs as a type when evaluating type hints
|
| 328 |
+
# Starting in 3.11, typing.get_type_hints will not raise an error if the retrieved type hints are not callable.
|
| 329 |
+
|
| 330 |
+
def _call_initvar(*args: Any, **kwargs: Any) -> NoReturn:
|
| 331 |
+
"""This function does nothing but raise an error that is as similar as possible to what you'd get
|
| 332 |
+
if you were to try calling `InitVar[int]()` without this monkeypatch. The whole purpose is just
|
| 333 |
+
to ensure typing._type_check does not error if the type hint evaluates to `InitVar[<parameter>]`.
|
| 334 |
+
"""
|
| 335 |
+
raise TypeError("'InitVar' object is not callable")
|
| 336 |
+
|
| 337 |
+
dataclasses.InitVar.__call__ = _call_initvar
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def rebuild_dataclass(
|
| 341 |
+
cls: type[PydanticDataclass],
|
| 342 |
+
*,
|
| 343 |
+
force: bool = False,
|
| 344 |
+
raise_errors: bool = True,
|
| 345 |
+
_parent_namespace_depth: int = 2,
|
| 346 |
+
_types_namespace: MappingNamespace | None = None,
|
| 347 |
+
) -> bool | None:
|
| 348 |
+
"""Try to rebuild the pydantic-core schema for the dataclass.
|
| 349 |
+
|
| 350 |
+
This may be necessary when one of the annotations is a ForwardRef which could not be resolved during
|
| 351 |
+
the initial attempt to build the schema, and automatic rebuilding fails.
|
| 352 |
+
|
| 353 |
+
This is analogous to `BaseModel.model_rebuild`.
|
| 354 |
+
|
| 355 |
+
Args:
|
| 356 |
+
cls: The class to rebuild the pydantic-core schema for.
|
| 357 |
+
force: Whether to force the rebuilding of the schema, defaults to `False`.
|
| 358 |
+
raise_errors: Whether to raise errors, defaults to `True`.
|
| 359 |
+
_parent_namespace_depth: The depth level of the parent namespace, defaults to 2.
|
| 360 |
+
_types_namespace: The types namespace, defaults to `None`.
|
| 361 |
+
|
| 362 |
+
Returns:
|
| 363 |
+
Returns `None` if the schema is already "complete" and rebuilding was not required.
|
| 364 |
+
If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`.
|
| 365 |
+
"""
|
| 366 |
+
if not force and cls.__pydantic_complete__:
|
| 367 |
+
return None
|
| 368 |
+
|
| 369 |
+
for attr in ('__pydantic_core_schema__', '__pydantic_validator__', '__pydantic_serializer__'):
|
| 370 |
+
if attr in cls.__dict__ and not isinstance(getattr(cls, attr), _mock_val_ser.MockValSer):
|
| 371 |
+
# Deleting the validator/serializer is necessary as otherwise they can get reused in
|
| 372 |
+
# pydantic-core. Same applies for the core schema that can be reused in schema generation.
|
| 373 |
+
delattr(cls, attr)
|
| 374 |
+
|
| 375 |
+
cls.__pydantic_complete__ = False
|
| 376 |
+
|
| 377 |
+
if _types_namespace is not None:
|
| 378 |
+
rebuild_ns = _types_namespace
|
| 379 |
+
elif _parent_namespace_depth > 0:
|
| 380 |
+
rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {}
|
| 381 |
+
else:
|
| 382 |
+
rebuild_ns = {}
|
| 383 |
+
|
| 384 |
+
ns_resolver = _namespace_utils.NsResolver(
|
| 385 |
+
parent_namespace=rebuild_ns,
|
| 386 |
+
)
|
| 387 |
+
|
| 388 |
+
return _pydantic_dataclasses.complete_dataclass(
|
| 389 |
+
cls,
|
| 390 |
+
_config.ConfigWrapper(cls.__pydantic_config__, check=False),
|
| 391 |
+
raise_errors=raise_errors,
|
| 392 |
+
ns_resolver=ns_resolver,
|
| 393 |
+
# We could provide a different config instead (with `'defer_build'` set to `True`)
|
| 394 |
+
# of this explicit `_force_build` argument, but because config can come from the
|
| 395 |
+
# decorator parameter or the `__pydantic_config__` attribute, `complete_dataclass`
|
| 396 |
+
# will overwrite `__pydantic_config__` with the provided config above:
|
| 397 |
+
_force_build=True,
|
| 398 |
+
)
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def is_pydantic_dataclass(class_: type[Any], /) -> TypeGuard[type[PydanticDataclass]]:
|
| 402 |
+
"""Whether a class is a pydantic dataclass.
|
| 403 |
+
|
| 404 |
+
Args:
|
| 405 |
+
class_: The class.
|
| 406 |
+
|
| 407 |
+
Returns:
|
| 408 |
+
`True` if the class is a pydantic dataclass, `False` otherwise.
|
| 409 |
+
"""
|
| 410 |
+
try:
|
| 411 |
+
return '__is_pydantic_dataclass__' in class_.__dict__ and dataclasses.is_dataclass(class_)
|
| 412 |
+
except AttributeError:
|
| 413 |
+
return False
|
.mcp-venv/lib/python3.13/site-packages/pydantic/datetime_parse.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The `datetime_parse` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/decorator.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The `decorator` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/__init__.py
ADDED
|
File without changes
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Old `@validator` and `@root_validator` function validators from V1."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
from functools import partial, partialmethod
|
| 6 |
+
from types import FunctionType
|
| 7 |
+
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union, overload
|
| 8 |
+
from warnings import warn
|
| 9 |
+
|
| 10 |
+
from typing_extensions import Protocol, TypeAlias, deprecated
|
| 11 |
+
|
| 12 |
+
from .._internal import _decorators, _decorators_v1
|
| 13 |
+
from ..errors import PydanticUserError
|
| 14 |
+
from ..warnings import PydanticDeprecatedSince20
|
| 15 |
+
|
| 16 |
+
_ALLOW_REUSE_WARNING_MESSAGE = '`allow_reuse` is deprecated and will be ignored; it should no longer be necessary'
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
|
| 21 |
+
class _OnlyValueValidatorClsMethod(Protocol):
|
| 22 |
+
def __call__(self, __cls: Any, __value: Any) -> Any: ...
|
| 23 |
+
|
| 24 |
+
class _V1ValidatorWithValuesClsMethod(Protocol):
|
| 25 |
+
def __call__(self, __cls: Any, __value: Any, values: dict[str, Any]) -> Any: ...
|
| 26 |
+
|
| 27 |
+
class _V1ValidatorWithValuesKwOnlyClsMethod(Protocol):
|
| 28 |
+
def __call__(self, __cls: Any, __value: Any, *, values: dict[str, Any]) -> Any: ...
|
| 29 |
+
|
| 30 |
+
class _V1ValidatorWithKwargsClsMethod(Protocol):
|
| 31 |
+
def __call__(self, __cls: Any, **kwargs: Any) -> Any: ...
|
| 32 |
+
|
| 33 |
+
class _V1ValidatorWithValuesAndKwargsClsMethod(Protocol):
|
| 34 |
+
def __call__(self, __cls: Any, values: dict[str, Any], **kwargs: Any) -> Any: ...
|
| 35 |
+
|
| 36 |
+
class _V1RootValidatorClsMethod(Protocol):
|
| 37 |
+
def __call__(
|
| 38 |
+
self, __cls: Any, __values: _decorators_v1.RootValidatorValues
|
| 39 |
+
) -> _decorators_v1.RootValidatorValues: ...
|
| 40 |
+
|
| 41 |
+
V1Validator = Union[
|
| 42 |
+
_OnlyValueValidatorClsMethod,
|
| 43 |
+
_V1ValidatorWithValuesClsMethod,
|
| 44 |
+
_V1ValidatorWithValuesKwOnlyClsMethod,
|
| 45 |
+
_V1ValidatorWithKwargsClsMethod,
|
| 46 |
+
_V1ValidatorWithValuesAndKwargsClsMethod,
|
| 47 |
+
_decorators_v1.V1ValidatorWithValues,
|
| 48 |
+
_decorators_v1.V1ValidatorWithValuesKwOnly,
|
| 49 |
+
_decorators_v1.V1ValidatorWithKwargs,
|
| 50 |
+
_decorators_v1.V1ValidatorWithValuesAndKwargs,
|
| 51 |
+
]
|
| 52 |
+
|
| 53 |
+
V1RootValidator = Union[
|
| 54 |
+
_V1RootValidatorClsMethod,
|
| 55 |
+
_decorators_v1.V1RootValidatorFunction,
|
| 56 |
+
]
|
| 57 |
+
|
| 58 |
+
_PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
|
| 59 |
+
|
| 60 |
+
# Allow both a V1 (assumed pre=False) or V2 (assumed mode='after') validator
|
| 61 |
+
# We lie to type checkers and say we return the same thing we get
|
| 62 |
+
# but in reality we return a proxy object that _mostly_ behaves like the wrapped thing
|
| 63 |
+
_V1ValidatorType = TypeVar('_V1ValidatorType', V1Validator, _PartialClsOrStaticMethod)
|
| 64 |
+
_V1RootValidatorFunctionType = TypeVar(
|
| 65 |
+
'_V1RootValidatorFunctionType',
|
| 66 |
+
_decorators_v1.V1RootValidatorFunction,
|
| 67 |
+
_V1RootValidatorClsMethod,
|
| 68 |
+
_PartialClsOrStaticMethod,
|
| 69 |
+
)
|
| 70 |
+
else:
|
| 71 |
+
# See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
|
| 72 |
+
# and https://youtrack.jetbrains.com/issue/PY-51428
|
| 73 |
+
DeprecationWarning = PydanticDeprecatedSince20
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
@deprecated(
|
| 77 |
+
'Pydantic V1 style `@validator` validators are deprecated.'
|
| 78 |
+
' You should migrate to Pydantic V2 style `@field_validator` validators,'
|
| 79 |
+
' see the migration guide for more details',
|
| 80 |
+
category=None,
|
| 81 |
+
)
|
| 82 |
+
def validator(
|
| 83 |
+
__field: str,
|
| 84 |
+
*fields: str,
|
| 85 |
+
pre: bool = False,
|
| 86 |
+
each_item: bool = False,
|
| 87 |
+
always: bool = False,
|
| 88 |
+
check_fields: bool | None = None,
|
| 89 |
+
allow_reuse: bool = False,
|
| 90 |
+
) -> Callable[[_V1ValidatorType], _V1ValidatorType]:
|
| 91 |
+
"""Decorate methods on the class indicating that they should be used to validate fields.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
__field (str): The first field the validator should be called on; this is separate
|
| 95 |
+
from `fields` to ensure an error is raised if you don't pass at least one.
|
| 96 |
+
*fields (str): Additional field(s) the validator should be called on.
|
| 97 |
+
pre (bool, optional): Whether this validator should be called before the standard
|
| 98 |
+
validators (else after). Defaults to False.
|
| 99 |
+
each_item (bool, optional): For complex objects (sets, lists etc.) whether to validate
|
| 100 |
+
individual elements rather than the whole object. Defaults to False.
|
| 101 |
+
always (bool, optional): Whether this method and other validators should be called even if
|
| 102 |
+
the value is missing. Defaults to False.
|
| 103 |
+
check_fields (bool | None, optional): Whether to check that the fields actually exist on the model.
|
| 104 |
+
Defaults to None.
|
| 105 |
+
allow_reuse (bool, optional): Whether to track and raise an error if another validator refers to
|
| 106 |
+
the decorated function. Defaults to False.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
Callable: A decorator that can be used to decorate a
|
| 110 |
+
function to be used as a validator.
|
| 111 |
+
"""
|
| 112 |
+
warn(
|
| 113 |
+
'Pydantic V1 style `@validator` validators are deprecated.'
|
| 114 |
+
' You should migrate to Pydantic V2 style `@field_validator` validators,'
|
| 115 |
+
' see the migration guide for more details',
|
| 116 |
+
DeprecationWarning,
|
| 117 |
+
stacklevel=2,
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
if allow_reuse is True: # pragma: no cover
|
| 121 |
+
warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
|
| 122 |
+
fields = __field, *fields
|
| 123 |
+
if isinstance(fields[0], FunctionType):
|
| 124 |
+
raise PydanticUserError(
|
| 125 |
+
'`@validator` should be used with fields and keyword arguments, not bare. '
|
| 126 |
+
"E.g. usage should be `@validator('<field_name>', ...)`",
|
| 127 |
+
code='decorator-missing-arguments',
|
| 128 |
+
)
|
| 129 |
+
elif not all(isinstance(field, str) for field in fields):
|
| 130 |
+
raise PydanticUserError(
|
| 131 |
+
'`@validator` fields should be passed as separate string args. '
|
| 132 |
+
"E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
|
| 133 |
+
code='decorator-invalid-fields',
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
|
| 137 |
+
|
| 138 |
+
def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
|
| 139 |
+
if _decorators.is_instance_method_from_sig(f):
|
| 140 |
+
raise PydanticUserError(
|
| 141 |
+
'`@validator` cannot be applied to instance methods', code='validator-instance-method'
|
| 142 |
+
)
|
| 143 |
+
# auto apply the @classmethod decorator
|
| 144 |
+
f = _decorators.ensure_classmethod_based_on_signature(f)
|
| 145 |
+
wrap = _decorators_v1.make_generic_v1_field_validator
|
| 146 |
+
validator_wrapper_info = _decorators.ValidatorDecoratorInfo(
|
| 147 |
+
fields=fields,
|
| 148 |
+
mode=mode,
|
| 149 |
+
each_item=each_item,
|
| 150 |
+
always=always,
|
| 151 |
+
check_fields=check_fields,
|
| 152 |
+
)
|
| 153 |
+
return _decorators.PydanticDescriptorProxy(f, validator_wrapper_info, shim=wrap)
|
| 154 |
+
|
| 155 |
+
return dec # type: ignore[return-value]
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
@overload
|
| 159 |
+
def root_validator(
|
| 160 |
+
*,
|
| 161 |
+
# if you don't specify `pre` the default is `pre=False`
|
| 162 |
+
# which means you need to specify `skip_on_failure=True`
|
| 163 |
+
skip_on_failure: Literal[True],
|
| 164 |
+
allow_reuse: bool = ...,
|
| 165 |
+
) -> Callable[
|
| 166 |
+
[_V1RootValidatorFunctionType],
|
| 167 |
+
_V1RootValidatorFunctionType,
|
| 168 |
+
]: ...
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@overload
|
| 172 |
+
def root_validator(
|
| 173 |
+
*,
|
| 174 |
+
# if you specify `pre=True` then you don't need to specify
|
| 175 |
+
# `skip_on_failure`, in fact it is not allowed as an argument!
|
| 176 |
+
pre: Literal[True],
|
| 177 |
+
allow_reuse: bool = ...,
|
| 178 |
+
) -> Callable[
|
| 179 |
+
[_V1RootValidatorFunctionType],
|
| 180 |
+
_V1RootValidatorFunctionType,
|
| 181 |
+
]: ...
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
@overload
|
| 185 |
+
def root_validator(
|
| 186 |
+
*,
|
| 187 |
+
# if you explicitly specify `pre=False` then you
|
| 188 |
+
# MUST specify `skip_on_failure=True`
|
| 189 |
+
pre: Literal[False],
|
| 190 |
+
skip_on_failure: Literal[True],
|
| 191 |
+
allow_reuse: bool = ...,
|
| 192 |
+
) -> Callable[
|
| 193 |
+
[_V1RootValidatorFunctionType],
|
| 194 |
+
_V1RootValidatorFunctionType,
|
| 195 |
+
]: ...
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@deprecated(
|
| 199 |
+
'Pydantic V1 style `@root_validator` validators are deprecated.'
|
| 200 |
+
' You should migrate to Pydantic V2 style `@model_validator` validators,'
|
| 201 |
+
' see the migration guide for more details',
|
| 202 |
+
category=None,
|
| 203 |
+
)
|
| 204 |
+
def root_validator(
|
| 205 |
+
*__args,
|
| 206 |
+
pre: bool = False,
|
| 207 |
+
skip_on_failure: bool = False,
|
| 208 |
+
allow_reuse: bool = False,
|
| 209 |
+
) -> Any:
|
| 210 |
+
"""Decorate methods on a model indicating that they should be used to validate (and perhaps
|
| 211 |
+
modify) data either before or after standard model parsing/validation is performed.
|
| 212 |
+
|
| 213 |
+
Args:
|
| 214 |
+
pre (bool, optional): Whether this validator should be called before the standard
|
| 215 |
+
validators (else after). Defaults to False.
|
| 216 |
+
skip_on_failure (bool, optional): Whether to stop validation and return as soon as a
|
| 217 |
+
failure is encountered. Defaults to False.
|
| 218 |
+
allow_reuse (bool, optional): Whether to track and raise an error if another validator
|
| 219 |
+
refers to the decorated function. Defaults to False.
|
| 220 |
+
|
| 221 |
+
Returns:
|
| 222 |
+
Any: A decorator that can be used to decorate a function to be used as a root_validator.
|
| 223 |
+
"""
|
| 224 |
+
warn(
|
| 225 |
+
'Pydantic V1 style `@root_validator` validators are deprecated.'
|
| 226 |
+
' You should migrate to Pydantic V2 style `@model_validator` validators,'
|
| 227 |
+
' see the migration guide for more details',
|
| 228 |
+
DeprecationWarning,
|
| 229 |
+
stacklevel=2,
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
if __args:
|
| 233 |
+
# Ensure a nice error is raised if someone attempts to use the bare decorator
|
| 234 |
+
return root_validator()(*__args) # type: ignore
|
| 235 |
+
|
| 236 |
+
if allow_reuse is True: # pragma: no cover
|
| 237 |
+
warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
|
| 238 |
+
mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
|
| 239 |
+
if pre is False and skip_on_failure is not True:
|
| 240 |
+
raise PydanticUserError(
|
| 241 |
+
'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.'
|
| 242 |
+
' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.',
|
| 243 |
+
code='root-validator-pre-skip',
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre)
|
| 247 |
+
|
| 248 |
+
def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any:
|
| 249 |
+
if _decorators.is_instance_method_from_sig(f):
|
| 250 |
+
raise TypeError('`@root_validator` cannot be applied to instance methods')
|
| 251 |
+
# auto apply the @classmethod decorator
|
| 252 |
+
res = _decorators.ensure_classmethod_based_on_signature(f)
|
| 253 |
+
dec_info = _decorators.RootValidatorDecoratorInfo(mode=mode)
|
| 254 |
+
return _decorators.PydanticDescriptorProxy(res, dec_info, shim=wrap)
|
| 255 |
+
|
| 256 |
+
return dec
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/config.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations as _annotations
|
| 2 |
+
|
| 3 |
+
import warnings
|
| 4 |
+
from typing import TYPE_CHECKING, Any, Literal
|
| 5 |
+
|
| 6 |
+
from typing_extensions import deprecated
|
| 7 |
+
|
| 8 |
+
from .._internal import _config
|
| 9 |
+
from ..warnings import PydanticDeprecatedSince20
|
| 10 |
+
|
| 11 |
+
if not TYPE_CHECKING:
|
| 12 |
+
# See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
|
| 13 |
+
# and https://youtrack.jetbrains.com/issue/PY-51428
|
| 14 |
+
DeprecationWarning = PydanticDeprecatedSince20
|
| 15 |
+
|
| 16 |
+
__all__ = 'BaseConfig', 'Extra'
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class _ConfigMetaclass(type):
|
| 20 |
+
def __getattr__(self, item: str) -> Any:
|
| 21 |
+
try:
|
| 22 |
+
obj = _config.config_defaults[item]
|
| 23 |
+
warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning)
|
| 24 |
+
return obj
|
| 25 |
+
except KeyError as exc:
|
| 26 |
+
raise AttributeError(f"type object '{self.__name__}' has no attribute {exc}") from exc
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@deprecated('BaseConfig is deprecated. Use the `pydantic.ConfigDict` instead.', category=PydanticDeprecatedSince20)
|
| 30 |
+
class BaseConfig(metaclass=_ConfigMetaclass):
|
| 31 |
+
"""This class is only retained for backwards compatibility.
|
| 32 |
+
|
| 33 |
+
!!! Warning "Deprecated"
|
| 34 |
+
BaseConfig is deprecated. Use the [`pydantic.ConfigDict`][pydantic.ConfigDict] instead.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __getattr__(self, item: str) -> Any:
|
| 38 |
+
try:
|
| 39 |
+
obj = super().__getattribute__(item)
|
| 40 |
+
warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning)
|
| 41 |
+
return obj
|
| 42 |
+
except AttributeError as exc:
|
| 43 |
+
try:
|
| 44 |
+
return getattr(type(self), item)
|
| 45 |
+
except AttributeError:
|
| 46 |
+
# re-raising changes the displayed text to reflect that `self` is not a type
|
| 47 |
+
raise AttributeError(str(exc)) from exc
|
| 48 |
+
|
| 49 |
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
| 50 |
+
warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning)
|
| 51 |
+
return super().__init_subclass__(**kwargs)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class _ExtraMeta(type):
|
| 55 |
+
def __getattribute__(self, __name: str) -> Any:
|
| 56 |
+
# The @deprecated decorator accesses other attributes, so we only emit a warning for the expected ones
|
| 57 |
+
if __name in {'allow', 'ignore', 'forbid'}:
|
| 58 |
+
warnings.warn(
|
| 59 |
+
"`pydantic.config.Extra` is deprecated, use literal values instead (e.g. `extra='allow'`)",
|
| 60 |
+
DeprecationWarning,
|
| 61 |
+
stacklevel=2,
|
| 62 |
+
)
|
| 63 |
+
return super().__getattribute__(__name)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
@deprecated(
|
| 67 |
+
"Extra is deprecated. Use literal values instead (e.g. `extra='allow'`)", category=PydanticDeprecatedSince20
|
| 68 |
+
)
|
| 69 |
+
class Extra(metaclass=_ExtraMeta):
|
| 70 |
+
allow: Literal['allow'] = 'allow'
|
| 71 |
+
ignore: Literal['ignore'] = 'ignore'
|
| 72 |
+
forbid: Literal['forbid'] = 'forbid'
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations as _annotations
|
| 2 |
+
|
| 3 |
+
import typing
|
| 4 |
+
from copy import deepcopy
|
| 5 |
+
from enum import Enum
|
| 6 |
+
from typing import Any
|
| 7 |
+
|
| 8 |
+
import typing_extensions
|
| 9 |
+
|
| 10 |
+
from .._internal import (
|
| 11 |
+
_model_construction,
|
| 12 |
+
_typing_extra,
|
| 13 |
+
_utils,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
if typing.TYPE_CHECKING:
|
| 17 |
+
from .. import BaseModel
|
| 18 |
+
from .._internal._utils import AbstractSetIntStr, MappingIntStrAny
|
| 19 |
+
|
| 20 |
+
AnyClassMethod = classmethod[Any, Any, Any]
|
| 21 |
+
TupleGenerator = typing.Generator[tuple[str, Any], None, None]
|
| 22 |
+
Model = typing.TypeVar('Model', bound='BaseModel')
|
| 23 |
+
# should be `set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None`, but mypy can't cope
|
| 24 |
+
IncEx: typing_extensions.TypeAlias = 'set[int] | set[str] | dict[int, Any] | dict[str, Any] | None'
|
| 25 |
+
|
| 26 |
+
_object_setattr = _model_construction.object_setattr
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _iter(
|
| 30 |
+
self: BaseModel,
|
| 31 |
+
to_dict: bool = False,
|
| 32 |
+
by_alias: bool = False,
|
| 33 |
+
include: AbstractSetIntStr | MappingIntStrAny | None = None,
|
| 34 |
+
exclude: AbstractSetIntStr | MappingIntStrAny | None = None,
|
| 35 |
+
exclude_unset: bool = False,
|
| 36 |
+
exclude_defaults: bool = False,
|
| 37 |
+
exclude_none: bool = False,
|
| 38 |
+
) -> TupleGenerator:
|
| 39 |
+
# Merge field set excludes with explicit exclude parameter with explicit overriding field set options.
|
| 40 |
+
# The extra "is not None" guards are not logically necessary but optimizes performance for the simple case.
|
| 41 |
+
if exclude is not None:
|
| 42 |
+
exclude = _utils.ValueItems.merge(
|
| 43 |
+
{k: v.exclude for k, v in self.__pydantic_fields__.items() if v.exclude is not None}, exclude
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
if include is not None:
|
| 47 |
+
include = _utils.ValueItems.merge(dict.fromkeys(self.__pydantic_fields__, True), include, intersect=True)
|
| 48 |
+
|
| 49 |
+
allowed_keys = _calculate_keys(self, include=include, exclude=exclude, exclude_unset=exclude_unset) # type: ignore
|
| 50 |
+
if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none):
|
| 51 |
+
# huge boost for plain _iter()
|
| 52 |
+
yield from self.__dict__.items()
|
| 53 |
+
if self.__pydantic_extra__:
|
| 54 |
+
yield from self.__pydantic_extra__.items()
|
| 55 |
+
return
|
| 56 |
+
|
| 57 |
+
value_exclude = _utils.ValueItems(self, exclude) if exclude is not None else None
|
| 58 |
+
value_include = _utils.ValueItems(self, include) if include is not None else None
|
| 59 |
+
|
| 60 |
+
if self.__pydantic_extra__ is None:
|
| 61 |
+
items = self.__dict__.items()
|
| 62 |
+
else:
|
| 63 |
+
items = list(self.__dict__.items()) + list(self.__pydantic_extra__.items())
|
| 64 |
+
|
| 65 |
+
for field_key, v in items:
|
| 66 |
+
if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None):
|
| 67 |
+
continue
|
| 68 |
+
|
| 69 |
+
if exclude_defaults:
|
| 70 |
+
try:
|
| 71 |
+
field = self.__pydantic_fields__[field_key]
|
| 72 |
+
except KeyError:
|
| 73 |
+
pass
|
| 74 |
+
else:
|
| 75 |
+
if not field.is_required() and field.default == v:
|
| 76 |
+
continue
|
| 77 |
+
|
| 78 |
+
if by_alias and field_key in self.__pydantic_fields__:
|
| 79 |
+
dict_key = self.__pydantic_fields__[field_key].alias or field_key
|
| 80 |
+
else:
|
| 81 |
+
dict_key = field_key
|
| 82 |
+
|
| 83 |
+
if to_dict or value_include or value_exclude:
|
| 84 |
+
v = _get_value(
|
| 85 |
+
type(self),
|
| 86 |
+
v,
|
| 87 |
+
to_dict=to_dict,
|
| 88 |
+
by_alias=by_alias,
|
| 89 |
+
include=value_include and value_include.for_element(field_key),
|
| 90 |
+
exclude=value_exclude and value_exclude.for_element(field_key),
|
| 91 |
+
exclude_unset=exclude_unset,
|
| 92 |
+
exclude_defaults=exclude_defaults,
|
| 93 |
+
exclude_none=exclude_none,
|
| 94 |
+
)
|
| 95 |
+
yield dict_key, v
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def _copy_and_set_values(
|
| 99 |
+
self: Model,
|
| 100 |
+
values: dict[str, Any],
|
| 101 |
+
fields_set: set[str],
|
| 102 |
+
extra: dict[str, Any] | None = None,
|
| 103 |
+
private: dict[str, Any] | None = None,
|
| 104 |
+
*,
|
| 105 |
+
deep: bool, # UP006
|
| 106 |
+
) -> Model:
|
| 107 |
+
if deep:
|
| 108 |
+
# chances of having empty dict here are quite low for using smart_deepcopy
|
| 109 |
+
values = deepcopy(values)
|
| 110 |
+
extra = deepcopy(extra)
|
| 111 |
+
private = deepcopy(private)
|
| 112 |
+
|
| 113 |
+
cls = self.__class__
|
| 114 |
+
m = cls.__new__(cls)
|
| 115 |
+
_object_setattr(m, '__dict__', values)
|
| 116 |
+
_object_setattr(m, '__pydantic_extra__', extra)
|
| 117 |
+
_object_setattr(m, '__pydantic_fields_set__', fields_set)
|
| 118 |
+
_object_setattr(m, '__pydantic_private__', private)
|
| 119 |
+
|
| 120 |
+
return m
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
@typing.no_type_check
|
| 124 |
+
def _get_value(
|
| 125 |
+
cls: type[BaseModel],
|
| 126 |
+
v: Any,
|
| 127 |
+
to_dict: bool,
|
| 128 |
+
by_alias: bool,
|
| 129 |
+
include: AbstractSetIntStr | MappingIntStrAny | None,
|
| 130 |
+
exclude: AbstractSetIntStr | MappingIntStrAny | None,
|
| 131 |
+
exclude_unset: bool,
|
| 132 |
+
exclude_defaults: bool,
|
| 133 |
+
exclude_none: bool,
|
| 134 |
+
) -> Any:
|
| 135 |
+
from .. import BaseModel
|
| 136 |
+
|
| 137 |
+
if isinstance(v, BaseModel):
|
| 138 |
+
if to_dict:
|
| 139 |
+
return v.model_dump(
|
| 140 |
+
by_alias=by_alias,
|
| 141 |
+
exclude_unset=exclude_unset,
|
| 142 |
+
exclude_defaults=exclude_defaults,
|
| 143 |
+
include=include, # type: ignore
|
| 144 |
+
exclude=exclude, # type: ignore
|
| 145 |
+
exclude_none=exclude_none,
|
| 146 |
+
)
|
| 147 |
+
else:
|
| 148 |
+
return v.copy(include=include, exclude=exclude)
|
| 149 |
+
|
| 150 |
+
value_exclude = _utils.ValueItems(v, exclude) if exclude else None
|
| 151 |
+
value_include = _utils.ValueItems(v, include) if include else None
|
| 152 |
+
|
| 153 |
+
if isinstance(v, dict):
|
| 154 |
+
return {
|
| 155 |
+
k_: _get_value(
|
| 156 |
+
cls,
|
| 157 |
+
v_,
|
| 158 |
+
to_dict=to_dict,
|
| 159 |
+
by_alias=by_alias,
|
| 160 |
+
exclude_unset=exclude_unset,
|
| 161 |
+
exclude_defaults=exclude_defaults,
|
| 162 |
+
include=value_include and value_include.for_element(k_),
|
| 163 |
+
exclude=value_exclude and value_exclude.for_element(k_),
|
| 164 |
+
exclude_none=exclude_none,
|
| 165 |
+
)
|
| 166 |
+
for k_, v_ in v.items()
|
| 167 |
+
if (not value_exclude or not value_exclude.is_excluded(k_))
|
| 168 |
+
and (not value_include or value_include.is_included(k_))
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
elif _utils.sequence_like(v):
|
| 172 |
+
seq_args = (
|
| 173 |
+
_get_value(
|
| 174 |
+
cls,
|
| 175 |
+
v_,
|
| 176 |
+
to_dict=to_dict,
|
| 177 |
+
by_alias=by_alias,
|
| 178 |
+
exclude_unset=exclude_unset,
|
| 179 |
+
exclude_defaults=exclude_defaults,
|
| 180 |
+
include=value_include and value_include.for_element(i),
|
| 181 |
+
exclude=value_exclude and value_exclude.for_element(i),
|
| 182 |
+
exclude_none=exclude_none,
|
| 183 |
+
)
|
| 184 |
+
for i, v_ in enumerate(v)
|
| 185 |
+
if (not value_exclude or not value_exclude.is_excluded(i))
|
| 186 |
+
and (not value_include or value_include.is_included(i))
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
return v.__class__(*seq_args) if _typing_extra.is_namedtuple(v.__class__) else v.__class__(seq_args)
|
| 190 |
+
|
| 191 |
+
elif isinstance(v, Enum) and getattr(cls.model_config, 'use_enum_values', False):
|
| 192 |
+
return v.value
|
| 193 |
+
|
| 194 |
+
else:
|
| 195 |
+
return v
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def _calculate_keys(
|
| 199 |
+
self: BaseModel,
|
| 200 |
+
include: MappingIntStrAny | None,
|
| 201 |
+
exclude: MappingIntStrAny | None,
|
| 202 |
+
exclude_unset: bool,
|
| 203 |
+
update: dict[str, Any] | None = None, # noqa UP006
|
| 204 |
+
) -> typing.AbstractSet[str] | None:
|
| 205 |
+
if include is None and exclude is None and exclude_unset is False:
|
| 206 |
+
return None
|
| 207 |
+
|
| 208 |
+
keys: typing.AbstractSet[str]
|
| 209 |
+
if exclude_unset:
|
| 210 |
+
keys = self.__pydantic_fields_set__.copy()
|
| 211 |
+
else:
|
| 212 |
+
keys = set(self.__dict__.keys())
|
| 213 |
+
keys = keys | (self.__pydantic_extra__ or {}).keys()
|
| 214 |
+
|
| 215 |
+
if include is not None:
|
| 216 |
+
keys &= include.keys()
|
| 217 |
+
|
| 218 |
+
if update:
|
| 219 |
+
keys -= update.keys()
|
| 220 |
+
|
| 221 |
+
if exclude:
|
| 222 |
+
keys -= {k for k, v in exclude.items() if _utils.ValueItems.is_true(v)}
|
| 223 |
+
|
| 224 |
+
return keys
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
from collections.abc import Mapping
|
| 3 |
+
from functools import wraps
|
| 4 |
+
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, overload
|
| 5 |
+
|
| 6 |
+
from typing_extensions import deprecated
|
| 7 |
+
|
| 8 |
+
from .._internal import _config, _typing_extra
|
| 9 |
+
from ..alias_generators import to_pascal
|
| 10 |
+
from ..errors import PydanticUserError
|
| 11 |
+
from ..functional_validators import field_validator
|
| 12 |
+
from ..main import BaseModel, create_model
|
| 13 |
+
from ..warnings import PydanticDeprecatedSince20
|
| 14 |
+
|
| 15 |
+
if not TYPE_CHECKING:
|
| 16 |
+
# See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
|
| 17 |
+
# and https://youtrack.jetbrains.com/issue/PY-51428
|
| 18 |
+
DeprecationWarning = PydanticDeprecatedSince20
|
| 19 |
+
|
| 20 |
+
__all__ = ('validate_arguments',)
|
| 21 |
+
|
| 22 |
+
if TYPE_CHECKING:
|
| 23 |
+
AnyCallable = Callable[..., Any]
|
| 24 |
+
|
| 25 |
+
AnyCallableT = TypeVar('AnyCallableT', bound=AnyCallable)
|
| 26 |
+
ConfigType = Union[None, type[Any], dict[str, Any]]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@overload
|
| 30 |
+
def validate_arguments(
|
| 31 |
+
func: None = None, *, config: 'ConfigType' = None
|
| 32 |
+
) -> Callable[['AnyCallableT'], 'AnyCallableT']: ...
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@overload
|
| 36 |
+
def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT': ...
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@deprecated(
|
| 40 |
+
'The `validate_arguments` method is deprecated; use `validate_call` instead.',
|
| 41 |
+
category=None,
|
| 42 |
+
)
|
| 43 |
+
def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any:
|
| 44 |
+
"""Decorator to validate the arguments passed to a function."""
|
| 45 |
+
warnings.warn(
|
| 46 |
+
'The `validate_arguments` method is deprecated; use `validate_call` instead.',
|
| 47 |
+
PydanticDeprecatedSince20,
|
| 48 |
+
stacklevel=2,
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
def validate(_func: 'AnyCallable') -> 'AnyCallable':
|
| 52 |
+
vd = ValidatedFunction(_func, config)
|
| 53 |
+
|
| 54 |
+
@wraps(_func)
|
| 55 |
+
def wrapper_function(*args: Any, **kwargs: Any) -> Any:
|
| 56 |
+
return vd.call(*args, **kwargs)
|
| 57 |
+
|
| 58 |
+
wrapper_function.vd = vd # type: ignore
|
| 59 |
+
wrapper_function.validate = vd.init_model_instance # type: ignore
|
| 60 |
+
wrapper_function.raw_function = vd.raw_function # type: ignore
|
| 61 |
+
wrapper_function.model = vd.model # type: ignore
|
| 62 |
+
return wrapper_function
|
| 63 |
+
|
| 64 |
+
if func:
|
| 65 |
+
return validate(func)
|
| 66 |
+
else:
|
| 67 |
+
return validate
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
ALT_V_ARGS = 'v__args'
|
| 71 |
+
ALT_V_KWARGS = 'v__kwargs'
|
| 72 |
+
V_POSITIONAL_ONLY_NAME = 'v__positional_only'
|
| 73 |
+
V_DUPLICATE_KWARGS = 'v__duplicate_kwargs'
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class ValidatedFunction:
|
| 77 |
+
def __init__(self, function: 'AnyCallable', config: 'ConfigType'):
|
| 78 |
+
from inspect import Parameter, signature
|
| 79 |
+
|
| 80 |
+
parameters: Mapping[str, Parameter] = signature(function).parameters
|
| 81 |
+
|
| 82 |
+
if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}:
|
| 83 |
+
raise PydanticUserError(
|
| 84 |
+
f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" '
|
| 85 |
+
f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator',
|
| 86 |
+
code=None,
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
self.raw_function = function
|
| 90 |
+
self.arg_mapping: dict[int, str] = {}
|
| 91 |
+
self.positional_only_args: set[str] = set()
|
| 92 |
+
self.v_args_name = 'args'
|
| 93 |
+
self.v_kwargs_name = 'kwargs'
|
| 94 |
+
|
| 95 |
+
type_hints = _typing_extra.get_type_hints(function, include_extras=True)
|
| 96 |
+
takes_args = False
|
| 97 |
+
takes_kwargs = False
|
| 98 |
+
fields: dict[str, tuple[Any, Any]] = {}
|
| 99 |
+
for i, (name, p) in enumerate(parameters.items()):
|
| 100 |
+
if p.annotation is p.empty:
|
| 101 |
+
annotation = Any
|
| 102 |
+
else:
|
| 103 |
+
annotation = type_hints[name]
|
| 104 |
+
|
| 105 |
+
default = ... if p.default is p.empty else p.default
|
| 106 |
+
if p.kind == Parameter.POSITIONAL_ONLY:
|
| 107 |
+
self.arg_mapping[i] = name
|
| 108 |
+
fields[name] = annotation, default
|
| 109 |
+
fields[V_POSITIONAL_ONLY_NAME] = list[str], None
|
| 110 |
+
self.positional_only_args.add(name)
|
| 111 |
+
elif p.kind == Parameter.POSITIONAL_OR_KEYWORD:
|
| 112 |
+
self.arg_mapping[i] = name
|
| 113 |
+
fields[name] = annotation, default
|
| 114 |
+
fields[V_DUPLICATE_KWARGS] = list[str], None
|
| 115 |
+
elif p.kind == Parameter.KEYWORD_ONLY:
|
| 116 |
+
fields[name] = annotation, default
|
| 117 |
+
elif p.kind == Parameter.VAR_POSITIONAL:
|
| 118 |
+
self.v_args_name = name
|
| 119 |
+
fields[name] = tuple[annotation, ...], None
|
| 120 |
+
takes_args = True
|
| 121 |
+
else:
|
| 122 |
+
assert p.kind == Parameter.VAR_KEYWORD, p.kind
|
| 123 |
+
self.v_kwargs_name = name
|
| 124 |
+
fields[name] = dict[str, annotation], None
|
| 125 |
+
takes_kwargs = True
|
| 126 |
+
|
| 127 |
+
# these checks avoid a clash between "args" and a field with that name
|
| 128 |
+
if not takes_args and self.v_args_name in fields:
|
| 129 |
+
self.v_args_name = ALT_V_ARGS
|
| 130 |
+
|
| 131 |
+
# same with "kwargs"
|
| 132 |
+
if not takes_kwargs and self.v_kwargs_name in fields:
|
| 133 |
+
self.v_kwargs_name = ALT_V_KWARGS
|
| 134 |
+
|
| 135 |
+
if not takes_args:
|
| 136 |
+
# we add the field so validation below can raise the correct exception
|
| 137 |
+
fields[self.v_args_name] = list[Any], None
|
| 138 |
+
|
| 139 |
+
if not takes_kwargs:
|
| 140 |
+
# same with kwargs
|
| 141 |
+
fields[self.v_kwargs_name] = dict[Any, Any], None
|
| 142 |
+
|
| 143 |
+
self.create_model(fields, takes_args, takes_kwargs, config)
|
| 144 |
+
|
| 145 |
+
def init_model_instance(self, *args: Any, **kwargs: Any) -> BaseModel:
|
| 146 |
+
values = self.build_values(args, kwargs)
|
| 147 |
+
return self.model(**values)
|
| 148 |
+
|
| 149 |
+
def call(self, *args: Any, **kwargs: Any) -> Any:
|
| 150 |
+
m = self.init_model_instance(*args, **kwargs)
|
| 151 |
+
return self.execute(m)
|
| 152 |
+
|
| 153 |
+
def build_values(self, args: tuple[Any, ...], kwargs: dict[str, Any]) -> dict[str, Any]:
|
| 154 |
+
values: dict[str, Any] = {}
|
| 155 |
+
if args:
|
| 156 |
+
arg_iter = enumerate(args)
|
| 157 |
+
while True:
|
| 158 |
+
try:
|
| 159 |
+
i, a = next(arg_iter)
|
| 160 |
+
except StopIteration:
|
| 161 |
+
break
|
| 162 |
+
arg_name = self.arg_mapping.get(i)
|
| 163 |
+
if arg_name is not None:
|
| 164 |
+
values[arg_name] = a
|
| 165 |
+
else:
|
| 166 |
+
values[self.v_args_name] = [a] + [a for _, a in arg_iter]
|
| 167 |
+
break
|
| 168 |
+
|
| 169 |
+
var_kwargs: dict[str, Any] = {}
|
| 170 |
+
wrong_positional_args = []
|
| 171 |
+
duplicate_kwargs = []
|
| 172 |
+
fields_alias = [
|
| 173 |
+
field.alias
|
| 174 |
+
for name, field in self.model.__pydantic_fields__.items()
|
| 175 |
+
if name not in (self.v_args_name, self.v_kwargs_name)
|
| 176 |
+
]
|
| 177 |
+
non_var_fields = set(self.model.__pydantic_fields__) - {self.v_args_name, self.v_kwargs_name}
|
| 178 |
+
for k, v in kwargs.items():
|
| 179 |
+
if k in non_var_fields or k in fields_alias:
|
| 180 |
+
if k in self.positional_only_args:
|
| 181 |
+
wrong_positional_args.append(k)
|
| 182 |
+
if k in values:
|
| 183 |
+
duplicate_kwargs.append(k)
|
| 184 |
+
values[k] = v
|
| 185 |
+
else:
|
| 186 |
+
var_kwargs[k] = v
|
| 187 |
+
|
| 188 |
+
if var_kwargs:
|
| 189 |
+
values[self.v_kwargs_name] = var_kwargs
|
| 190 |
+
if wrong_positional_args:
|
| 191 |
+
values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args
|
| 192 |
+
if duplicate_kwargs:
|
| 193 |
+
values[V_DUPLICATE_KWARGS] = duplicate_kwargs
|
| 194 |
+
return values
|
| 195 |
+
|
| 196 |
+
def execute(self, m: BaseModel) -> Any:
|
| 197 |
+
d = {
|
| 198 |
+
k: v
|
| 199 |
+
for k, v in m.__dict__.items()
|
| 200 |
+
if k in m.__pydantic_fields_set__ or m.__pydantic_fields__[k].default_factory
|
| 201 |
+
}
|
| 202 |
+
var_kwargs = d.pop(self.v_kwargs_name, {})
|
| 203 |
+
|
| 204 |
+
if self.v_args_name in d:
|
| 205 |
+
args_: list[Any] = []
|
| 206 |
+
in_kwargs = False
|
| 207 |
+
kwargs = {}
|
| 208 |
+
for name, value in d.items():
|
| 209 |
+
if in_kwargs:
|
| 210 |
+
kwargs[name] = value
|
| 211 |
+
elif name == self.v_args_name:
|
| 212 |
+
args_ += value
|
| 213 |
+
in_kwargs = True
|
| 214 |
+
else:
|
| 215 |
+
args_.append(value)
|
| 216 |
+
return self.raw_function(*args_, **kwargs, **var_kwargs)
|
| 217 |
+
elif self.positional_only_args:
|
| 218 |
+
args_ = []
|
| 219 |
+
kwargs = {}
|
| 220 |
+
for name, value in d.items():
|
| 221 |
+
if name in self.positional_only_args:
|
| 222 |
+
args_.append(value)
|
| 223 |
+
else:
|
| 224 |
+
kwargs[name] = value
|
| 225 |
+
return self.raw_function(*args_, **kwargs, **var_kwargs)
|
| 226 |
+
else:
|
| 227 |
+
return self.raw_function(**d, **var_kwargs)
|
| 228 |
+
|
| 229 |
+
def create_model(self, fields: dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None:
|
| 230 |
+
pos_args = len(self.arg_mapping)
|
| 231 |
+
|
| 232 |
+
config_wrapper = _config.ConfigWrapper(config)
|
| 233 |
+
|
| 234 |
+
if config_wrapper.alias_generator:
|
| 235 |
+
raise PydanticUserError(
|
| 236 |
+
'Setting the "alias_generator" property on custom Config for '
|
| 237 |
+
'@validate_arguments is not yet supported, please remove.',
|
| 238 |
+
code=None,
|
| 239 |
+
)
|
| 240 |
+
if config_wrapper.extra is None:
|
| 241 |
+
config_wrapper.config_dict['extra'] = 'forbid'
|
| 242 |
+
|
| 243 |
+
class DecoratorBaseModel(BaseModel):
|
| 244 |
+
@field_validator(self.v_args_name, check_fields=False)
|
| 245 |
+
@classmethod
|
| 246 |
+
def check_args(cls, v: Optional[list[Any]]) -> Optional[list[Any]]:
|
| 247 |
+
if takes_args or v is None:
|
| 248 |
+
return v
|
| 249 |
+
|
| 250 |
+
raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given')
|
| 251 |
+
|
| 252 |
+
@field_validator(self.v_kwargs_name, check_fields=False)
|
| 253 |
+
@classmethod
|
| 254 |
+
def check_kwargs(cls, v: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
|
| 255 |
+
if takes_kwargs or v is None:
|
| 256 |
+
return v
|
| 257 |
+
|
| 258 |
+
plural = '' if len(v) == 1 else 's'
|
| 259 |
+
keys = ', '.join(map(repr, v.keys()))
|
| 260 |
+
raise TypeError(f'unexpected keyword argument{plural}: {keys}')
|
| 261 |
+
|
| 262 |
+
@field_validator(V_POSITIONAL_ONLY_NAME, check_fields=False)
|
| 263 |
+
@classmethod
|
| 264 |
+
def check_positional_only(cls, v: Optional[list[str]]) -> None:
|
| 265 |
+
if v is None:
|
| 266 |
+
return
|
| 267 |
+
|
| 268 |
+
plural = '' if len(v) == 1 else 's'
|
| 269 |
+
keys = ', '.join(map(repr, v))
|
| 270 |
+
raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}')
|
| 271 |
+
|
| 272 |
+
@field_validator(V_DUPLICATE_KWARGS, check_fields=False)
|
| 273 |
+
@classmethod
|
| 274 |
+
def check_duplicate_kwargs(cls, v: Optional[list[str]]) -> None:
|
| 275 |
+
if v is None:
|
| 276 |
+
return
|
| 277 |
+
|
| 278 |
+
plural = '' if len(v) == 1 else 's'
|
| 279 |
+
keys = ', '.join(map(repr, v))
|
| 280 |
+
raise TypeError(f'multiple values for argument{plural}: {keys}')
|
| 281 |
+
|
| 282 |
+
model_config = config_wrapper.config_dict
|
| 283 |
+
|
| 284 |
+
self.model = create_model(to_pascal(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/json.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
import warnings
|
| 3 |
+
from collections import deque
|
| 4 |
+
from decimal import Decimal
|
| 5 |
+
from enum import Enum
|
| 6 |
+
from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from re import Pattern
|
| 9 |
+
from types import GeneratorType
|
| 10 |
+
from typing import TYPE_CHECKING, Any, Callable, Union
|
| 11 |
+
from uuid import UUID
|
| 12 |
+
|
| 13 |
+
from typing_extensions import deprecated
|
| 14 |
+
|
| 15 |
+
from .._internal._import_utils import import_cached_base_model
|
| 16 |
+
from ..color import Color
|
| 17 |
+
from ..networks import NameEmail
|
| 18 |
+
from ..types import SecretBytes, SecretStr
|
| 19 |
+
from ..warnings import PydanticDeprecatedSince20
|
| 20 |
+
|
| 21 |
+
if not TYPE_CHECKING:
|
| 22 |
+
# See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
|
| 23 |
+
# and https://youtrack.jetbrains.com/issue/PY-51428
|
| 24 |
+
DeprecationWarning = PydanticDeprecatedSince20
|
| 25 |
+
|
| 26 |
+
__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat'
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def isoformat(o: Union[datetime.date, datetime.time]) -> str:
|
| 30 |
+
return o.isoformat()
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
| 34 |
+
"""Encodes a Decimal as int of there's no exponent, otherwise float.
|
| 35 |
+
|
| 36 |
+
This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
|
| 37 |
+
where a integer (but not int typed) is used. Encoding this as a float
|
| 38 |
+
results in failed round-tripping between encode and parse.
|
| 39 |
+
Our Id type is a prime example of this.
|
| 40 |
+
|
| 41 |
+
>>> decimal_encoder(Decimal("1.0"))
|
| 42 |
+
1.0
|
| 43 |
+
|
| 44 |
+
>>> decimal_encoder(Decimal("1"))
|
| 45 |
+
1
|
| 46 |
+
"""
|
| 47 |
+
exponent = dec_value.as_tuple().exponent
|
| 48 |
+
if isinstance(exponent, int) and exponent >= 0:
|
| 49 |
+
return int(dec_value)
|
| 50 |
+
else:
|
| 51 |
+
return float(dec_value)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
|
| 55 |
+
bytes: lambda o: o.decode(),
|
| 56 |
+
Color: str,
|
| 57 |
+
datetime.date: isoformat,
|
| 58 |
+
datetime.datetime: isoformat,
|
| 59 |
+
datetime.time: isoformat,
|
| 60 |
+
datetime.timedelta: lambda td: td.total_seconds(),
|
| 61 |
+
Decimal: decimal_encoder,
|
| 62 |
+
Enum: lambda o: o.value,
|
| 63 |
+
frozenset: list,
|
| 64 |
+
deque: list,
|
| 65 |
+
GeneratorType: list,
|
| 66 |
+
IPv4Address: str,
|
| 67 |
+
IPv4Interface: str,
|
| 68 |
+
IPv4Network: str,
|
| 69 |
+
IPv6Address: str,
|
| 70 |
+
IPv6Interface: str,
|
| 71 |
+
IPv6Network: str,
|
| 72 |
+
NameEmail: str,
|
| 73 |
+
Path: str,
|
| 74 |
+
Pattern: lambda o: o.pattern,
|
| 75 |
+
SecretBytes: str,
|
| 76 |
+
SecretStr: str,
|
| 77 |
+
set: list,
|
| 78 |
+
UUID: str,
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@deprecated(
|
| 83 |
+
'`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.',
|
| 84 |
+
category=None,
|
| 85 |
+
)
|
| 86 |
+
def pydantic_encoder(obj: Any) -> Any:
|
| 87 |
+
warnings.warn(
|
| 88 |
+
'`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.',
|
| 89 |
+
category=PydanticDeprecatedSince20,
|
| 90 |
+
stacklevel=2,
|
| 91 |
+
)
|
| 92 |
+
from dataclasses import asdict, is_dataclass
|
| 93 |
+
|
| 94 |
+
BaseModel = import_cached_base_model()
|
| 95 |
+
|
| 96 |
+
if isinstance(obj, BaseModel):
|
| 97 |
+
return obj.model_dump()
|
| 98 |
+
elif is_dataclass(obj):
|
| 99 |
+
return asdict(obj) # type: ignore
|
| 100 |
+
|
| 101 |
+
# Check the class type and its superclasses for a matching encoder
|
| 102 |
+
for base in obj.__class__.__mro__[:-1]:
|
| 103 |
+
try:
|
| 104 |
+
encoder = ENCODERS_BY_TYPE[base]
|
| 105 |
+
except KeyError:
|
| 106 |
+
continue
|
| 107 |
+
return encoder(obj)
|
| 108 |
+
else: # We have exited the for loop without finding a suitable encoder
|
| 109 |
+
raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable")
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
# TODO: Add a suggested migration path once there is a way to use custom encoders
|
| 113 |
+
@deprecated(
|
| 114 |
+
'`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.',
|
| 115 |
+
category=None,
|
| 116 |
+
)
|
| 117 |
+
def custom_pydantic_encoder(type_encoders: dict[Any, Callable[[type[Any]], Any]], obj: Any) -> Any:
|
| 118 |
+
warnings.warn(
|
| 119 |
+
'`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.',
|
| 120 |
+
category=PydanticDeprecatedSince20,
|
| 121 |
+
stacklevel=2,
|
| 122 |
+
)
|
| 123 |
+
# Check the class type and its superclasses for a matching encoder
|
| 124 |
+
for base in obj.__class__.__mro__[:-1]:
|
| 125 |
+
try:
|
| 126 |
+
encoder = type_encoders[base]
|
| 127 |
+
except KeyError:
|
| 128 |
+
continue
|
| 129 |
+
|
| 130 |
+
return encoder(obj)
|
| 131 |
+
else: # We have exited the for loop without finding a suitable encoder
|
| 132 |
+
return pydantic_encoder(obj)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
@deprecated('`timedelta_isoformat` is deprecated.', category=None)
|
| 136 |
+
def timedelta_isoformat(td: datetime.timedelta) -> str:
|
| 137 |
+
"""ISO 8601 encoding for Python timedelta object."""
|
| 138 |
+
warnings.warn('`timedelta_isoformat` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
|
| 139 |
+
minutes, seconds = divmod(td.seconds, 60)
|
| 140 |
+
hours, minutes = divmod(minutes, 60)
|
| 141 |
+
return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import pickle
|
| 5 |
+
import warnings
|
| 6 |
+
from enum import Enum
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import TYPE_CHECKING, Any, Callable
|
| 9 |
+
|
| 10 |
+
from typing_extensions import deprecated
|
| 11 |
+
|
| 12 |
+
from ..warnings import PydanticDeprecatedSince20
|
| 13 |
+
|
| 14 |
+
if not TYPE_CHECKING:
|
| 15 |
+
# See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
|
| 16 |
+
# and https://youtrack.jetbrains.com/issue/PY-51428
|
| 17 |
+
DeprecationWarning = PydanticDeprecatedSince20
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class Protocol(str, Enum):
|
| 21 |
+
json = 'json'
|
| 22 |
+
pickle = 'pickle'
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@deprecated('`load_str_bytes` is deprecated.', category=None)
|
| 26 |
+
def load_str_bytes(
|
| 27 |
+
b: str | bytes,
|
| 28 |
+
*,
|
| 29 |
+
content_type: str | None = None,
|
| 30 |
+
encoding: str = 'utf8',
|
| 31 |
+
proto: Protocol | None = None,
|
| 32 |
+
allow_pickle: bool = False,
|
| 33 |
+
json_loads: Callable[[str], Any] = json.loads,
|
| 34 |
+
) -> Any:
|
| 35 |
+
warnings.warn('`load_str_bytes` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
|
| 36 |
+
if proto is None and content_type:
|
| 37 |
+
if content_type.endswith(('json', 'javascript')):
|
| 38 |
+
pass
|
| 39 |
+
elif allow_pickle and content_type.endswith('pickle'):
|
| 40 |
+
proto = Protocol.pickle
|
| 41 |
+
else:
|
| 42 |
+
raise TypeError(f'Unknown content-type: {content_type}')
|
| 43 |
+
|
| 44 |
+
proto = proto or Protocol.json
|
| 45 |
+
|
| 46 |
+
if proto == Protocol.json:
|
| 47 |
+
if isinstance(b, bytes):
|
| 48 |
+
b = b.decode(encoding)
|
| 49 |
+
return json_loads(b) # type: ignore
|
| 50 |
+
elif proto == Protocol.pickle:
|
| 51 |
+
if not allow_pickle:
|
| 52 |
+
raise RuntimeError('Trying to decode with pickle with allow_pickle=False')
|
| 53 |
+
bb = b if isinstance(b, bytes) else b.encode() # type: ignore
|
| 54 |
+
return pickle.loads(bb)
|
| 55 |
+
else:
|
| 56 |
+
raise TypeError(f'Unknown protocol: {proto}')
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
@deprecated('`load_file` is deprecated.', category=None)
|
| 60 |
+
def load_file(
|
| 61 |
+
path: str | Path,
|
| 62 |
+
*,
|
| 63 |
+
content_type: str | None = None,
|
| 64 |
+
encoding: str = 'utf8',
|
| 65 |
+
proto: Protocol | None = None,
|
| 66 |
+
allow_pickle: bool = False,
|
| 67 |
+
json_loads: Callable[[str], Any] = json.loads,
|
| 68 |
+
) -> Any:
|
| 69 |
+
warnings.warn('`load_file` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
|
| 70 |
+
path = Path(path)
|
| 71 |
+
b = path.read_bytes()
|
| 72 |
+
if content_type is None:
|
| 73 |
+
if path.suffix in ('.js', '.json'):
|
| 74 |
+
proto = Protocol.json
|
| 75 |
+
elif path.suffix == '.pkl':
|
| 76 |
+
proto = Protocol.pickle
|
| 77 |
+
|
| 78 |
+
return load_str_bytes(
|
| 79 |
+
b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads
|
| 80 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import warnings
|
| 5 |
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
|
| 6 |
+
|
| 7 |
+
from typing_extensions import deprecated
|
| 8 |
+
|
| 9 |
+
from ..json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema
|
| 10 |
+
from ..type_adapter import TypeAdapter
|
| 11 |
+
from ..warnings import PydanticDeprecatedSince20
|
| 12 |
+
|
| 13 |
+
if not TYPE_CHECKING:
|
| 14 |
+
# See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
|
| 15 |
+
# and https://youtrack.jetbrains.com/issue/PY-51428
|
| 16 |
+
DeprecationWarning = PydanticDeprecatedSince20
|
| 17 |
+
|
| 18 |
+
__all__ = 'parse_obj_as', 'schema_of', 'schema_json_of'
|
| 19 |
+
|
| 20 |
+
NameFactory = Union[str, Callable[[type[Any]], str]]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
T = TypeVar('T')
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@deprecated(
|
| 27 |
+
'`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.',
|
| 28 |
+
category=None,
|
| 29 |
+
)
|
| 30 |
+
def parse_obj_as(type_: type[T], obj: Any, type_name: NameFactory | None = None) -> T:
|
| 31 |
+
warnings.warn(
|
| 32 |
+
'`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.',
|
| 33 |
+
category=PydanticDeprecatedSince20,
|
| 34 |
+
stacklevel=2,
|
| 35 |
+
)
|
| 36 |
+
if type_name is not None: # pragma: no cover
|
| 37 |
+
warnings.warn(
|
| 38 |
+
'The type_name parameter is deprecated. parse_obj_as no longer creates temporary models',
|
| 39 |
+
DeprecationWarning,
|
| 40 |
+
stacklevel=2,
|
| 41 |
+
)
|
| 42 |
+
return TypeAdapter(type_).validate_python(obj)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@deprecated(
|
| 46 |
+
'`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
|
| 47 |
+
category=None,
|
| 48 |
+
)
|
| 49 |
+
def schema_of(
|
| 50 |
+
type_: Any,
|
| 51 |
+
*,
|
| 52 |
+
title: NameFactory | None = None,
|
| 53 |
+
by_alias: bool = True,
|
| 54 |
+
ref_template: str = DEFAULT_REF_TEMPLATE,
|
| 55 |
+
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
|
| 56 |
+
) -> dict[str, Any]:
|
| 57 |
+
"""Generate a JSON schema (as dict) for the passed model or dynamically generated one."""
|
| 58 |
+
warnings.warn(
|
| 59 |
+
'`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
|
| 60 |
+
category=PydanticDeprecatedSince20,
|
| 61 |
+
stacklevel=2,
|
| 62 |
+
)
|
| 63 |
+
res = TypeAdapter(type_).json_schema(
|
| 64 |
+
by_alias=by_alias,
|
| 65 |
+
schema_generator=schema_generator,
|
| 66 |
+
ref_template=ref_template,
|
| 67 |
+
)
|
| 68 |
+
if title is not None:
|
| 69 |
+
if isinstance(title, str):
|
| 70 |
+
res['title'] = title
|
| 71 |
+
else:
|
| 72 |
+
warnings.warn(
|
| 73 |
+
'Passing a callable for the `title` parameter is deprecated and no longer supported',
|
| 74 |
+
DeprecationWarning,
|
| 75 |
+
stacklevel=2,
|
| 76 |
+
)
|
| 77 |
+
res['title'] = title(type_)
|
| 78 |
+
return res
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
@deprecated(
|
| 82 |
+
'`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
|
| 83 |
+
category=None,
|
| 84 |
+
)
|
| 85 |
+
def schema_json_of(
|
| 86 |
+
type_: Any,
|
| 87 |
+
*,
|
| 88 |
+
title: NameFactory | None = None,
|
| 89 |
+
by_alias: bool = True,
|
| 90 |
+
ref_template: str = DEFAULT_REF_TEMPLATE,
|
| 91 |
+
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
|
| 92 |
+
**dumps_kwargs: Any,
|
| 93 |
+
) -> str:
|
| 94 |
+
"""Generate a JSON schema (as JSON) for the passed model or dynamically generated one."""
|
| 95 |
+
warnings.warn(
|
| 96 |
+
'`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
|
| 97 |
+
category=PydanticDeprecatedSince20,
|
| 98 |
+
stacklevel=2,
|
| 99 |
+
)
|
| 100 |
+
return json.dumps(
|
| 101 |
+
schema_of(type_, title=title, by_alias=by_alias, ref_template=ref_template, schema_generator=schema_generator),
|
| 102 |
+
**dumps_kwargs,
|
| 103 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/env_settings.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The `env_settings` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/error_wrappers.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The `error_wrappers` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/errors.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Pydantic-specific errors."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import re
|
| 6 |
+
from typing import Any, ClassVar, Literal
|
| 7 |
+
|
| 8 |
+
from typing_extensions import Self
|
| 9 |
+
from typing_inspection.introspection import Qualifier
|
| 10 |
+
|
| 11 |
+
from pydantic._internal import _repr
|
| 12 |
+
|
| 13 |
+
from ._migration import getattr_migration
|
| 14 |
+
from .version import version_short
|
| 15 |
+
|
| 16 |
+
__all__ = (
|
| 17 |
+
'PydanticUserError',
|
| 18 |
+
'PydanticUndefinedAnnotation',
|
| 19 |
+
'PydanticImportError',
|
| 20 |
+
'PydanticSchemaGenerationError',
|
| 21 |
+
'PydanticInvalidForJsonSchema',
|
| 22 |
+
'PydanticForbiddenQualifier',
|
| 23 |
+
'PydanticErrorCodes',
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
# We use this URL to allow for future flexibility about how we host the docs, while allowing for Pydantic
|
| 27 |
+
# code in the while with "old" URLs to still work.
|
| 28 |
+
# 'u' refers to "user errors" - e.g. errors caused by developers using pydantic, as opposed to validation errors.
|
| 29 |
+
DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/'
|
| 30 |
+
PydanticErrorCodes = Literal[
|
| 31 |
+
'class-not-fully-defined',
|
| 32 |
+
'custom-json-schema',
|
| 33 |
+
'decorator-invalid-fields',
|
| 34 |
+
'decorator-missing-arguments',
|
| 35 |
+
'decorator-missing-field',
|
| 36 |
+
'discriminator-no-field',
|
| 37 |
+
'discriminator-alias-type',
|
| 38 |
+
'discriminator-needs-literal',
|
| 39 |
+
'discriminator-alias',
|
| 40 |
+
'discriminator-validator',
|
| 41 |
+
'callable-discriminator-no-tag',
|
| 42 |
+
'typed-dict-version',
|
| 43 |
+
'model-field-overridden',
|
| 44 |
+
'model-field-missing-annotation',
|
| 45 |
+
'config-both',
|
| 46 |
+
'removed-kwargs',
|
| 47 |
+
'circular-reference-schema',
|
| 48 |
+
'invalid-for-json-schema',
|
| 49 |
+
'json-schema-already-used',
|
| 50 |
+
'base-model-instantiated',
|
| 51 |
+
'undefined-annotation',
|
| 52 |
+
'schema-for-unknown-type',
|
| 53 |
+
'import-error',
|
| 54 |
+
'create-model-field-definitions',
|
| 55 |
+
'validator-instance-method',
|
| 56 |
+
'validator-input-type',
|
| 57 |
+
'root-validator-pre-skip',
|
| 58 |
+
'model-serializer-instance-method',
|
| 59 |
+
'validator-field-config-info',
|
| 60 |
+
'validator-v1-signature',
|
| 61 |
+
'validator-signature',
|
| 62 |
+
'field-serializer-signature',
|
| 63 |
+
'model-serializer-signature',
|
| 64 |
+
'multiple-field-serializers',
|
| 65 |
+
'invalid-annotated-type',
|
| 66 |
+
'type-adapter-config-unused',
|
| 67 |
+
'root-model-extra',
|
| 68 |
+
'unevaluable-type-annotation',
|
| 69 |
+
'dataclass-init-false-extra-allow',
|
| 70 |
+
'clashing-init-and-init-var',
|
| 71 |
+
'model-config-invalid-field-name',
|
| 72 |
+
'with-config-on-model',
|
| 73 |
+
'dataclass-on-model',
|
| 74 |
+
'validate-call-type',
|
| 75 |
+
'unpack-typed-dict',
|
| 76 |
+
'overlapping-unpack-typed-dict',
|
| 77 |
+
'invalid-self-type',
|
| 78 |
+
'validate-by-alias-and-name-false',
|
| 79 |
+
]
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class PydanticErrorMixin:
|
| 83 |
+
"""A mixin class for common functionality shared by all Pydantic-specific errors.
|
| 84 |
+
|
| 85 |
+
Attributes:
|
| 86 |
+
message: A message describing the error.
|
| 87 |
+
code: An optional error code from PydanticErrorCodes enum.
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None:
|
| 91 |
+
self.message = message
|
| 92 |
+
self.code = code
|
| 93 |
+
|
| 94 |
+
def __str__(self) -> str:
|
| 95 |
+
if self.code is None:
|
| 96 |
+
return self.message
|
| 97 |
+
else:
|
| 98 |
+
return f'{self.message}\n\nFor further information visit {DEV_ERROR_DOCS_URL}{self.code}'
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class PydanticUserError(PydanticErrorMixin, RuntimeError):
|
| 102 |
+
"""An error raised due to incorrect use of Pydantic."""
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError):
|
| 106 |
+
"""A subclass of `NameError` raised when handling undefined annotations during `CoreSchema` generation.
|
| 107 |
+
|
| 108 |
+
Attributes:
|
| 109 |
+
name: Name of the error.
|
| 110 |
+
message: Description of the error.
|
| 111 |
+
"""
|
| 112 |
+
|
| 113 |
+
def __init__(self, name: str, message: str) -> None:
|
| 114 |
+
self.name = name
|
| 115 |
+
super().__init__(message=message, code='undefined-annotation')
|
| 116 |
+
|
| 117 |
+
@classmethod
|
| 118 |
+
def from_name_error(cls, name_error: NameError) -> Self:
|
| 119 |
+
"""Convert a `NameError` to a `PydanticUndefinedAnnotation` error.
|
| 120 |
+
|
| 121 |
+
Args:
|
| 122 |
+
name_error: `NameError` to be converted.
|
| 123 |
+
|
| 124 |
+
Returns:
|
| 125 |
+
Converted `PydanticUndefinedAnnotation` error.
|
| 126 |
+
"""
|
| 127 |
+
try:
|
| 128 |
+
name = name_error.name # type: ignore # python > 3.10
|
| 129 |
+
except AttributeError:
|
| 130 |
+
name = re.search(r".*'(.+?)'", str(name_error)).group(1) # type: ignore[union-attr]
|
| 131 |
+
return cls(name=name, message=str(name_error))
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
class PydanticImportError(PydanticErrorMixin, ImportError):
|
| 135 |
+
"""An error raised when an import fails due to module changes between V1 and V2.
|
| 136 |
+
|
| 137 |
+
Attributes:
|
| 138 |
+
message: Description of the error.
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
def __init__(self, message: str) -> None:
|
| 142 |
+
super().__init__(message, code='import-error')
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
class PydanticSchemaGenerationError(PydanticUserError):
|
| 146 |
+
"""An error raised during failures to generate a `CoreSchema` for some type.
|
| 147 |
+
|
| 148 |
+
Attributes:
|
| 149 |
+
message: Description of the error.
|
| 150 |
+
"""
|
| 151 |
+
|
| 152 |
+
def __init__(self, message: str) -> None:
|
| 153 |
+
super().__init__(message, code='schema-for-unknown-type')
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class PydanticInvalidForJsonSchema(PydanticUserError):
|
| 157 |
+
"""An error raised during failures to generate a JSON schema for some `CoreSchema`.
|
| 158 |
+
|
| 159 |
+
Attributes:
|
| 160 |
+
message: Description of the error.
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
def __init__(self, message: str) -> None:
|
| 164 |
+
super().__init__(message, code='invalid-for-json-schema')
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class PydanticForbiddenQualifier(PydanticUserError):
|
| 168 |
+
"""An error raised if a forbidden type qualifier is found in a type annotation."""
|
| 169 |
+
|
| 170 |
+
_qualifier_repr_map: ClassVar[dict[Qualifier, str]] = {
|
| 171 |
+
'required': 'typing.Required',
|
| 172 |
+
'not_required': 'typing.NotRequired',
|
| 173 |
+
'read_only': 'typing.ReadOnly',
|
| 174 |
+
'class_var': 'typing.ClassVar',
|
| 175 |
+
'init_var': 'dataclasses.InitVar',
|
| 176 |
+
'final': 'typing.Final',
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
def __init__(self, qualifier: Qualifier, annotation: Any) -> None:
|
| 180 |
+
super().__init__(
|
| 181 |
+
message=(
|
| 182 |
+
f'The annotation {_repr.display_as_type(annotation)!r} contains the {self._qualifier_repr_map[qualifier]!r} '
|
| 183 |
+
f'type qualifier, which is invalid in the context it is defined.'
|
| 184 |
+
),
|
| 185 |
+
code=None,
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""The "experimental" module of pydantic contains potential new features that are subject to change."""
|
.mcp-venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Experimental module exposing a function to generate a core schema that validates callable arguments."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from collections.abc import Callable
|
| 6 |
+
from typing import Any, Literal
|
| 7 |
+
|
| 8 |
+
from pydantic_core import CoreSchema
|
| 9 |
+
|
| 10 |
+
from pydantic import ConfigDict
|
| 11 |
+
from pydantic._internal import _config, _generate_schema, _namespace_utils
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def generate_arguments_schema(
|
| 15 |
+
func: Callable[..., Any],
|
| 16 |
+
schema_type: Literal['arguments', 'arguments-v3'] = 'arguments-v3',
|
| 17 |
+
parameters_callback: Callable[[int, str, Any], Literal['skip'] | None] | None = None,
|
| 18 |
+
config: ConfigDict | None = None,
|
| 19 |
+
) -> CoreSchema:
|
| 20 |
+
"""Generate the schema for the arguments of a function.
|
| 21 |
+
|
| 22 |
+
Args:
|
| 23 |
+
func: The function to generate the schema for.
|
| 24 |
+
schema_type: The type of schema to generate.
|
| 25 |
+
parameters_callback: A callable that will be invoked for each parameter. The callback
|
| 26 |
+
should take three required arguments: the index, the name and the type annotation
|
| 27 |
+
(or [`Parameter.empty`][inspect.Parameter.empty] if not annotated) of the parameter.
|
| 28 |
+
The callback can optionally return `'skip'`, so that the parameter gets excluded
|
| 29 |
+
from the resulting schema.
|
| 30 |
+
config: The configuration to use.
|
| 31 |
+
|
| 32 |
+
Returns:
|
| 33 |
+
The generated schema.
|
| 34 |
+
"""
|
| 35 |
+
generate_schema = _generate_schema.GenerateSchema(
|
| 36 |
+
_config.ConfigWrapper(config),
|
| 37 |
+
ns_resolver=_namespace_utils.NsResolver(namespaces_tuple=_namespace_utils.ns_for_function(func)),
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
if schema_type == 'arguments':
|
| 41 |
+
schema = generate_schema._arguments_schema(func, parameters_callback) # pyright: ignore[reportArgumentType]
|
| 42 |
+
else:
|
| 43 |
+
schema = generate_schema._arguments_v3_schema(func, parameters_callback) # pyright: ignore[reportArgumentType]
|
| 44 |
+
return generate_schema.clean_schema(schema)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Experimental module exposing a function a `MISSING` sentinel."""
|
| 2 |
+
|
| 3 |
+
from pydantic_core import MISSING
|
| 4 |
+
|
| 5 |
+
__all__ = ('MISSING',)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py
ADDED
|
@@ -0,0 +1,663 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Experimental pipeline API functionality. Be careful with this API, it's subject to change."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import datetime
|
| 6 |
+
import operator
|
| 7 |
+
import re
|
| 8 |
+
import sys
|
| 9 |
+
from collections import deque
|
| 10 |
+
from collections.abc import Container
|
| 11 |
+
from dataclasses import dataclass
|
| 12 |
+
from decimal import Decimal
|
| 13 |
+
from functools import cached_property, partial
|
| 14 |
+
from re import Pattern
|
| 15 |
+
from typing import TYPE_CHECKING, Annotated, Any, Callable, Generic, Protocol, TypeVar, Union, overload
|
| 16 |
+
|
| 17 |
+
import annotated_types
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from pydantic import GetCoreSchemaHandler
|
| 21 |
+
|
| 22 |
+
from pydantic_core import PydanticCustomError
|
| 23 |
+
from pydantic_core import core_schema as cs
|
| 24 |
+
|
| 25 |
+
from pydantic import Strict
|
| 26 |
+
from pydantic._internal._internal_dataclass import slots_true as _slots_true
|
| 27 |
+
|
| 28 |
+
if sys.version_info < (3, 10):
|
| 29 |
+
EllipsisType = type(Ellipsis)
|
| 30 |
+
else:
|
| 31 |
+
from types import EllipsisType
|
| 32 |
+
|
| 33 |
+
__all__ = ['validate_as', 'validate_as_deferred', 'transform']
|
| 34 |
+
|
| 35 |
+
_slots_frozen = {**_slots_true, 'frozen': True}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@dataclass(**_slots_frozen)
|
| 39 |
+
class _ValidateAs:
|
| 40 |
+
tp: type[Any]
|
| 41 |
+
strict: bool = False
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@dataclass
|
| 45 |
+
class _ValidateAsDefer:
|
| 46 |
+
func: Callable[[], type[Any]]
|
| 47 |
+
|
| 48 |
+
@cached_property
|
| 49 |
+
def tp(self) -> type[Any]:
|
| 50 |
+
return self.func()
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@dataclass(**_slots_frozen)
|
| 54 |
+
class _Transform:
|
| 55 |
+
func: Callable[[Any], Any]
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
@dataclass(**_slots_frozen)
|
| 59 |
+
class _PipelineOr:
|
| 60 |
+
left: _Pipeline[Any, Any]
|
| 61 |
+
right: _Pipeline[Any, Any]
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
@dataclass(**_slots_frozen)
|
| 65 |
+
class _PipelineAnd:
|
| 66 |
+
left: _Pipeline[Any, Any]
|
| 67 |
+
right: _Pipeline[Any, Any]
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
@dataclass(**_slots_frozen)
|
| 71 |
+
class _Eq:
|
| 72 |
+
value: Any
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
@dataclass(**_slots_frozen)
|
| 76 |
+
class _NotEq:
|
| 77 |
+
value: Any
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
@dataclass(**_slots_frozen)
|
| 81 |
+
class _In:
|
| 82 |
+
values: Container[Any]
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
@dataclass(**_slots_frozen)
|
| 86 |
+
class _NotIn:
|
| 87 |
+
values: Container[Any]
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
_ConstraintAnnotation = Union[
|
| 91 |
+
annotated_types.Le,
|
| 92 |
+
annotated_types.Ge,
|
| 93 |
+
annotated_types.Lt,
|
| 94 |
+
annotated_types.Gt,
|
| 95 |
+
annotated_types.Len,
|
| 96 |
+
annotated_types.MultipleOf,
|
| 97 |
+
annotated_types.Timezone,
|
| 98 |
+
annotated_types.Interval,
|
| 99 |
+
annotated_types.Predicate,
|
| 100 |
+
# common predicates not included in annotated_types
|
| 101 |
+
_Eq,
|
| 102 |
+
_NotEq,
|
| 103 |
+
_In,
|
| 104 |
+
_NotIn,
|
| 105 |
+
# regular expressions
|
| 106 |
+
Pattern[str],
|
| 107 |
+
]
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
@dataclass(**_slots_frozen)
|
| 111 |
+
class _Constraint:
|
| 112 |
+
constraint: _ConstraintAnnotation
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
_Step = Union[_ValidateAs, _ValidateAsDefer, _Transform, _PipelineOr, _PipelineAnd, _Constraint]
|
| 116 |
+
|
| 117 |
+
_InT = TypeVar('_InT')
|
| 118 |
+
_OutT = TypeVar('_OutT')
|
| 119 |
+
_NewOutT = TypeVar('_NewOutT')
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class _FieldTypeMarker:
|
| 123 |
+
pass
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
# TODO: ultimately, make this public, see https://github.com/pydantic/pydantic/pull/9459#discussion_r1628197626
|
| 127 |
+
# Also, make this frozen eventually, but that doesn't work right now because of the generic base
|
| 128 |
+
# Which attempts to modify __orig_base__ and such.
|
| 129 |
+
# We could go with a manual freeze, but that seems overkill for now.
|
| 130 |
+
@dataclass(**_slots_true)
|
| 131 |
+
class _Pipeline(Generic[_InT, _OutT]):
|
| 132 |
+
"""Abstract representation of a chain of validation, transformation, and parsing steps."""
|
| 133 |
+
|
| 134 |
+
_steps: tuple[_Step, ...]
|
| 135 |
+
|
| 136 |
+
def transform(
|
| 137 |
+
self,
|
| 138 |
+
func: Callable[[_OutT], _NewOutT],
|
| 139 |
+
) -> _Pipeline[_InT, _NewOutT]:
|
| 140 |
+
"""Transform the output of the previous step.
|
| 141 |
+
|
| 142 |
+
If used as the first step in a pipeline, the type of the field is used.
|
| 143 |
+
That is, the transformation is applied to after the value is parsed to the field's type.
|
| 144 |
+
"""
|
| 145 |
+
return _Pipeline[_InT, _NewOutT](self._steps + (_Transform(func),))
|
| 146 |
+
|
| 147 |
+
@overload
|
| 148 |
+
def validate_as(self, tp: type[_NewOutT], *, strict: bool = False) -> _Pipeline[_InT, _NewOutT]: ...
|
| 149 |
+
|
| 150 |
+
@overload
|
| 151 |
+
def validate_as(
|
| 152 |
+
self,
|
| 153 |
+
tp: ellipsis, # noqa: F821 # TODO: use `_typing_extra.EllipsisType` when we drop Py3.9
|
| 154 |
+
*,
|
| 155 |
+
strict: bool = False,
|
| 156 |
+
) -> _Pipeline[_InT, Any]: ...
|
| 157 |
+
|
| 158 |
+
# TODO PEP 747: use TypeForm to properly type Annotated aliases (e.g. NewPath, FilePath).
|
| 159 |
+
# This fallback accepts any type expression but loses generic type inference.
|
| 160 |
+
@overload
|
| 161 |
+
def validate_as(self, tp: Any, *, strict: bool = ...) -> _Pipeline[_InT, Any]: ...
|
| 162 |
+
|
| 163 |
+
def validate_as(self, tp: type[_NewOutT] | EllipsisType | Any, *, strict: bool = False) -> _Pipeline[_InT, Any]: # type: ignore
|
| 164 |
+
"""Validate / parse the input into a new type.
|
| 165 |
+
|
| 166 |
+
If no type is provided, the type of the field is used.
|
| 167 |
+
|
| 168 |
+
Types are parsed in Pydantic's `lax` mode by default,
|
| 169 |
+
but you can enable `strict` mode by passing `strict=True`.
|
| 170 |
+
"""
|
| 171 |
+
if isinstance(tp, EllipsisType):
|
| 172 |
+
return _Pipeline[_InT, Any](self._steps + (_ValidateAs(_FieldTypeMarker, strict=strict),))
|
| 173 |
+
return _Pipeline[_InT, _NewOutT](self._steps + (_ValidateAs(tp, strict=strict),))
|
| 174 |
+
|
| 175 |
+
def validate_as_deferred(self, func: Callable[[], type[_NewOutT]]) -> _Pipeline[_InT, _NewOutT]:
|
| 176 |
+
"""Parse the input into a new type, deferring resolution of the type until the current class
|
| 177 |
+
is fully defined.
|
| 178 |
+
|
| 179 |
+
This is useful when you need to reference the class in it's own type annotations.
|
| 180 |
+
"""
|
| 181 |
+
return _Pipeline[_InT, _NewOutT](self._steps + (_ValidateAsDefer(func),))
|
| 182 |
+
|
| 183 |
+
# constraints
|
| 184 |
+
@overload
|
| 185 |
+
def constrain(self: _Pipeline[_InT, _NewOutGe], constraint: annotated_types.Ge) -> _Pipeline[_InT, _NewOutGe]: ...
|
| 186 |
+
|
| 187 |
+
@overload
|
| 188 |
+
def constrain(self: _Pipeline[_InT, _NewOutGt], constraint: annotated_types.Gt) -> _Pipeline[_InT, _NewOutGt]: ...
|
| 189 |
+
|
| 190 |
+
@overload
|
| 191 |
+
def constrain(self: _Pipeline[_InT, _NewOutLe], constraint: annotated_types.Le) -> _Pipeline[_InT, _NewOutLe]: ...
|
| 192 |
+
|
| 193 |
+
@overload
|
| 194 |
+
def constrain(self: _Pipeline[_InT, _NewOutLt], constraint: annotated_types.Lt) -> _Pipeline[_InT, _NewOutLt]: ...
|
| 195 |
+
|
| 196 |
+
@overload
|
| 197 |
+
def constrain(
|
| 198 |
+
self: _Pipeline[_InT, _NewOutLen], constraint: annotated_types.Len
|
| 199 |
+
) -> _Pipeline[_InT, _NewOutLen]: ...
|
| 200 |
+
|
| 201 |
+
@overload
|
| 202 |
+
def constrain(
|
| 203 |
+
self: _Pipeline[_InT, _NewOutT], constraint: annotated_types.MultipleOf
|
| 204 |
+
) -> _Pipeline[_InT, _NewOutT]: ...
|
| 205 |
+
|
| 206 |
+
@overload
|
| 207 |
+
def constrain(
|
| 208 |
+
self: _Pipeline[_InT, _NewOutDatetime], constraint: annotated_types.Timezone
|
| 209 |
+
) -> _Pipeline[_InT, _NewOutDatetime]: ...
|
| 210 |
+
|
| 211 |
+
@overload
|
| 212 |
+
def constrain(self: _Pipeline[_InT, _OutT], constraint: annotated_types.Predicate) -> _Pipeline[_InT, _OutT]: ...
|
| 213 |
+
|
| 214 |
+
@overload
|
| 215 |
+
def constrain(
|
| 216 |
+
self: _Pipeline[_InT, _NewOutInterval], constraint: annotated_types.Interval
|
| 217 |
+
) -> _Pipeline[_InT, _NewOutInterval]: ...
|
| 218 |
+
|
| 219 |
+
@overload
|
| 220 |
+
def constrain(self: _Pipeline[_InT, _OutT], constraint: _Eq) -> _Pipeline[_InT, _OutT]: ...
|
| 221 |
+
|
| 222 |
+
@overload
|
| 223 |
+
def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotEq) -> _Pipeline[_InT, _OutT]: ...
|
| 224 |
+
|
| 225 |
+
@overload
|
| 226 |
+
def constrain(self: _Pipeline[_InT, _OutT], constraint: _In) -> _Pipeline[_InT, _OutT]: ...
|
| 227 |
+
|
| 228 |
+
@overload
|
| 229 |
+
def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotIn) -> _Pipeline[_InT, _OutT]: ...
|
| 230 |
+
|
| 231 |
+
@overload
|
| 232 |
+
def constrain(self: _Pipeline[_InT, _NewOutT], constraint: Pattern[str]) -> _Pipeline[_InT, _NewOutT]: ...
|
| 233 |
+
|
| 234 |
+
def constrain(self, constraint: _ConstraintAnnotation) -> Any:
|
| 235 |
+
"""Constrain a value to meet a certain condition.
|
| 236 |
+
|
| 237 |
+
We support most conditions from `annotated_types`, as well as regular expressions.
|
| 238 |
+
|
| 239 |
+
Most of the time you'll be calling a shortcut method like `gt`, `lt`, `len`, etc
|
| 240 |
+
so you don't need to call this directly.
|
| 241 |
+
"""
|
| 242 |
+
return _Pipeline[_InT, _OutT](self._steps + (_Constraint(constraint),))
|
| 243 |
+
|
| 244 |
+
def predicate(self: _Pipeline[_InT, _NewOutT], func: Callable[[_NewOutT], bool]) -> _Pipeline[_InT, _NewOutT]:
|
| 245 |
+
"""Constrain a value to meet a certain predicate."""
|
| 246 |
+
return self.constrain(annotated_types.Predicate(func))
|
| 247 |
+
|
| 248 |
+
def gt(self: _Pipeline[_InT, _NewOutGt], gt: _NewOutGt) -> _Pipeline[_InT, _NewOutGt]:
|
| 249 |
+
"""Constrain a value to be greater than a certain value."""
|
| 250 |
+
return self.constrain(annotated_types.Gt(gt))
|
| 251 |
+
|
| 252 |
+
def lt(self: _Pipeline[_InT, _NewOutLt], lt: _NewOutLt) -> _Pipeline[_InT, _NewOutLt]:
|
| 253 |
+
"""Constrain a value to be less than a certain value."""
|
| 254 |
+
return self.constrain(annotated_types.Lt(lt))
|
| 255 |
+
|
| 256 |
+
def ge(self: _Pipeline[_InT, _NewOutGe], ge: _NewOutGe) -> _Pipeline[_InT, _NewOutGe]:
|
| 257 |
+
"""Constrain a value to be greater than or equal to a certain value."""
|
| 258 |
+
return self.constrain(annotated_types.Ge(ge))
|
| 259 |
+
|
| 260 |
+
def le(self: _Pipeline[_InT, _NewOutLe], le: _NewOutLe) -> _Pipeline[_InT, _NewOutLe]:
|
| 261 |
+
"""Constrain a value to be less than or equal to a certain value."""
|
| 262 |
+
return self.constrain(annotated_types.Le(le))
|
| 263 |
+
|
| 264 |
+
def len(self: _Pipeline[_InT, _NewOutLen], min_len: int, max_len: int | None = None) -> _Pipeline[_InT, _NewOutLen]:
|
| 265 |
+
"""Constrain a value to have a certain length."""
|
| 266 |
+
return self.constrain(annotated_types.Len(min_len, max_len))
|
| 267 |
+
|
| 268 |
+
@overload
|
| 269 |
+
def multiple_of(self: _Pipeline[_InT, _NewOutDiv], multiple_of: _NewOutDiv) -> _Pipeline[_InT, _NewOutDiv]: ...
|
| 270 |
+
|
| 271 |
+
@overload
|
| 272 |
+
def multiple_of(self: _Pipeline[_InT, _NewOutMod], multiple_of: _NewOutMod) -> _Pipeline[_InT, _NewOutMod]: ...
|
| 273 |
+
|
| 274 |
+
def multiple_of(self: _Pipeline[_InT, Any], multiple_of: Any) -> _Pipeline[_InT, Any]:
|
| 275 |
+
"""Constrain a value to be a multiple of a certain number."""
|
| 276 |
+
return self.constrain(annotated_types.MultipleOf(multiple_of))
|
| 277 |
+
|
| 278 |
+
def eq(self: _Pipeline[_InT, _OutT], value: _OutT) -> _Pipeline[_InT, _OutT]:
|
| 279 |
+
"""Constrain a value to be equal to a certain value."""
|
| 280 |
+
return self.constrain(_Eq(value))
|
| 281 |
+
|
| 282 |
+
def not_eq(self: _Pipeline[_InT, _OutT], value: _OutT) -> _Pipeline[_InT, _OutT]:
|
| 283 |
+
"""Constrain a value to not be equal to a certain value."""
|
| 284 |
+
return self.constrain(_NotEq(value))
|
| 285 |
+
|
| 286 |
+
def in_(self: _Pipeline[_InT, _OutT], values: Container[_OutT]) -> _Pipeline[_InT, _OutT]:
|
| 287 |
+
"""Constrain a value to be in a certain set."""
|
| 288 |
+
return self.constrain(_In(values))
|
| 289 |
+
|
| 290 |
+
def not_in(self: _Pipeline[_InT, _OutT], values: Container[_OutT]) -> _Pipeline[_InT, _OutT]:
|
| 291 |
+
"""Constrain a value to not be in a certain set."""
|
| 292 |
+
return self.constrain(_NotIn(values))
|
| 293 |
+
|
| 294 |
+
# timezone methods
|
| 295 |
+
def datetime_tz_naive(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]:
|
| 296 |
+
return self.constrain(annotated_types.Timezone(None))
|
| 297 |
+
|
| 298 |
+
def datetime_tz_aware(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]:
|
| 299 |
+
return self.constrain(annotated_types.Timezone(...))
|
| 300 |
+
|
| 301 |
+
def datetime_tz(
|
| 302 |
+
self: _Pipeline[_InT, datetime.datetime], tz: datetime.tzinfo
|
| 303 |
+
) -> _Pipeline[_InT, datetime.datetime]:
|
| 304 |
+
return self.constrain(annotated_types.Timezone(tz)) # type: ignore
|
| 305 |
+
|
| 306 |
+
def datetime_with_tz(
|
| 307 |
+
self: _Pipeline[_InT, datetime.datetime], tz: datetime.tzinfo | None
|
| 308 |
+
) -> _Pipeline[_InT, datetime.datetime]:
|
| 309 |
+
return self.transform(partial(datetime.datetime.replace, tzinfo=tz))
|
| 310 |
+
|
| 311 |
+
# string methods
|
| 312 |
+
def str_lower(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
|
| 313 |
+
return self.transform(str.lower)
|
| 314 |
+
|
| 315 |
+
def str_upper(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
|
| 316 |
+
return self.transform(str.upper)
|
| 317 |
+
|
| 318 |
+
def str_title(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
|
| 319 |
+
return self.transform(str.title)
|
| 320 |
+
|
| 321 |
+
def str_strip(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
|
| 322 |
+
return self.transform(str.strip)
|
| 323 |
+
|
| 324 |
+
def str_pattern(self: _Pipeline[_InT, str], pattern: str) -> _Pipeline[_InT, str]:
|
| 325 |
+
return self.constrain(re.compile(pattern))
|
| 326 |
+
|
| 327 |
+
def str_contains(self: _Pipeline[_InT, str], substring: str) -> _Pipeline[_InT, str]:
|
| 328 |
+
return self.predicate(lambda v: substring in v)
|
| 329 |
+
|
| 330 |
+
def str_starts_with(self: _Pipeline[_InT, str], prefix: str) -> _Pipeline[_InT, str]:
|
| 331 |
+
return self.predicate(lambda v: v.startswith(prefix))
|
| 332 |
+
|
| 333 |
+
def str_ends_with(self: _Pipeline[_InT, str], suffix: str) -> _Pipeline[_InT, str]:
|
| 334 |
+
return self.predicate(lambda v: v.endswith(suffix))
|
| 335 |
+
|
| 336 |
+
# operators
|
| 337 |
+
def otherwise(self, other: _Pipeline[_OtherIn, _OtherOut]) -> _Pipeline[_InT | _OtherIn, _OutT | _OtherOut]:
|
| 338 |
+
"""Combine two validation chains, returning the result of the first chain if it succeeds, and the second chain if it fails."""
|
| 339 |
+
return _Pipeline((_PipelineOr(self, other),))
|
| 340 |
+
|
| 341 |
+
__or__ = otherwise
|
| 342 |
+
|
| 343 |
+
def then(self, other: _Pipeline[_OutT, _OtherOut]) -> _Pipeline[_InT, _OtherOut]:
|
| 344 |
+
"""Pipe the result of one validation chain into another."""
|
| 345 |
+
return _Pipeline((_PipelineAnd(self, other),))
|
| 346 |
+
|
| 347 |
+
__and__ = then
|
| 348 |
+
|
| 349 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> cs.CoreSchema:
|
| 350 |
+
queue = deque(self._steps)
|
| 351 |
+
|
| 352 |
+
s = None
|
| 353 |
+
|
| 354 |
+
while queue:
|
| 355 |
+
step = queue.popleft()
|
| 356 |
+
s = _apply_step(step, s, handler, source_type)
|
| 357 |
+
|
| 358 |
+
s = s or cs.any_schema()
|
| 359 |
+
return s
|
| 360 |
+
|
| 361 |
+
def __supports_type__(self, _: _OutT) -> bool:
|
| 362 |
+
raise NotImplementedError
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
validate_as = _Pipeline[Any, Any](()).validate_as
|
| 366 |
+
validate_as_deferred = _Pipeline[Any, Any](()).validate_as_deferred
|
| 367 |
+
transform = _Pipeline[Any, Any]((_ValidateAs(_FieldTypeMarker),)).transform
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
def _check_func(
|
| 371 |
+
func: Callable[[Any], bool], predicate_err: str | Callable[[], str], s: cs.CoreSchema | None
|
| 372 |
+
) -> cs.CoreSchema:
|
| 373 |
+
def handler(v: Any) -> Any:
|
| 374 |
+
if func(v):
|
| 375 |
+
return v
|
| 376 |
+
raise ValueError(f'Expected {predicate_err if isinstance(predicate_err, str) else predicate_err()}')
|
| 377 |
+
|
| 378 |
+
if s is None:
|
| 379 |
+
return cs.no_info_plain_validator_function(handler)
|
| 380 |
+
else:
|
| 381 |
+
return cs.no_info_after_validator_function(handler, s)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def _apply_step(step: _Step, s: cs.CoreSchema | None, handler: GetCoreSchemaHandler, source_type: Any) -> cs.CoreSchema:
|
| 385 |
+
if isinstance(step, _ValidateAs):
|
| 386 |
+
s = _apply_parse(s, step.tp, step.strict, handler, source_type)
|
| 387 |
+
elif isinstance(step, _ValidateAsDefer):
|
| 388 |
+
s = _apply_parse(s, step.tp, False, handler, source_type)
|
| 389 |
+
elif isinstance(step, _Transform):
|
| 390 |
+
s = _apply_transform(s, step.func, handler)
|
| 391 |
+
elif isinstance(step, _Constraint):
|
| 392 |
+
s = _apply_constraint(s, step.constraint)
|
| 393 |
+
elif isinstance(step, _PipelineOr):
|
| 394 |
+
s = cs.union_schema([handler(step.left), handler(step.right)])
|
| 395 |
+
else:
|
| 396 |
+
assert isinstance(step, _PipelineAnd)
|
| 397 |
+
s = cs.chain_schema([handler(step.left), handler(step.right)])
|
| 398 |
+
return s
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def _apply_parse(
|
| 402 |
+
s: cs.CoreSchema | None,
|
| 403 |
+
tp: type[Any],
|
| 404 |
+
strict: bool,
|
| 405 |
+
handler: GetCoreSchemaHandler,
|
| 406 |
+
source_type: Any,
|
| 407 |
+
) -> cs.CoreSchema:
|
| 408 |
+
if tp is _FieldTypeMarker:
|
| 409 |
+
return cs.chain_schema([s, handler(source_type)]) if s else handler(source_type)
|
| 410 |
+
|
| 411 |
+
if strict:
|
| 412 |
+
tp = Annotated[tp, Strict()] # type: ignore
|
| 413 |
+
|
| 414 |
+
if s and s['type'] == 'any':
|
| 415 |
+
return handler(tp)
|
| 416 |
+
else:
|
| 417 |
+
return cs.chain_schema([s, handler(tp)]) if s else handler(tp)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def _apply_transform(
|
| 421 |
+
s: cs.CoreSchema | None, func: Callable[[Any], Any], handler: GetCoreSchemaHandler
|
| 422 |
+
) -> cs.CoreSchema:
|
| 423 |
+
if s is None:
|
| 424 |
+
return cs.no_info_plain_validator_function(func)
|
| 425 |
+
|
| 426 |
+
if s['type'] == 'str':
|
| 427 |
+
if func is str.strip:
|
| 428 |
+
s = s.copy()
|
| 429 |
+
s['strip_whitespace'] = True
|
| 430 |
+
return s
|
| 431 |
+
elif func is str.lower:
|
| 432 |
+
s = s.copy()
|
| 433 |
+
s['to_lower'] = True
|
| 434 |
+
return s
|
| 435 |
+
elif func is str.upper:
|
| 436 |
+
s = s.copy()
|
| 437 |
+
s['to_upper'] = True
|
| 438 |
+
return s
|
| 439 |
+
|
| 440 |
+
return cs.no_info_after_validator_function(func, s)
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
def _apply_constraint( # noqa: C901
|
| 444 |
+
s: cs.CoreSchema | None, constraint: _ConstraintAnnotation
|
| 445 |
+
) -> cs.CoreSchema:
|
| 446 |
+
"""Apply a single constraint to a schema."""
|
| 447 |
+
if isinstance(constraint, annotated_types.Gt):
|
| 448 |
+
gt = constraint.gt
|
| 449 |
+
if s and s['type'] in {'int', 'float', 'decimal'}:
|
| 450 |
+
s = s.copy()
|
| 451 |
+
if s['type'] == 'int' and isinstance(gt, int):
|
| 452 |
+
s['gt'] = gt
|
| 453 |
+
elif s['type'] == 'float' and isinstance(gt, float):
|
| 454 |
+
s['gt'] = gt
|
| 455 |
+
elif s['type'] == 'decimal' and isinstance(gt, Decimal):
|
| 456 |
+
s['gt'] = gt
|
| 457 |
+
else:
|
| 458 |
+
|
| 459 |
+
def check_gt(v: Any) -> bool:
|
| 460 |
+
return v > gt
|
| 461 |
+
|
| 462 |
+
s = _check_func(check_gt, f'> {gt}', s)
|
| 463 |
+
elif isinstance(constraint, annotated_types.Ge):
|
| 464 |
+
ge = constraint.ge
|
| 465 |
+
if s and s['type'] in {'int', 'float', 'decimal'}:
|
| 466 |
+
s = s.copy()
|
| 467 |
+
if s['type'] == 'int' and isinstance(ge, int):
|
| 468 |
+
s['ge'] = ge
|
| 469 |
+
elif s['type'] == 'float' and isinstance(ge, float):
|
| 470 |
+
s['ge'] = ge
|
| 471 |
+
elif s['type'] == 'decimal' and isinstance(ge, Decimal):
|
| 472 |
+
s['ge'] = ge
|
| 473 |
+
|
| 474 |
+
def check_ge(v: Any) -> bool:
|
| 475 |
+
return v >= ge
|
| 476 |
+
|
| 477 |
+
s = _check_func(check_ge, f'>= {ge}', s)
|
| 478 |
+
elif isinstance(constraint, annotated_types.Lt):
|
| 479 |
+
lt = constraint.lt
|
| 480 |
+
if s and s['type'] in {'int', 'float', 'decimal'}:
|
| 481 |
+
s = s.copy()
|
| 482 |
+
if s['type'] == 'int' and isinstance(lt, int):
|
| 483 |
+
s['lt'] = lt
|
| 484 |
+
elif s['type'] == 'float' and isinstance(lt, float):
|
| 485 |
+
s['lt'] = lt
|
| 486 |
+
elif s['type'] == 'decimal' and isinstance(lt, Decimal):
|
| 487 |
+
s['lt'] = lt
|
| 488 |
+
|
| 489 |
+
def check_lt(v: Any) -> bool:
|
| 490 |
+
return v < lt
|
| 491 |
+
|
| 492 |
+
s = _check_func(check_lt, f'< {lt}', s)
|
| 493 |
+
elif isinstance(constraint, annotated_types.Le):
|
| 494 |
+
le = constraint.le
|
| 495 |
+
if s and s['type'] in {'int', 'float', 'decimal'}:
|
| 496 |
+
s = s.copy()
|
| 497 |
+
if s['type'] == 'int' and isinstance(le, int):
|
| 498 |
+
s['le'] = le
|
| 499 |
+
elif s['type'] == 'float' and isinstance(le, float):
|
| 500 |
+
s['le'] = le
|
| 501 |
+
elif s['type'] == 'decimal' and isinstance(le, Decimal):
|
| 502 |
+
s['le'] = le
|
| 503 |
+
|
| 504 |
+
def check_le(v: Any) -> bool:
|
| 505 |
+
return v <= le
|
| 506 |
+
|
| 507 |
+
s = _check_func(check_le, f'<= {le}', s)
|
| 508 |
+
elif isinstance(constraint, annotated_types.Len):
|
| 509 |
+
min_len = constraint.min_length
|
| 510 |
+
max_len = constraint.max_length
|
| 511 |
+
|
| 512 |
+
if s and s['type'] in {'str', 'list', 'tuple', 'set', 'frozenset', 'dict'}:
|
| 513 |
+
assert (
|
| 514 |
+
s['type'] == 'str'
|
| 515 |
+
or s['type'] == 'list'
|
| 516 |
+
or s['type'] == 'tuple'
|
| 517 |
+
or s['type'] == 'set'
|
| 518 |
+
or s['type'] == 'dict'
|
| 519 |
+
or s['type'] == 'frozenset'
|
| 520 |
+
)
|
| 521 |
+
s = s.copy()
|
| 522 |
+
if min_len != 0:
|
| 523 |
+
s['min_length'] = min_len
|
| 524 |
+
if max_len is not None:
|
| 525 |
+
s['max_length'] = max_len
|
| 526 |
+
|
| 527 |
+
def check_len(v: Any) -> bool:
|
| 528 |
+
if max_len is not None:
|
| 529 |
+
return (min_len <= len(v)) and (len(v) <= max_len)
|
| 530 |
+
return min_len <= len(v)
|
| 531 |
+
|
| 532 |
+
s = _check_func(check_len, f'length >= {min_len} and length <= {max_len}', s)
|
| 533 |
+
elif isinstance(constraint, annotated_types.MultipleOf):
|
| 534 |
+
multiple_of = constraint.multiple_of
|
| 535 |
+
if s and s['type'] in {'int', 'float', 'decimal'}:
|
| 536 |
+
s = s.copy()
|
| 537 |
+
if s['type'] == 'int' and isinstance(multiple_of, int):
|
| 538 |
+
s['multiple_of'] = multiple_of
|
| 539 |
+
elif s['type'] == 'float' and isinstance(multiple_of, float):
|
| 540 |
+
s['multiple_of'] = multiple_of
|
| 541 |
+
elif s['type'] == 'decimal' and isinstance(multiple_of, Decimal):
|
| 542 |
+
s['multiple_of'] = multiple_of
|
| 543 |
+
|
| 544 |
+
def check_multiple_of(v: Any) -> bool:
|
| 545 |
+
return v % multiple_of == 0
|
| 546 |
+
|
| 547 |
+
s = _check_func(check_multiple_of, f'% {multiple_of} == 0', s)
|
| 548 |
+
elif isinstance(constraint, annotated_types.Timezone):
|
| 549 |
+
tz = constraint.tz
|
| 550 |
+
|
| 551 |
+
if tz is ...:
|
| 552 |
+
if s and s['type'] == 'datetime':
|
| 553 |
+
s = s.copy()
|
| 554 |
+
s['tz_constraint'] = 'aware'
|
| 555 |
+
else:
|
| 556 |
+
|
| 557 |
+
def check_tz_aware(v: object) -> bool:
|
| 558 |
+
assert isinstance(v, datetime.datetime)
|
| 559 |
+
return v.tzinfo is not None
|
| 560 |
+
|
| 561 |
+
s = _check_func(check_tz_aware, 'timezone aware', s)
|
| 562 |
+
elif tz is None:
|
| 563 |
+
if s and s['type'] == 'datetime':
|
| 564 |
+
s = s.copy()
|
| 565 |
+
s['tz_constraint'] = 'naive'
|
| 566 |
+
else:
|
| 567 |
+
|
| 568 |
+
def check_tz_naive(v: object) -> bool:
|
| 569 |
+
assert isinstance(v, datetime.datetime)
|
| 570 |
+
return v.tzinfo is None
|
| 571 |
+
|
| 572 |
+
s = _check_func(check_tz_naive, 'timezone naive', s)
|
| 573 |
+
else:
|
| 574 |
+
raise NotImplementedError('Constraining to a specific timezone is not yet supported')
|
| 575 |
+
elif isinstance(constraint, annotated_types.Interval):
|
| 576 |
+
if constraint.ge:
|
| 577 |
+
s = _apply_constraint(s, annotated_types.Ge(constraint.ge))
|
| 578 |
+
if constraint.gt:
|
| 579 |
+
s = _apply_constraint(s, annotated_types.Gt(constraint.gt))
|
| 580 |
+
if constraint.le:
|
| 581 |
+
s = _apply_constraint(s, annotated_types.Le(constraint.le))
|
| 582 |
+
if constraint.lt:
|
| 583 |
+
s = _apply_constraint(s, annotated_types.Lt(constraint.lt))
|
| 584 |
+
assert s is not None
|
| 585 |
+
elif isinstance(constraint, annotated_types.Predicate):
|
| 586 |
+
func = constraint.func
|
| 587 |
+
# Same logic as in `_known_annotated_metadata.apply_known_metadata()`:
|
| 588 |
+
predicate_name = f'{func.__qualname__!r} ' if hasattr(func, '__qualname__') else ''
|
| 589 |
+
|
| 590 |
+
def predicate_func(v: Any) -> Any:
|
| 591 |
+
if not func(v):
|
| 592 |
+
raise PydanticCustomError(
|
| 593 |
+
'predicate_failed',
|
| 594 |
+
f'Predicate {predicate_name}failed', # pyright: ignore[reportArgumentType]
|
| 595 |
+
)
|
| 596 |
+
return v
|
| 597 |
+
|
| 598 |
+
if s is None:
|
| 599 |
+
s = cs.no_info_plain_validator_function(predicate_func)
|
| 600 |
+
else:
|
| 601 |
+
s = cs.no_info_after_validator_function(predicate_func, s)
|
| 602 |
+
elif isinstance(constraint, _NotEq):
|
| 603 |
+
value = constraint.value
|
| 604 |
+
|
| 605 |
+
def check_not_eq(v: Any) -> bool:
|
| 606 |
+
return operator.__ne__(v, value)
|
| 607 |
+
|
| 608 |
+
s = _check_func(check_not_eq, f'!= {value}', s)
|
| 609 |
+
elif isinstance(constraint, _Eq):
|
| 610 |
+
value = constraint.value
|
| 611 |
+
|
| 612 |
+
def check_eq(v: Any) -> bool:
|
| 613 |
+
return operator.__eq__(v, value)
|
| 614 |
+
|
| 615 |
+
s = _check_func(check_eq, f'== {value}', s)
|
| 616 |
+
elif isinstance(constraint, _In):
|
| 617 |
+
values = constraint.values
|
| 618 |
+
|
| 619 |
+
def check_in(v: Any) -> bool:
|
| 620 |
+
return operator.__contains__(values, v)
|
| 621 |
+
|
| 622 |
+
s = _check_func(check_in, f'in {values}', s)
|
| 623 |
+
elif isinstance(constraint, _NotIn):
|
| 624 |
+
values = constraint.values
|
| 625 |
+
|
| 626 |
+
def check_not_in(v: Any) -> bool:
|
| 627 |
+
return operator.__not__(operator.__contains__(values, v))
|
| 628 |
+
|
| 629 |
+
s = _check_func(check_not_in, f'not in {values}', s)
|
| 630 |
+
else:
|
| 631 |
+
assert isinstance(constraint, Pattern)
|
| 632 |
+
if s and s['type'] == 'str':
|
| 633 |
+
s = s.copy()
|
| 634 |
+
s['pattern'] = constraint.pattern
|
| 635 |
+
else:
|
| 636 |
+
|
| 637 |
+
def check_pattern(v: object) -> bool:
|
| 638 |
+
assert isinstance(v, str)
|
| 639 |
+
return constraint.match(v) is not None
|
| 640 |
+
|
| 641 |
+
s = _check_func(check_pattern, f'~ {constraint.pattern}', s)
|
| 642 |
+
return s
|
| 643 |
+
|
| 644 |
+
|
| 645 |
+
class _SupportsRange(annotated_types.SupportsLe, annotated_types.SupportsGe, Protocol):
|
| 646 |
+
pass
|
| 647 |
+
|
| 648 |
+
|
| 649 |
+
class _SupportsLen(Protocol):
|
| 650 |
+
def __len__(self) -> int: ...
|
| 651 |
+
|
| 652 |
+
|
| 653 |
+
_NewOutGt = TypeVar('_NewOutGt', bound=annotated_types.SupportsGt)
|
| 654 |
+
_NewOutGe = TypeVar('_NewOutGe', bound=annotated_types.SupportsGe)
|
| 655 |
+
_NewOutLt = TypeVar('_NewOutLt', bound=annotated_types.SupportsLt)
|
| 656 |
+
_NewOutLe = TypeVar('_NewOutLe', bound=annotated_types.SupportsLe)
|
| 657 |
+
_NewOutLen = TypeVar('_NewOutLen', bound=_SupportsLen)
|
| 658 |
+
_NewOutDiv = TypeVar('_NewOutDiv', bound=annotated_types.SupportsDiv)
|
| 659 |
+
_NewOutMod = TypeVar('_NewOutMod', bound=annotated_types.SupportsMod)
|
| 660 |
+
_NewOutDatetime = TypeVar('_NewOutDatetime', bound=datetime.datetime)
|
| 661 |
+
_NewOutInterval = TypeVar('_NewOutInterval', bound=_SupportsRange)
|
| 662 |
+
_OtherIn = TypeVar('_OtherIn')
|
| 663 |
+
_OtherOut = TypeVar('_OtherOut')
|
.mcp-venv/lib/python3.13/site-packages/pydantic/fields.py
ADDED
|
@@ -0,0 +1,1892 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Defining fields on models."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
import inspect
|
| 7 |
+
import re
|
| 8 |
+
import sys
|
| 9 |
+
from collections.abc import Callable, Mapping
|
| 10 |
+
from copy import copy
|
| 11 |
+
from dataclasses import Field as DataclassField
|
| 12 |
+
from functools import cached_property
|
| 13 |
+
from typing import TYPE_CHECKING, Annotated, Any, ClassVar, Literal, TypeVar, final, overload
|
| 14 |
+
from warnings import warn
|
| 15 |
+
|
| 16 |
+
import annotated_types
|
| 17 |
+
import typing_extensions
|
| 18 |
+
from pydantic_core import MISSING, PydanticUndefined
|
| 19 |
+
from typing_extensions import Self, TypeAlias, TypedDict, Unpack, deprecated
|
| 20 |
+
from typing_inspection import typing_objects
|
| 21 |
+
from typing_inspection.introspection import UNKNOWN, AnnotationSource, ForbiddenQualifier, Qualifier, inspect_annotation
|
| 22 |
+
|
| 23 |
+
from . import types
|
| 24 |
+
from ._internal import _decorators, _fields, _generics, _internal_dataclass, _repr, _typing_extra, _utils
|
| 25 |
+
from ._internal._namespace_utils import GlobalsNamespace, MappingNamespace
|
| 26 |
+
from .aliases import AliasChoices, AliasGenerator, AliasPath
|
| 27 |
+
from .config import JsonDict
|
| 28 |
+
from .errors import PydanticForbiddenQualifier, PydanticUserError
|
| 29 |
+
from .json_schema import PydanticJsonSchemaWarning
|
| 30 |
+
from .warnings import PydanticDeprecatedSince20
|
| 31 |
+
|
| 32 |
+
if TYPE_CHECKING:
|
| 33 |
+
from ._internal._config import ConfigWrapper
|
| 34 |
+
from ._internal._repr import ReprArgs
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
__all__ = 'Field', 'FieldInfo', 'PrivateAttr', 'computed_field'
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
_Unset: Any = PydanticUndefined
|
| 41 |
+
|
| 42 |
+
if sys.version_info >= (3, 13):
|
| 43 |
+
import warnings
|
| 44 |
+
|
| 45 |
+
Deprecated: TypeAlias = warnings.deprecated | deprecated
|
| 46 |
+
else:
|
| 47 |
+
Deprecated: TypeAlias = deprecated
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class _FromFieldInfoInputs(TypedDict, total=False):
|
| 51 |
+
"""This class exists solely to add type checking for the `**kwargs` in `FieldInfo.from_field`."""
|
| 52 |
+
|
| 53 |
+
# TODO PEP 747: use TypeForm:
|
| 54 |
+
annotation: type[Any] | None
|
| 55 |
+
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None
|
| 56 |
+
alias: str | None
|
| 57 |
+
alias_priority: int | None
|
| 58 |
+
validation_alias: str | AliasPath | AliasChoices | None
|
| 59 |
+
serialization_alias: str | None
|
| 60 |
+
title: str | None
|
| 61 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None
|
| 62 |
+
description: str | None
|
| 63 |
+
examples: list[Any] | None
|
| 64 |
+
exclude: bool | None
|
| 65 |
+
exclude_if: Callable[[Any], bool] | None
|
| 66 |
+
gt: annotated_types.SupportsGt | None
|
| 67 |
+
ge: annotated_types.SupportsGe | None
|
| 68 |
+
lt: annotated_types.SupportsLt | None
|
| 69 |
+
le: annotated_types.SupportsLe | None
|
| 70 |
+
multiple_of: float | None
|
| 71 |
+
strict: bool | None
|
| 72 |
+
min_length: int | None
|
| 73 |
+
max_length: int | None
|
| 74 |
+
pattern: str | re.Pattern[str] | None
|
| 75 |
+
allow_inf_nan: bool | None
|
| 76 |
+
max_digits: int | None
|
| 77 |
+
decimal_places: int | None
|
| 78 |
+
union_mode: Literal['smart', 'left_to_right'] | None
|
| 79 |
+
discriminator: str | types.Discriminator | None
|
| 80 |
+
deprecated: Deprecated | str | bool | None
|
| 81 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None
|
| 82 |
+
frozen: bool | None
|
| 83 |
+
validate_default: bool | None
|
| 84 |
+
repr: bool
|
| 85 |
+
init: bool | None
|
| 86 |
+
init_var: bool | None
|
| 87 |
+
kw_only: bool | None
|
| 88 |
+
coerce_numbers_to_str: bool | None
|
| 89 |
+
fail_fast: bool | None
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
class _FieldInfoInputs(_FromFieldInfoInputs, total=False):
|
| 93 |
+
"""This class exists solely to add type checking for the `**kwargs` in `FieldInfo.__init__`."""
|
| 94 |
+
|
| 95 |
+
default: Any
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class _FieldInfoAsDict(TypedDict, closed=True):
|
| 99 |
+
# TODO PEP 747: use TypeForm:
|
| 100 |
+
annotation: Any
|
| 101 |
+
metadata: list[Any]
|
| 102 |
+
attributes: dict[str, Any]
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
@final
|
| 106 |
+
class FieldInfo(_repr.Representation):
|
| 107 |
+
"""This class holds information about a field.
|
| 108 |
+
|
| 109 |
+
`FieldInfo` is used for any field definition regardless of whether the [`Field()`][pydantic.fields.Field]
|
| 110 |
+
function is explicitly used.
|
| 111 |
+
|
| 112 |
+
!!! warning
|
| 113 |
+
The `FieldInfo` class is meant to expose information about a field in a Pydantic model or dataclass.
|
| 114 |
+
`FieldInfo` instances shouldn't be instantiated directly, nor mutated.
|
| 115 |
+
|
| 116 |
+
If you need to derive a new model from another one and are willing to alter `FieldInfo` instances,
|
| 117 |
+
refer to this [dynamic model example](../examples/dynamic_models.md).
|
| 118 |
+
|
| 119 |
+
Attributes:
|
| 120 |
+
annotation: The type annotation of the field.
|
| 121 |
+
default: The default value of the field.
|
| 122 |
+
default_factory: A callable to generate the default value. The callable can either take 0 arguments
|
| 123 |
+
(in which case it is called as is) or a single argument containing the already validated data.
|
| 124 |
+
alias: The alias name of the field.
|
| 125 |
+
alias_priority: The priority of the field's alias.
|
| 126 |
+
validation_alias: The validation alias of the field.
|
| 127 |
+
serialization_alias: The serialization alias of the field.
|
| 128 |
+
title: The title of the field.
|
| 129 |
+
field_title_generator: A callable that takes a field name and returns title for it.
|
| 130 |
+
description: The description of the field.
|
| 131 |
+
examples: List of examples of the field.
|
| 132 |
+
exclude: Whether to exclude the field from the model serialization.
|
| 133 |
+
exclude_if: A callable that determines whether to exclude a field during serialization based on its value.
|
| 134 |
+
discriminator: Field name or Discriminator for discriminating the type in a tagged union.
|
| 135 |
+
deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport,
|
| 136 |
+
or a boolean. If `True`, a default deprecation message will be emitted when accessing the field.
|
| 137 |
+
json_schema_extra: A dict or callable to provide extra JSON schema properties.
|
| 138 |
+
frozen: Whether the field is frozen.
|
| 139 |
+
validate_default: Whether to validate the default value of the field.
|
| 140 |
+
repr: Whether to include the field in representation of the model.
|
| 141 |
+
init: Whether the field should be included in the constructor of the dataclass.
|
| 142 |
+
init_var: Whether the field should _only_ be included in the constructor of the dataclass, and not stored.
|
| 143 |
+
kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass.
|
| 144 |
+
metadata: The metadata list. Contains all the data that isn't expressed as direct `FieldInfo` attributes, including:
|
| 145 |
+
|
| 146 |
+
* Type-specific constraints, such as `gt` or `min_length` (these are converted to metadata classes such as `annotated_types.Gt`).
|
| 147 |
+
* Any other arbitrary object used within [`Annotated`][typing.Annotated] metadata
|
| 148 |
+
(e.g. [custom types handlers](../concepts/types.md#as-an-annotation) or any object not recognized by Pydantic).
|
| 149 |
+
"""
|
| 150 |
+
|
| 151 |
+
# TODO PEP 747: use TypeForm:
|
| 152 |
+
annotation: type[Any] | None
|
| 153 |
+
default: Any
|
| 154 |
+
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None
|
| 155 |
+
alias: str | None
|
| 156 |
+
alias_priority: int | None
|
| 157 |
+
validation_alias: str | AliasPath | AliasChoices | None
|
| 158 |
+
serialization_alias: str | None
|
| 159 |
+
title: str | None
|
| 160 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None
|
| 161 |
+
description: str | None
|
| 162 |
+
examples: list[Any] | None
|
| 163 |
+
exclude: bool | None
|
| 164 |
+
exclude_if: Callable[[Any], bool] | None
|
| 165 |
+
discriminator: str | types.Discriminator | None
|
| 166 |
+
deprecated: Deprecated | str | bool | None
|
| 167 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None
|
| 168 |
+
frozen: bool | None
|
| 169 |
+
validate_default: bool | None
|
| 170 |
+
repr: bool
|
| 171 |
+
init: bool | None
|
| 172 |
+
init_var: bool | None
|
| 173 |
+
kw_only: bool | None
|
| 174 |
+
metadata: list[Any]
|
| 175 |
+
|
| 176 |
+
__slots__ = (
|
| 177 |
+
'annotation',
|
| 178 |
+
'default',
|
| 179 |
+
'default_factory',
|
| 180 |
+
'alias',
|
| 181 |
+
'alias_priority',
|
| 182 |
+
'validation_alias',
|
| 183 |
+
'serialization_alias',
|
| 184 |
+
'title',
|
| 185 |
+
'field_title_generator',
|
| 186 |
+
'description',
|
| 187 |
+
'examples',
|
| 188 |
+
'exclude',
|
| 189 |
+
'exclude_if',
|
| 190 |
+
'discriminator',
|
| 191 |
+
'deprecated',
|
| 192 |
+
'json_schema_extra',
|
| 193 |
+
'frozen',
|
| 194 |
+
'validate_default',
|
| 195 |
+
'repr',
|
| 196 |
+
'init',
|
| 197 |
+
'init_var',
|
| 198 |
+
'kw_only',
|
| 199 |
+
'metadata',
|
| 200 |
+
'_attributes_set',
|
| 201 |
+
'_qualifiers',
|
| 202 |
+
'_complete',
|
| 203 |
+
'_original_assignment',
|
| 204 |
+
'_original_annotation',
|
| 205 |
+
'_final',
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
# used to convert kwargs to metadata/constraints,
|
| 209 |
+
# None has a special meaning - these items are collected into a `PydanticGeneralMetadata`
|
| 210 |
+
metadata_lookup: ClassVar[dict[str, Callable[[Any], Any] | None]] = {
|
| 211 |
+
'strict': types.Strict,
|
| 212 |
+
'gt': annotated_types.Gt,
|
| 213 |
+
'ge': annotated_types.Ge,
|
| 214 |
+
'lt': annotated_types.Lt,
|
| 215 |
+
'le': annotated_types.Le,
|
| 216 |
+
'multiple_of': annotated_types.MultipleOf,
|
| 217 |
+
'min_length': annotated_types.MinLen,
|
| 218 |
+
'max_length': annotated_types.MaxLen,
|
| 219 |
+
'pattern': None,
|
| 220 |
+
'allow_inf_nan': None,
|
| 221 |
+
'max_digits': None,
|
| 222 |
+
'decimal_places': None,
|
| 223 |
+
'union_mode': None,
|
| 224 |
+
'coerce_numbers_to_str': None,
|
| 225 |
+
'fail_fast': types.FailFast,
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
def __init__(self, **kwargs: Unpack[_FieldInfoInputs]) -> None:
|
| 229 |
+
"""This class should generally not be initialized directly; instead, use the `pydantic.fields.Field` function
|
| 230 |
+
or one of the constructor classmethods.
|
| 231 |
+
|
| 232 |
+
See the signature of `pydantic.fields.Field` for more details about the expected arguments.
|
| 233 |
+
"""
|
| 234 |
+
# Tracking the explicitly set attributes is necessary to correctly merge `Field()` functions
|
| 235 |
+
# (e.g. with `Annotated[int, Field(alias='a'), Field(alias=None)]`, even though `None` is the default value,
|
| 236 |
+
# we need to track that `alias=None` was explicitly set):
|
| 237 |
+
self._attributes_set = {k: v for k, v in kwargs.items() if v is not _Unset and k not in self.metadata_lookup}
|
| 238 |
+
kwargs = {k: _DefaultValues.get(k) if v is _Unset else v for k, v in kwargs.items()} # type: ignore
|
| 239 |
+
self.annotation = kwargs.get('annotation')
|
| 240 |
+
|
| 241 |
+
# Note: in theory, the second `pop()` arguments are not required below, as defaults are already set from `_DefaultsValues`.
|
| 242 |
+
default = kwargs.pop('default', PydanticUndefined)
|
| 243 |
+
if default is Ellipsis:
|
| 244 |
+
self.default = PydanticUndefined
|
| 245 |
+
self._attributes_set.pop('default', None)
|
| 246 |
+
else:
|
| 247 |
+
self.default = default
|
| 248 |
+
|
| 249 |
+
self.default_factory = kwargs.pop('default_factory', None)
|
| 250 |
+
|
| 251 |
+
if self.default is not PydanticUndefined and self.default_factory is not None:
|
| 252 |
+
raise TypeError('cannot specify both default and default_factory')
|
| 253 |
+
|
| 254 |
+
self.alias = kwargs.pop('alias', None)
|
| 255 |
+
self.validation_alias = kwargs.pop('validation_alias', None)
|
| 256 |
+
self.serialization_alias = kwargs.pop('serialization_alias', None)
|
| 257 |
+
alias_is_set = any(alias is not None for alias in (self.alias, self.validation_alias, self.serialization_alias))
|
| 258 |
+
self.alias_priority = kwargs.pop('alias_priority', None) or 2 if alias_is_set else None
|
| 259 |
+
self.title = kwargs.pop('title', None)
|
| 260 |
+
self.field_title_generator = kwargs.pop('field_title_generator', None)
|
| 261 |
+
self.description = kwargs.pop('description', None)
|
| 262 |
+
self.examples = kwargs.pop('examples', None)
|
| 263 |
+
self.exclude = kwargs.pop('exclude', None)
|
| 264 |
+
self.exclude_if = kwargs.pop('exclude_if', None)
|
| 265 |
+
self.discriminator = kwargs.pop('discriminator', None)
|
| 266 |
+
# For compatibility with FastAPI<=0.110.0, we preserve the existing value if it is not overridden
|
| 267 |
+
self.deprecated = kwargs.pop('deprecated', getattr(self, 'deprecated', None))
|
| 268 |
+
self.repr = kwargs.pop('repr', True)
|
| 269 |
+
self.json_schema_extra = kwargs.pop('json_schema_extra', None)
|
| 270 |
+
self.validate_default = kwargs.pop('validate_default', None)
|
| 271 |
+
self.frozen = kwargs.pop('frozen', None)
|
| 272 |
+
# currently only used on dataclasses
|
| 273 |
+
self.init = kwargs.pop('init', None)
|
| 274 |
+
self.init_var = kwargs.pop('init_var', None)
|
| 275 |
+
self.kw_only = kwargs.pop('kw_only', None)
|
| 276 |
+
|
| 277 |
+
self.metadata = self._collect_metadata(kwargs) # type: ignore
|
| 278 |
+
|
| 279 |
+
# Private attributes:
|
| 280 |
+
self._qualifiers: set[Qualifier] = set()
|
| 281 |
+
# Used to rebuild FieldInfo instances:
|
| 282 |
+
self._complete = True
|
| 283 |
+
self._original_annotation: Any = PydanticUndefined
|
| 284 |
+
self._original_assignment: Any = PydanticUndefined
|
| 285 |
+
# Used to track whether the `FieldInfo` instance represents the data about a field (and is exposed in `model_fields`/`__pydantic_fields__`),
|
| 286 |
+
# or if it is the result of the `Field()` function being used as metadata in an `Annotated` type/as an assignment
|
| 287 |
+
# (not an ideal pattern, see https://github.com/pydantic/pydantic/issues/11122):
|
| 288 |
+
self._final = False
|
| 289 |
+
|
| 290 |
+
@staticmethod
|
| 291 |
+
def from_field(default: Any = PydanticUndefined, **kwargs: Unpack[_FromFieldInfoInputs]) -> FieldInfo:
|
| 292 |
+
"""Create a new `FieldInfo` object with the `Field` function.
|
| 293 |
+
|
| 294 |
+
Args:
|
| 295 |
+
default: The default value for the field. Defaults to Undefined.
|
| 296 |
+
**kwargs: Additional arguments dictionary.
|
| 297 |
+
|
| 298 |
+
Raises:
|
| 299 |
+
TypeError: If 'annotation' is passed as a keyword argument.
|
| 300 |
+
|
| 301 |
+
Returns:
|
| 302 |
+
A new FieldInfo object with the given parameters.
|
| 303 |
+
|
| 304 |
+
Example:
|
| 305 |
+
This is how you can create a field with default value like this:
|
| 306 |
+
|
| 307 |
+
```python
|
| 308 |
+
import pydantic
|
| 309 |
+
|
| 310 |
+
class MyModel(pydantic.BaseModel):
|
| 311 |
+
foo: int = pydantic.Field(4)
|
| 312 |
+
```
|
| 313 |
+
"""
|
| 314 |
+
if 'annotation' in kwargs:
|
| 315 |
+
raise TypeError('"annotation" is not permitted as a Field keyword argument')
|
| 316 |
+
return FieldInfo(default=default, **kwargs)
|
| 317 |
+
|
| 318 |
+
@staticmethod
|
| 319 |
+
def from_annotation(annotation: type[Any], *, _source: AnnotationSource = AnnotationSource.ANY) -> FieldInfo:
|
| 320 |
+
"""Creates a `FieldInfo` instance from a bare annotation.
|
| 321 |
+
|
| 322 |
+
This function is used internally to create a `FieldInfo` from a bare annotation like this:
|
| 323 |
+
|
| 324 |
+
```python
|
| 325 |
+
import pydantic
|
| 326 |
+
|
| 327 |
+
class MyModel(pydantic.BaseModel):
|
| 328 |
+
foo: int # <-- like this
|
| 329 |
+
```
|
| 330 |
+
|
| 331 |
+
We also account for the case where the annotation can be an instance of `Annotated` and where
|
| 332 |
+
one of the (not first) arguments in `Annotated` is an instance of `FieldInfo`, e.g.:
|
| 333 |
+
|
| 334 |
+
```python
|
| 335 |
+
from typing import Annotated
|
| 336 |
+
|
| 337 |
+
import annotated_types
|
| 338 |
+
|
| 339 |
+
import pydantic
|
| 340 |
+
|
| 341 |
+
class MyModel(pydantic.BaseModel):
|
| 342 |
+
foo: Annotated[int, annotated_types.Gt(42)]
|
| 343 |
+
bar: Annotated[int, pydantic.Field(gt=42)]
|
| 344 |
+
```
|
| 345 |
+
|
| 346 |
+
Args:
|
| 347 |
+
annotation: An annotation object.
|
| 348 |
+
|
| 349 |
+
Returns:
|
| 350 |
+
An instance of the field metadata.
|
| 351 |
+
"""
|
| 352 |
+
try:
|
| 353 |
+
inspected_ann = inspect_annotation(
|
| 354 |
+
annotation,
|
| 355 |
+
annotation_source=_source,
|
| 356 |
+
unpack_type_aliases='skip',
|
| 357 |
+
)
|
| 358 |
+
except ForbiddenQualifier as e:
|
| 359 |
+
raise PydanticForbiddenQualifier(e.qualifier, annotation)
|
| 360 |
+
|
| 361 |
+
# TODO check for classvar and error?
|
| 362 |
+
|
| 363 |
+
# No assigned value, this happens when using a bare `Final` qualifier (also for other
|
| 364 |
+
# qualifiers, but they shouldn't appear here). In this case we infer the type as `Any`
|
| 365 |
+
# because we don't have any assigned value.
|
| 366 |
+
type_expr: Any = Any if inspected_ann.type is UNKNOWN else inspected_ann.type
|
| 367 |
+
final = 'final' in inspected_ann.qualifiers
|
| 368 |
+
metadata = inspected_ann.metadata
|
| 369 |
+
|
| 370 |
+
attr_overrides = {'annotation': type_expr}
|
| 371 |
+
if final:
|
| 372 |
+
attr_overrides['frozen'] = True
|
| 373 |
+
field_info = FieldInfo._construct(metadata, **attr_overrides)
|
| 374 |
+
field_info._qualifiers = inspected_ann.qualifiers
|
| 375 |
+
field_info._final = True
|
| 376 |
+
return field_info
|
| 377 |
+
|
| 378 |
+
@staticmethod
|
| 379 |
+
def from_annotated_attribute(
|
| 380 |
+
annotation: type[Any], default: Any, *, _source: AnnotationSource = AnnotationSource.ANY
|
| 381 |
+
) -> FieldInfo:
|
| 382 |
+
"""Create `FieldInfo` from an annotation with a default value.
|
| 383 |
+
|
| 384 |
+
This is used in cases like the following:
|
| 385 |
+
|
| 386 |
+
```python
|
| 387 |
+
from typing import Annotated
|
| 388 |
+
|
| 389 |
+
import annotated_types
|
| 390 |
+
|
| 391 |
+
import pydantic
|
| 392 |
+
|
| 393 |
+
class MyModel(pydantic.BaseModel):
|
| 394 |
+
foo: int = 4 # <-- like this
|
| 395 |
+
bar: Annotated[int, annotated_types.Gt(4)] = 4 # <-- or this
|
| 396 |
+
spam: Annotated[int, pydantic.Field(gt=4)] = 4 # <-- or this
|
| 397 |
+
```
|
| 398 |
+
|
| 399 |
+
Args:
|
| 400 |
+
annotation: The type annotation of the field.
|
| 401 |
+
default: The default value of the field.
|
| 402 |
+
|
| 403 |
+
Returns:
|
| 404 |
+
A field object with the passed values.
|
| 405 |
+
"""
|
| 406 |
+
if annotation is not MISSING and annotation is default:
|
| 407 |
+
raise PydanticUserError(
|
| 408 |
+
'Error when building FieldInfo from annotated attribute. '
|
| 409 |
+
"Make sure you don't have any field name clashing with a type annotation.",
|
| 410 |
+
code='unevaluable-type-annotation',
|
| 411 |
+
)
|
| 412 |
+
|
| 413 |
+
try:
|
| 414 |
+
inspected_ann = inspect_annotation(
|
| 415 |
+
annotation,
|
| 416 |
+
annotation_source=_source,
|
| 417 |
+
unpack_type_aliases='skip',
|
| 418 |
+
)
|
| 419 |
+
except ForbiddenQualifier as e:
|
| 420 |
+
raise PydanticForbiddenQualifier(e.qualifier, annotation)
|
| 421 |
+
|
| 422 |
+
# TODO check for classvar and error?
|
| 423 |
+
|
| 424 |
+
# TODO infer from the default, this can be done in v3 once we treat final fields with
|
| 425 |
+
# a default as proper fields and not class variables:
|
| 426 |
+
type_expr: Any = Any if inspected_ann.type is UNKNOWN else inspected_ann.type
|
| 427 |
+
final = 'final' in inspected_ann.qualifiers
|
| 428 |
+
metadata = inspected_ann.metadata
|
| 429 |
+
|
| 430 |
+
# HACK 1: the order in which the metadata is merged is inconsistent; we need to prepend
|
| 431 |
+
# metadata from the assignment at the beginning of the metadata. Changing this is only
|
| 432 |
+
# possible in v3 (at least). See https://github.com/pydantic/pydantic/issues/10507
|
| 433 |
+
prepend_metadata: list[Any] | None = None
|
| 434 |
+
attr_overrides = {'annotation': type_expr}
|
| 435 |
+
if final:
|
| 436 |
+
attr_overrides['frozen'] = True
|
| 437 |
+
|
| 438 |
+
# HACK 2: FastAPI is subclassing `FieldInfo` and historically expected the actual
|
| 439 |
+
# instance's type to be preserved when constructing new models with its subclasses as assignments.
|
| 440 |
+
# This code is never reached by Pydantic itself, and in an ideal world this shouldn't be necessary.
|
| 441 |
+
if not metadata and isinstance(default, FieldInfo) and type(default) is not FieldInfo:
|
| 442 |
+
field_info = default._copy()
|
| 443 |
+
field_info._attributes_set.update(attr_overrides)
|
| 444 |
+
for k, v in attr_overrides.items():
|
| 445 |
+
setattr(field_info, k, v)
|
| 446 |
+
return field_info
|
| 447 |
+
|
| 448 |
+
if isinstance(default, FieldInfo):
|
| 449 |
+
default_copy = default._copy() # Copy unnecessary when we remove HACK 1.
|
| 450 |
+
prepend_metadata = default_copy.metadata
|
| 451 |
+
default_copy.metadata = []
|
| 452 |
+
metadata = metadata + [default_copy]
|
| 453 |
+
if 'init_var' in inspected_ann.qualifiers:
|
| 454 |
+
# Only relevant for dataclasses, when `f: InitVar[<type>] = Field(...)`
|
| 455 |
+
# is used:
|
| 456 |
+
attr_overrides['init_var'] = True
|
| 457 |
+
elif isinstance(default, dataclasses.Field):
|
| 458 |
+
from_field = FieldInfo._from_dataclass_field(default)
|
| 459 |
+
prepend_metadata = from_field.metadata # Unnecessary when we remove HACK 1.
|
| 460 |
+
from_field.metadata = []
|
| 461 |
+
metadata = metadata + [from_field]
|
| 462 |
+
if 'init_var' in inspected_ann.qualifiers:
|
| 463 |
+
attr_overrides['init_var'] = True
|
| 464 |
+
if (init := getattr(default, 'init', None)) is not None:
|
| 465 |
+
attr_overrides['init'] = init
|
| 466 |
+
if (kw_only := getattr(default, 'kw_only', None)) is not None:
|
| 467 |
+
attr_overrides['kw_only'] = kw_only
|
| 468 |
+
else:
|
| 469 |
+
# `default` is the actual default value
|
| 470 |
+
attr_overrides['default'] = default
|
| 471 |
+
|
| 472 |
+
field_info = FieldInfo._construct(
|
| 473 |
+
prepend_metadata + metadata if prepend_metadata is not None else metadata, **attr_overrides
|
| 474 |
+
)
|
| 475 |
+
field_info._qualifiers = inspected_ann.qualifiers
|
| 476 |
+
field_info._final = True
|
| 477 |
+
return field_info
|
| 478 |
+
|
| 479 |
+
@classmethod
|
| 480 |
+
def _construct(cls, metadata: list[Any], **attr_overrides: Any) -> Self:
|
| 481 |
+
"""Construct the final `FieldInfo` instance, by merging the possibly existing `FieldInfo` instances from the metadata.
|
| 482 |
+
|
| 483 |
+
With the following example:
|
| 484 |
+
|
| 485 |
+
```python {test="skip" lint="skip"}
|
| 486 |
+
class Model(BaseModel):
|
| 487 |
+
f: Annotated[int, Gt(1), Field(description='desc', lt=2)]
|
| 488 |
+
```
|
| 489 |
+
|
| 490 |
+
`metadata` refers to the metadata elements of the `Annotated` form. This metadata is iterated over from left to right:
|
| 491 |
+
|
| 492 |
+
- If the element is a `Field()` function (which is itself a `FieldInfo` instance), the field attributes (such as
|
| 493 |
+
`description`) are saved to be set on the final `FieldInfo` instance.
|
| 494 |
+
On the other hand, some kwargs (such as `lt`) are stored as `metadata` (see `FieldInfo.__init__()`, calling
|
| 495 |
+
`FieldInfo._collect_metadata()`). In this case, the final metadata list is extended with the one from this instance.
|
| 496 |
+
- Else, the element is considered as a single metadata object, and is appended to the final metadata list.
|
| 497 |
+
|
| 498 |
+
Args:
|
| 499 |
+
metadata: The list of metadata elements to merge together. If the `FieldInfo` instance to be constructed is for
|
| 500 |
+
a field with an assigned `Field()`, this `Field()` assignment should be added as the last element of the
|
| 501 |
+
provided metadata.
|
| 502 |
+
**attr_overrides: Extra attributes that should be set on the final merged `FieldInfo` instance.
|
| 503 |
+
|
| 504 |
+
Returns:
|
| 505 |
+
The final merged `FieldInfo` instance.
|
| 506 |
+
"""
|
| 507 |
+
merged_metadata: list[Any] = []
|
| 508 |
+
merged_kwargs: dict[str, Any] = {}
|
| 509 |
+
|
| 510 |
+
for meta in metadata:
|
| 511 |
+
if isinstance(meta, FieldInfo):
|
| 512 |
+
merged_metadata.extend(meta.metadata)
|
| 513 |
+
|
| 514 |
+
new_js_extra: JsonDict | None = None
|
| 515 |
+
current_js_extra = meta.json_schema_extra
|
| 516 |
+
if current_js_extra is not None and 'json_schema_extra' in merged_kwargs:
|
| 517 |
+
# We need to merge `json_schema_extra`'s:
|
| 518 |
+
existing_js_extra = merged_kwargs['json_schema_extra']
|
| 519 |
+
if isinstance(existing_js_extra, dict):
|
| 520 |
+
if isinstance(current_js_extra, dict):
|
| 521 |
+
new_js_extra = {
|
| 522 |
+
**existing_js_extra,
|
| 523 |
+
**current_js_extra,
|
| 524 |
+
}
|
| 525 |
+
elif callable(current_js_extra):
|
| 526 |
+
warn(
|
| 527 |
+
'Composing `dict` and `callable` type `json_schema_extra` is not supported. '
|
| 528 |
+
'The `callable` type is being ignored. '
|
| 529 |
+
"If you'd like support for this behavior, please open an issue on pydantic.",
|
| 530 |
+
UserWarning,
|
| 531 |
+
)
|
| 532 |
+
elif callable(existing_js_extra) and isinstance(current_js_extra, dict):
|
| 533 |
+
warn(
|
| 534 |
+
'Composing `dict` and `callable` type `json_schema_extra` is not supported. '
|
| 535 |
+
'The `callable` type is being ignored. '
|
| 536 |
+
"If you'd like support for this behavior, please open an issue on pydantic.",
|
| 537 |
+
UserWarning,
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
# HACK: It is common for users to define "make model partial" (or similar) utilities, that
|
| 541 |
+
# convert all model fields to be optional (i.e. have a default value). To do so, they mutate
|
| 542 |
+
# each `FieldInfo` instance from `model_fields` to set a `default`, and use `create_model()`
|
| 543 |
+
# with `Annotated[<orig_type> | None, mutated_field_info]`` as an annotation. However, such
|
| 544 |
+
# mutations (by doing simple assignments) are only accidentally working, because we also
|
| 545 |
+
# need to track attributes explicitly set in `_attributes_set` (relying on default values for
|
| 546 |
+
# each attribute is *not* enough, for instance with `Annotated[int, Field(alias='a'), Field(alias=None)]`
|
| 547 |
+
# the resulting `FieldInfo` should have `alias=None`).
|
| 548 |
+
# To mitigate this, we add a special case when a "final" `FieldInfo` instance (that is an instance coming
|
| 549 |
+
# from `model_fields`) is used in annotated metadata (or assignment). In this case, we assume *all* attributes
|
| 550 |
+
# were explicitly set, and as such we use all of them (and this will correctly pick up the mutations).
|
| 551 |
+
# In theory, this shouldn't really be supported, you are only supposed to use the `Field()` function, not
|
| 552 |
+
# a `FieldInfo` instance directly (granted, `Field()` returns a `FieldInfo`, see
|
| 553 |
+
# https://github.com/pydantic/pydantic/issues/11122):
|
| 554 |
+
if meta._final:
|
| 555 |
+
merged_kwargs.update({attr: getattr(meta, attr) for attr in _Attrs})
|
| 556 |
+
else:
|
| 557 |
+
merged_kwargs.update(meta._attributes_set)
|
| 558 |
+
|
| 559 |
+
if new_js_extra is not None:
|
| 560 |
+
merged_kwargs['json_schema_extra'] = new_js_extra
|
| 561 |
+
elif typing_objects.is_deprecated(meta):
|
| 562 |
+
merged_kwargs['deprecated'] = meta
|
| 563 |
+
else:
|
| 564 |
+
merged_metadata.append(meta)
|
| 565 |
+
|
| 566 |
+
merged_kwargs.update(attr_overrides)
|
| 567 |
+
merged_field_info = cls(**merged_kwargs)
|
| 568 |
+
merged_field_info.metadata = merged_metadata
|
| 569 |
+
return merged_field_info
|
| 570 |
+
|
| 571 |
+
@staticmethod
|
| 572 |
+
@typing_extensions.deprecated(
|
| 573 |
+
"The 'merge_field_infos()' method is deprecated and will be removed in a future version. "
|
| 574 |
+
'If you relied on this method, please open an issue in the Pydantic issue tracker.',
|
| 575 |
+
category=None,
|
| 576 |
+
)
|
| 577 |
+
def merge_field_infos(*field_infos: FieldInfo, **overrides: Any) -> FieldInfo:
|
| 578 |
+
"""Merge `FieldInfo` instances keeping only explicitly set attributes.
|
| 579 |
+
|
| 580 |
+
Later `FieldInfo` instances override earlier ones.
|
| 581 |
+
|
| 582 |
+
Returns:
|
| 583 |
+
FieldInfo: A merged FieldInfo instance.
|
| 584 |
+
"""
|
| 585 |
+
if len(field_infos) == 1:
|
| 586 |
+
# No merging necessary, but we still need to make a copy and apply the overrides
|
| 587 |
+
field_info = field_infos[0]._copy()
|
| 588 |
+
field_info._attributes_set.update(overrides)
|
| 589 |
+
|
| 590 |
+
default_override = overrides.pop('default', PydanticUndefined)
|
| 591 |
+
if default_override is Ellipsis:
|
| 592 |
+
default_override = PydanticUndefined
|
| 593 |
+
if default_override is not PydanticUndefined:
|
| 594 |
+
field_info.default = default_override
|
| 595 |
+
|
| 596 |
+
for k, v in overrides.items():
|
| 597 |
+
setattr(field_info, k, v)
|
| 598 |
+
return field_info # type: ignore
|
| 599 |
+
|
| 600 |
+
merged_field_info_kwargs: dict[str, Any] = {}
|
| 601 |
+
metadata = {}
|
| 602 |
+
for field_info in field_infos:
|
| 603 |
+
attributes_set = field_info._attributes_set.copy()
|
| 604 |
+
|
| 605 |
+
try:
|
| 606 |
+
json_schema_extra = attributes_set.pop('json_schema_extra')
|
| 607 |
+
existing_json_schema_extra = merged_field_info_kwargs.get('json_schema_extra')
|
| 608 |
+
|
| 609 |
+
if existing_json_schema_extra is None:
|
| 610 |
+
merged_field_info_kwargs['json_schema_extra'] = json_schema_extra
|
| 611 |
+
if isinstance(existing_json_schema_extra, dict):
|
| 612 |
+
if isinstance(json_schema_extra, dict):
|
| 613 |
+
merged_field_info_kwargs['json_schema_extra'] = {
|
| 614 |
+
**existing_json_schema_extra,
|
| 615 |
+
**json_schema_extra,
|
| 616 |
+
}
|
| 617 |
+
if callable(json_schema_extra):
|
| 618 |
+
warn(
|
| 619 |
+
'Composing `dict` and `callable` type `json_schema_extra` is not supported.'
|
| 620 |
+
'The `callable` type is being ignored.'
|
| 621 |
+
"If you'd like support for this behavior, please open an issue on pydantic.",
|
| 622 |
+
PydanticJsonSchemaWarning,
|
| 623 |
+
)
|
| 624 |
+
elif callable(json_schema_extra):
|
| 625 |
+
# if ever there's a case of a callable, we'll just keep the last json schema extra spec
|
| 626 |
+
merged_field_info_kwargs['json_schema_extra'] = json_schema_extra
|
| 627 |
+
except KeyError:
|
| 628 |
+
pass
|
| 629 |
+
|
| 630 |
+
# later FieldInfo instances override everything except json_schema_extra from earlier FieldInfo instances
|
| 631 |
+
merged_field_info_kwargs.update(attributes_set)
|
| 632 |
+
|
| 633 |
+
for x in field_info.metadata:
|
| 634 |
+
if not isinstance(x, FieldInfo):
|
| 635 |
+
metadata[type(x)] = x
|
| 636 |
+
|
| 637 |
+
merged_field_info_kwargs.update(overrides)
|
| 638 |
+
field_info = FieldInfo(**merged_field_info_kwargs)
|
| 639 |
+
field_info.metadata = list(metadata.values())
|
| 640 |
+
return field_info
|
| 641 |
+
|
| 642 |
+
@staticmethod
|
| 643 |
+
def _from_dataclass_field(dc_field: DataclassField[Any]) -> FieldInfo:
|
| 644 |
+
"""Return a new `FieldInfo` instance from a `dataclasses.Field` instance.
|
| 645 |
+
|
| 646 |
+
Args:
|
| 647 |
+
dc_field: The `dataclasses.Field` instance to convert.
|
| 648 |
+
|
| 649 |
+
Returns:
|
| 650 |
+
The corresponding `FieldInfo` instance.
|
| 651 |
+
|
| 652 |
+
Raises:
|
| 653 |
+
TypeError: If any of the `FieldInfo` kwargs does not match the `dataclass.Field` kwargs.
|
| 654 |
+
"""
|
| 655 |
+
default = dc_field.default
|
| 656 |
+
if default is dataclasses.MISSING:
|
| 657 |
+
default = _Unset
|
| 658 |
+
|
| 659 |
+
if dc_field.default_factory is dataclasses.MISSING:
|
| 660 |
+
default_factory = _Unset
|
| 661 |
+
else:
|
| 662 |
+
default_factory = dc_field.default_factory
|
| 663 |
+
|
| 664 |
+
# use the `Field` function so in correct kwargs raise the correct `TypeError`
|
| 665 |
+
dc_field_metadata = {k: v for k, v in dc_field.metadata.items() if k in _FIELD_ARG_NAMES}
|
| 666 |
+
if sys.version_info >= (3, 14) and dc_field.doc is not None:
|
| 667 |
+
dc_field_metadata['description'] = dc_field.doc
|
| 668 |
+
return Field(default=default, default_factory=default_factory, repr=dc_field.repr, **dc_field_metadata) # pyright: ignore[reportCallIssue]
|
| 669 |
+
|
| 670 |
+
@staticmethod
|
| 671 |
+
def _collect_metadata(kwargs: dict[str, Any]) -> list[Any]:
|
| 672 |
+
"""Collect annotations from kwargs.
|
| 673 |
+
|
| 674 |
+
Args:
|
| 675 |
+
kwargs: Keyword arguments passed to the function.
|
| 676 |
+
|
| 677 |
+
Returns:
|
| 678 |
+
A list of metadata objects - a combination of `annotated_types.BaseMetadata` and
|
| 679 |
+
`PydanticMetadata`.
|
| 680 |
+
"""
|
| 681 |
+
metadata: list[Any] = []
|
| 682 |
+
general_metadata = {}
|
| 683 |
+
for key, value in list(kwargs.items()):
|
| 684 |
+
try:
|
| 685 |
+
marker = FieldInfo.metadata_lookup[key]
|
| 686 |
+
except KeyError:
|
| 687 |
+
continue
|
| 688 |
+
|
| 689 |
+
del kwargs[key]
|
| 690 |
+
if value is not None:
|
| 691 |
+
if marker is None:
|
| 692 |
+
general_metadata[key] = value
|
| 693 |
+
else:
|
| 694 |
+
metadata.append(marker(value))
|
| 695 |
+
if general_metadata:
|
| 696 |
+
metadata.append(_fields.pydantic_general_metadata(**general_metadata))
|
| 697 |
+
return metadata
|
| 698 |
+
|
| 699 |
+
@property
|
| 700 |
+
def deprecation_message(self) -> str | None:
|
| 701 |
+
"""The deprecation message to be emitted, or `None` if not set."""
|
| 702 |
+
if self.deprecated is None:
|
| 703 |
+
return None
|
| 704 |
+
if isinstance(self.deprecated, bool):
|
| 705 |
+
return 'deprecated' if self.deprecated else None
|
| 706 |
+
return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message
|
| 707 |
+
|
| 708 |
+
@property
|
| 709 |
+
def default_factory_takes_validated_data(self) -> bool | None:
|
| 710 |
+
"""Whether the provided default factory callable has a validated data parameter.
|
| 711 |
+
|
| 712 |
+
Returns `None` if no default factory is set.
|
| 713 |
+
"""
|
| 714 |
+
if self.default_factory is not None:
|
| 715 |
+
return _fields.takes_validated_data_argument(self.default_factory)
|
| 716 |
+
|
| 717 |
+
@overload
|
| 718 |
+
def get_default(
|
| 719 |
+
self, *, call_default_factory: Literal[True], validated_data: dict[str, Any] | None = None
|
| 720 |
+
) -> Any: ...
|
| 721 |
+
|
| 722 |
+
@overload
|
| 723 |
+
def get_default(self, *, call_default_factory: Literal[False] = ...) -> Any: ...
|
| 724 |
+
|
| 725 |
+
def get_default(self, *, call_default_factory: bool = False, validated_data: dict[str, Any] | None = None) -> Any:
|
| 726 |
+
"""Get the default value.
|
| 727 |
+
|
| 728 |
+
We expose an option for whether to call the default_factory (if present), as calling it may
|
| 729 |
+
result in side effects that we want to avoid. However, there are times when it really should
|
| 730 |
+
be called (namely, when instantiating a model via `model_construct`).
|
| 731 |
+
|
| 732 |
+
Args:
|
| 733 |
+
call_default_factory: Whether to call the default factory or not.
|
| 734 |
+
validated_data: The already validated data to be passed to the default factory.
|
| 735 |
+
|
| 736 |
+
Returns:
|
| 737 |
+
The default value, calling the default factory if requested or `PydanticUndefined` if not set.
|
| 738 |
+
"""
|
| 739 |
+
return _fields.resolve_default_value(
|
| 740 |
+
default=self.default,
|
| 741 |
+
default_factory=self.default_factory,
|
| 742 |
+
validated_data=validated_data,
|
| 743 |
+
call_default_factory=call_default_factory,
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
def is_required(self) -> bool:
|
| 747 |
+
"""Check if the field is required (i.e., does not have a default value or factory).
|
| 748 |
+
|
| 749 |
+
Returns:
|
| 750 |
+
`True` if the field is required, `False` otherwise.
|
| 751 |
+
"""
|
| 752 |
+
return self.default is PydanticUndefined and self.default_factory is None
|
| 753 |
+
|
| 754 |
+
def rebuild_annotation(self) -> Any:
|
| 755 |
+
"""Attempts to rebuild the original annotation for use in function signatures.
|
| 756 |
+
|
| 757 |
+
If metadata is present, it adds it to the original annotation using
|
| 758 |
+
`Annotated`. Otherwise, it returns the original annotation as-is.
|
| 759 |
+
|
| 760 |
+
Note that because the metadata has been flattened, the original annotation
|
| 761 |
+
may not be reconstructed exactly as originally provided, e.g. if the original
|
| 762 |
+
type had unrecognized annotations, or was annotated with a call to `pydantic.Field`.
|
| 763 |
+
|
| 764 |
+
Returns:
|
| 765 |
+
The rebuilt annotation.
|
| 766 |
+
"""
|
| 767 |
+
if not self.metadata:
|
| 768 |
+
return self.annotation
|
| 769 |
+
else:
|
| 770 |
+
# Annotated arguments must be a tuple
|
| 771 |
+
return Annotated[(self.annotation, *self.metadata)] # type: ignore
|
| 772 |
+
|
| 773 |
+
def apply_typevars_map(
|
| 774 |
+
self,
|
| 775 |
+
typevars_map: Mapping[TypeVar, Any] | None,
|
| 776 |
+
globalns: GlobalsNamespace | None = None,
|
| 777 |
+
localns: MappingNamespace | None = None,
|
| 778 |
+
) -> None:
|
| 779 |
+
"""Apply a `typevars_map` to the annotation.
|
| 780 |
+
|
| 781 |
+
This method is used when analyzing parametrized generic types to replace typevars with their concrete types.
|
| 782 |
+
|
| 783 |
+
This method applies the `typevars_map` to the annotation in place.
|
| 784 |
+
|
| 785 |
+
Args:
|
| 786 |
+
typevars_map: A dictionary mapping type variables to their concrete types.
|
| 787 |
+
globalns: The globals namespace to use during type annotation evaluation.
|
| 788 |
+
localns: The locals namespace to use during type annotation evaluation.
|
| 789 |
+
|
| 790 |
+
See Also:
|
| 791 |
+
pydantic._internal._generics.replace_types is used for replacing the typevars with
|
| 792 |
+
their concrete types.
|
| 793 |
+
"""
|
| 794 |
+
annotation = _generics.replace_types(self.annotation, typevars_map)
|
| 795 |
+
annotation, evaluated = _typing_extra.try_eval_type(annotation, globalns, localns)
|
| 796 |
+
self.annotation = annotation
|
| 797 |
+
if not evaluated:
|
| 798 |
+
self._complete = False
|
| 799 |
+
self._original_annotation = self.annotation
|
| 800 |
+
|
| 801 |
+
def asdict(self) -> _FieldInfoAsDict:
|
| 802 |
+
"""Return a dictionary representation of the `FieldInfo` instance.
|
| 803 |
+
|
| 804 |
+
The returned value is a dictionary with three items:
|
| 805 |
+
|
| 806 |
+
* `annotation`: The type annotation of the field.
|
| 807 |
+
* `metadata`: The metadata list.
|
| 808 |
+
* `attributes`: A mapping of the remaining `FieldInfo` attributes to their values (e.g. `alias`, `title`).
|
| 809 |
+
"""
|
| 810 |
+
return {
|
| 811 |
+
'annotation': self.annotation,
|
| 812 |
+
'metadata': self.metadata,
|
| 813 |
+
'attributes': {attr: getattr(self, attr) for attr in _Attrs},
|
| 814 |
+
}
|
| 815 |
+
|
| 816 |
+
def _copy(self) -> Self:
|
| 817 |
+
"""Return a copy of the `FieldInfo` instance."""
|
| 818 |
+
# Note: we can't define a custom `__copy__()`, as `FieldInfo` is being subclassed
|
| 819 |
+
# by some third-party libraries with extra attributes defined (and as `FieldInfo`
|
| 820 |
+
# is slotted, we can't make a copy of the `__dict__`).
|
| 821 |
+
if type(self) is FieldInfo:
|
| 822 |
+
# Fast-path if the instance isn't a subclass (`copy.copy()` relies on pickling which is slower):
|
| 823 |
+
copied = FieldInfo.__new__(FieldInfo)
|
| 824 |
+
for attr_name in FieldInfo.__slots__:
|
| 825 |
+
setattr(copied, attr_name, getattr(self, attr_name))
|
| 826 |
+
else:
|
| 827 |
+
copied = copy(self)
|
| 828 |
+
|
| 829 |
+
for attr_name in ('metadata', '_attributes_set', '_qualifiers'):
|
| 830 |
+
# Apply "deep-copy" behavior on collections attributes:
|
| 831 |
+
setattr(copied, attr_name, getattr(copied, attr_name).copy())
|
| 832 |
+
|
| 833 |
+
return copied # pyright: ignore[reportReturnType]
|
| 834 |
+
|
| 835 |
+
def __repr_args__(self) -> ReprArgs:
|
| 836 |
+
yield 'annotation', _repr.PlainRepr(_repr.display_as_type(self.annotation))
|
| 837 |
+
yield 'required', self.is_required()
|
| 838 |
+
|
| 839 |
+
for s in self.__slots__:
|
| 840 |
+
# TODO: properly make use of the protocol (https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol)
|
| 841 |
+
# By yielding a three-tuple:
|
| 842 |
+
if s in (
|
| 843 |
+
'annotation',
|
| 844 |
+
'_attributes_set',
|
| 845 |
+
'_qualifiers',
|
| 846 |
+
'_complete',
|
| 847 |
+
'_original_assignment',
|
| 848 |
+
'_original_annotation',
|
| 849 |
+
'_final',
|
| 850 |
+
):
|
| 851 |
+
continue
|
| 852 |
+
elif s == 'metadata' and not self.metadata:
|
| 853 |
+
continue
|
| 854 |
+
elif s == 'repr' and self.repr is True:
|
| 855 |
+
continue
|
| 856 |
+
if s == 'frozen' and self.frozen is False:
|
| 857 |
+
continue
|
| 858 |
+
if s == 'validation_alias' and self.validation_alias == self.alias:
|
| 859 |
+
continue
|
| 860 |
+
if s == 'serialization_alias' and self.serialization_alias == self.alias:
|
| 861 |
+
continue
|
| 862 |
+
if s == 'default' and self.default is not PydanticUndefined:
|
| 863 |
+
yield 'default', self.default
|
| 864 |
+
elif s == 'default_factory' and self.default_factory is not None:
|
| 865 |
+
yield 'default_factory', _repr.PlainRepr(_repr.display_as_type(self.default_factory))
|
| 866 |
+
else:
|
| 867 |
+
value = getattr(self, s)
|
| 868 |
+
if value is not None and value is not PydanticUndefined:
|
| 869 |
+
yield s, value
|
| 870 |
+
|
| 871 |
+
|
| 872 |
+
class _EmptyKwargs(TypedDict):
|
| 873 |
+
"""This class exists solely to ensure that type checking warns about passing `**extra` in `Field`."""
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
_Attrs = {
|
| 877 |
+
'default': ...,
|
| 878 |
+
'default_factory': None,
|
| 879 |
+
'alias': None,
|
| 880 |
+
'alias_priority': None,
|
| 881 |
+
'validation_alias': None,
|
| 882 |
+
'serialization_alias': None,
|
| 883 |
+
'title': None,
|
| 884 |
+
'field_title_generator': None,
|
| 885 |
+
'description': None,
|
| 886 |
+
'examples': None,
|
| 887 |
+
'exclude': None,
|
| 888 |
+
'exclude_if': None,
|
| 889 |
+
'discriminator': None,
|
| 890 |
+
'deprecated': None,
|
| 891 |
+
'json_schema_extra': None,
|
| 892 |
+
'frozen': None,
|
| 893 |
+
'validate_default': None,
|
| 894 |
+
'repr': True,
|
| 895 |
+
'init': None,
|
| 896 |
+
'init_var': None,
|
| 897 |
+
'kw_only': None,
|
| 898 |
+
}
|
| 899 |
+
|
| 900 |
+
_DefaultValues = {
|
| 901 |
+
**_Attrs,
|
| 902 |
+
'kw_only': None,
|
| 903 |
+
'pattern': None,
|
| 904 |
+
'strict': None,
|
| 905 |
+
'gt': None,
|
| 906 |
+
'ge': None,
|
| 907 |
+
'lt': None,
|
| 908 |
+
'le': None,
|
| 909 |
+
'multiple_of': None,
|
| 910 |
+
'allow_inf_nan': None,
|
| 911 |
+
'max_digits': None,
|
| 912 |
+
'decimal_places': None,
|
| 913 |
+
'min_length': None,
|
| 914 |
+
'max_length': None,
|
| 915 |
+
'coerce_numbers_to_str': None,
|
| 916 |
+
}
|
| 917 |
+
|
| 918 |
+
|
| 919 |
+
_T = TypeVar('_T')
|
| 920 |
+
|
| 921 |
+
|
| 922 |
+
# NOTE: Actual return type is 'FieldInfo', but we want to help type checkers
|
| 923 |
+
# to understand the magic that happens at runtime with the following overloads:
|
| 924 |
+
@overload # type hint the return value as `Any` to avoid type checking regressions when using `...`.
|
| 925 |
+
def Field(
|
| 926 |
+
default: ellipsis, # noqa: F821 # TODO: use `_typing_extra.EllipsisType` when we drop Py3.9
|
| 927 |
+
*,
|
| 928 |
+
alias: str | None = _Unset,
|
| 929 |
+
alias_priority: int | None = _Unset,
|
| 930 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 931 |
+
serialization_alias: str | None = _Unset,
|
| 932 |
+
title: str | None = _Unset,
|
| 933 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 934 |
+
description: str | None = _Unset,
|
| 935 |
+
examples: list[Any] | None = _Unset,
|
| 936 |
+
exclude: bool | None = _Unset,
|
| 937 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 938 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 939 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 940 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 941 |
+
frozen: bool | None = _Unset,
|
| 942 |
+
validate_default: bool | None = _Unset,
|
| 943 |
+
repr: bool = _Unset,
|
| 944 |
+
init: bool | None = _Unset,
|
| 945 |
+
init_var: bool | None = _Unset,
|
| 946 |
+
kw_only: bool | None = _Unset,
|
| 947 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 948 |
+
strict: bool | None = _Unset,
|
| 949 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 950 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 951 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 952 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 953 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 954 |
+
multiple_of: float | None = _Unset,
|
| 955 |
+
allow_inf_nan: bool | None = _Unset,
|
| 956 |
+
max_digits: int | None = _Unset,
|
| 957 |
+
decimal_places: int | None = _Unset,
|
| 958 |
+
min_length: int | None = _Unset,
|
| 959 |
+
max_length: int | None = _Unset,
|
| 960 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 961 |
+
fail_fast: bool | None = _Unset,
|
| 962 |
+
**extra: Unpack[_EmptyKwargs],
|
| 963 |
+
) -> Any: ...
|
| 964 |
+
@overload # `default` argument set, validate_default=True (no type checking on the default value)
|
| 965 |
+
def Field(
|
| 966 |
+
default: Any,
|
| 967 |
+
*,
|
| 968 |
+
alias: str | None = _Unset,
|
| 969 |
+
alias_priority: int | None = _Unset,
|
| 970 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 971 |
+
serialization_alias: str | None = _Unset,
|
| 972 |
+
title: str | None = _Unset,
|
| 973 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 974 |
+
description: str | None = _Unset,
|
| 975 |
+
examples: list[Any] | None = _Unset,
|
| 976 |
+
exclude: bool | None = _Unset,
|
| 977 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 978 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 979 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 980 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 981 |
+
frozen: bool | None = _Unset,
|
| 982 |
+
validate_default: Literal[True],
|
| 983 |
+
repr: bool = _Unset,
|
| 984 |
+
init: bool | None = _Unset,
|
| 985 |
+
init_var: bool | None = _Unset,
|
| 986 |
+
kw_only: bool | None = _Unset,
|
| 987 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 988 |
+
strict: bool | None = _Unset,
|
| 989 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 990 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 991 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 992 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 993 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 994 |
+
multiple_of: float | None = _Unset,
|
| 995 |
+
allow_inf_nan: bool | None = _Unset,
|
| 996 |
+
max_digits: int | None = _Unset,
|
| 997 |
+
decimal_places: int | None = _Unset,
|
| 998 |
+
min_length: int | None = _Unset,
|
| 999 |
+
max_length: int | None = _Unset,
|
| 1000 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 1001 |
+
fail_fast: bool | None = _Unset,
|
| 1002 |
+
**extra: Unpack[_EmptyKwargs],
|
| 1003 |
+
) -> Any: ...
|
| 1004 |
+
@overload # `default` argument set, validate_default=False or unset
|
| 1005 |
+
def Field(
|
| 1006 |
+
default: _T,
|
| 1007 |
+
*,
|
| 1008 |
+
alias: str | None = _Unset,
|
| 1009 |
+
alias_priority: int | None = _Unset,
|
| 1010 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 1011 |
+
serialization_alias: str | None = _Unset,
|
| 1012 |
+
title: str | None = _Unset,
|
| 1013 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 1014 |
+
description: str | None = _Unset,
|
| 1015 |
+
examples: list[Any] | None = _Unset,
|
| 1016 |
+
exclude: bool | None = _Unset,
|
| 1017 |
+
# NOTE: to get proper type checking on `exclude_if`'s argument, we could use `_T` instead of `Any`. However,
|
| 1018 |
+
# this requires (at least for pyright) adding an additional overload where `exclude_if` is required (otherwise
|
| 1019 |
+
# `a: int = Field(default_factory=str)` results in a false negative).
|
| 1020 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 1021 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 1022 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 1023 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 1024 |
+
frozen: bool | None = _Unset,
|
| 1025 |
+
validate_default: Literal[False] = ...,
|
| 1026 |
+
repr: bool = _Unset,
|
| 1027 |
+
init: bool | None = _Unset,
|
| 1028 |
+
init_var: bool | None = _Unset,
|
| 1029 |
+
kw_only: bool | None = _Unset,
|
| 1030 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 1031 |
+
strict: bool | None = _Unset,
|
| 1032 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 1033 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 1034 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 1035 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 1036 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 1037 |
+
multiple_of: float | None = _Unset,
|
| 1038 |
+
allow_inf_nan: bool | None = _Unset,
|
| 1039 |
+
max_digits: int | None = _Unset,
|
| 1040 |
+
decimal_places: int | None = _Unset,
|
| 1041 |
+
min_length: int | None = _Unset,
|
| 1042 |
+
max_length: int | None = _Unset,
|
| 1043 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 1044 |
+
fail_fast: bool | None = _Unset,
|
| 1045 |
+
**extra: Unpack[_EmptyKwargs],
|
| 1046 |
+
) -> _T: ...
|
| 1047 |
+
@overload # `default_factory` argument set, validate_default=True (no type checking on the default value)
|
| 1048 |
+
def Field( # pyright: ignore[reportOverlappingOverload]
|
| 1049 |
+
*,
|
| 1050 |
+
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
|
| 1051 |
+
alias: str | None = _Unset,
|
| 1052 |
+
alias_priority: int | None = _Unset,
|
| 1053 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 1054 |
+
serialization_alias: str | None = _Unset,
|
| 1055 |
+
title: str | None = _Unset,
|
| 1056 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 1057 |
+
description: str | None = _Unset,
|
| 1058 |
+
examples: list[Any] | None = _Unset,
|
| 1059 |
+
exclude: bool | None = _Unset,
|
| 1060 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 1061 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 1062 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 1063 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 1064 |
+
frozen: bool | None = _Unset,
|
| 1065 |
+
validate_default: Literal[True],
|
| 1066 |
+
repr: bool = _Unset,
|
| 1067 |
+
init: bool | None = _Unset,
|
| 1068 |
+
init_var: bool | None = _Unset,
|
| 1069 |
+
kw_only: bool | None = _Unset,
|
| 1070 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 1071 |
+
strict: bool | None = _Unset,
|
| 1072 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 1073 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 1074 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 1075 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 1076 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 1077 |
+
multiple_of: float | None = _Unset,
|
| 1078 |
+
allow_inf_nan: bool | None = _Unset,
|
| 1079 |
+
max_digits: int | None = _Unset,
|
| 1080 |
+
decimal_places: int | None = _Unset,
|
| 1081 |
+
min_length: int | None = _Unset,
|
| 1082 |
+
max_length: int | None = _Unset,
|
| 1083 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 1084 |
+
fail_fast: bool | None = _Unset,
|
| 1085 |
+
**extra: Unpack[_EmptyKwargs],
|
| 1086 |
+
) -> Any: ...
|
| 1087 |
+
@overload # `default_factory` argument set, validate_default=False or unset
|
| 1088 |
+
def Field(
|
| 1089 |
+
*,
|
| 1090 |
+
default_factory: Callable[[], _T] | Callable[[dict[str, Any]], _T],
|
| 1091 |
+
alias: str | None = _Unset,
|
| 1092 |
+
alias_priority: int | None = _Unset,
|
| 1093 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 1094 |
+
serialization_alias: str | None = _Unset,
|
| 1095 |
+
title: str | None = _Unset,
|
| 1096 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 1097 |
+
description: str | None = _Unset,
|
| 1098 |
+
examples: list[Any] | None = _Unset,
|
| 1099 |
+
exclude: bool | None = _Unset,
|
| 1100 |
+
# NOTE: to get proper type checking on `exclude_if`'s argument, we could use `_T` instead of `Any`. However,
|
| 1101 |
+
# this requires (at least for pyright) adding an additional overload where `exclude_if` is required (otherwise
|
| 1102 |
+
# `a: int = Field(default_factory=str)` results in a false negative).
|
| 1103 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 1104 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 1105 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 1106 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 1107 |
+
frozen: bool | None = _Unset,
|
| 1108 |
+
validate_default: Literal[False] | None = _Unset,
|
| 1109 |
+
repr: bool = _Unset,
|
| 1110 |
+
init: bool | None = _Unset,
|
| 1111 |
+
init_var: bool | None = _Unset,
|
| 1112 |
+
kw_only: bool | None = _Unset,
|
| 1113 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 1114 |
+
strict: bool | None = _Unset,
|
| 1115 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 1116 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 1117 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 1118 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 1119 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 1120 |
+
multiple_of: float | None = _Unset,
|
| 1121 |
+
allow_inf_nan: bool | None = _Unset,
|
| 1122 |
+
max_digits: int | None = _Unset,
|
| 1123 |
+
decimal_places: int | None = _Unset,
|
| 1124 |
+
min_length: int | None = _Unset,
|
| 1125 |
+
max_length: int | None = _Unset,
|
| 1126 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 1127 |
+
fail_fast: bool | None = _Unset,
|
| 1128 |
+
**extra: Unpack[_EmptyKwargs],
|
| 1129 |
+
) -> _T: ...
|
| 1130 |
+
@overload
|
| 1131 |
+
def Field( # No default set
|
| 1132 |
+
*,
|
| 1133 |
+
alias: str | None = _Unset,
|
| 1134 |
+
alias_priority: int | None = _Unset,
|
| 1135 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 1136 |
+
serialization_alias: str | None = _Unset,
|
| 1137 |
+
title: str | None = _Unset,
|
| 1138 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 1139 |
+
description: str | None = _Unset,
|
| 1140 |
+
examples: list[Any] | None = _Unset,
|
| 1141 |
+
exclude: bool | None = _Unset,
|
| 1142 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 1143 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 1144 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 1145 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 1146 |
+
frozen: bool | None = _Unset,
|
| 1147 |
+
validate_default: bool | None = _Unset,
|
| 1148 |
+
repr: bool = _Unset,
|
| 1149 |
+
init: bool | None = _Unset,
|
| 1150 |
+
init_var: bool | None = _Unset,
|
| 1151 |
+
kw_only: bool | None = _Unset,
|
| 1152 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 1153 |
+
strict: bool | None = _Unset,
|
| 1154 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 1155 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 1156 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 1157 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 1158 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 1159 |
+
multiple_of: float | None = _Unset,
|
| 1160 |
+
allow_inf_nan: bool | None = _Unset,
|
| 1161 |
+
max_digits: int | None = _Unset,
|
| 1162 |
+
decimal_places: int | None = _Unset,
|
| 1163 |
+
min_length: int | None = _Unset,
|
| 1164 |
+
max_length: int | None = _Unset,
|
| 1165 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 1166 |
+
fail_fast: bool | None = _Unset,
|
| 1167 |
+
**extra: Unpack[_EmptyKwargs],
|
| 1168 |
+
) -> Any: ...
|
| 1169 |
+
def Field( # noqa: C901
|
| 1170 |
+
default: Any = PydanticUndefined,
|
| 1171 |
+
*,
|
| 1172 |
+
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = _Unset,
|
| 1173 |
+
alias: str | None = _Unset,
|
| 1174 |
+
alias_priority: int | None = _Unset,
|
| 1175 |
+
validation_alias: str | AliasPath | AliasChoices | None = _Unset,
|
| 1176 |
+
serialization_alias: str | None = _Unset,
|
| 1177 |
+
title: str | None = _Unset,
|
| 1178 |
+
field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
|
| 1179 |
+
description: str | None = _Unset,
|
| 1180 |
+
examples: list[Any] | None = _Unset,
|
| 1181 |
+
exclude: bool | None = _Unset,
|
| 1182 |
+
exclude_if: Callable[[Any], bool] | None = _Unset,
|
| 1183 |
+
discriminator: str | types.Discriminator | None = _Unset,
|
| 1184 |
+
deprecated: Deprecated | str | bool | None = _Unset,
|
| 1185 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
|
| 1186 |
+
frozen: bool | None = _Unset,
|
| 1187 |
+
validate_default: bool | None = _Unset,
|
| 1188 |
+
repr: bool = _Unset,
|
| 1189 |
+
init: bool | None = _Unset,
|
| 1190 |
+
init_var: bool | None = _Unset,
|
| 1191 |
+
kw_only: bool | None = _Unset,
|
| 1192 |
+
pattern: str | re.Pattern[str] | None = _Unset,
|
| 1193 |
+
strict: bool | None = _Unset,
|
| 1194 |
+
coerce_numbers_to_str: bool | None = _Unset,
|
| 1195 |
+
gt: annotated_types.SupportsGt | None = _Unset,
|
| 1196 |
+
ge: annotated_types.SupportsGe | None = _Unset,
|
| 1197 |
+
lt: annotated_types.SupportsLt | None = _Unset,
|
| 1198 |
+
le: annotated_types.SupportsLe | None = _Unset,
|
| 1199 |
+
multiple_of: float | None = _Unset,
|
| 1200 |
+
allow_inf_nan: bool | None = _Unset,
|
| 1201 |
+
max_digits: int | None = _Unset,
|
| 1202 |
+
decimal_places: int | None = _Unset,
|
| 1203 |
+
min_length: int | None = _Unset,
|
| 1204 |
+
max_length: int | None = _Unset,
|
| 1205 |
+
union_mode: Literal['smart', 'left_to_right'] = _Unset,
|
| 1206 |
+
fail_fast: bool | None = _Unset,
|
| 1207 |
+
**extra: Unpack[_EmptyKwargs],
|
| 1208 |
+
) -> Any:
|
| 1209 |
+
"""!!! abstract "Usage Documentation"
|
| 1210 |
+
[Fields](../concepts/fields.md)
|
| 1211 |
+
|
| 1212 |
+
Create a field for objects that can be configured.
|
| 1213 |
+
|
| 1214 |
+
Used to provide extra information about a field, either for the model schema or complex validation. Some arguments
|
| 1215 |
+
apply only to number fields (`int`, `float`, `Decimal`) and some apply only to `str`.
|
| 1216 |
+
|
| 1217 |
+
Note:
|
| 1218 |
+
- Any `_Unset` objects will be replaced by the corresponding value defined in the `_DefaultValues` dictionary. If a key for the `_Unset` object is not found in the `_DefaultValues` dictionary, it will default to `None`
|
| 1219 |
+
|
| 1220 |
+
Args:
|
| 1221 |
+
default: Default value if the field is not set.
|
| 1222 |
+
default_factory: A callable to generate the default value. The callable can either take 0 arguments
|
| 1223 |
+
(in which case it is called as is) or a single argument containing the already validated data.
|
| 1224 |
+
alias: The name to use for the attribute when validating or serializing by alias.
|
| 1225 |
+
This is often used for things like converting between snake and camel case.
|
| 1226 |
+
alias_priority: Priority of the alias. This affects whether an alias generator is used.
|
| 1227 |
+
validation_alias: Like `alias`, but only affects validation, not serialization.
|
| 1228 |
+
serialization_alias: Like `alias`, but only affects serialization, not validation.
|
| 1229 |
+
title: Human-readable title.
|
| 1230 |
+
field_title_generator: A callable that takes a field name and returns title for it.
|
| 1231 |
+
description: Human-readable description.
|
| 1232 |
+
examples: Example values for this field.
|
| 1233 |
+
exclude: Whether to exclude the field from the model serialization.
|
| 1234 |
+
exclude_if: A callable that determines whether to exclude a field during serialization based on its value.
|
| 1235 |
+
discriminator: Field name or Discriminator for discriminating the type in a tagged union.
|
| 1236 |
+
deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport,
|
| 1237 |
+
or a boolean. If `True`, a default deprecation message will be emitted when accessing the field.
|
| 1238 |
+
json_schema_extra: A dict or callable to provide extra JSON schema properties.
|
| 1239 |
+
frozen: Whether the field is frozen. If true, attempts to change the value on an instance will raise an error.
|
| 1240 |
+
validate_default: If `True`, apply validation to the default value every time you create an instance.
|
| 1241 |
+
Otherwise, for performance reasons, the default value of the field is trusted and not validated.
|
| 1242 |
+
repr: A boolean indicating whether to include the field in the `__repr__` output.
|
| 1243 |
+
init: Whether the field should be included in the constructor of the dataclass.
|
| 1244 |
+
(Only applies to dataclasses.)
|
| 1245 |
+
init_var: Whether the field should _only_ be included in the constructor of the dataclass.
|
| 1246 |
+
(Only applies to dataclasses.)
|
| 1247 |
+
kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass.
|
| 1248 |
+
(Only applies to dataclasses.)
|
| 1249 |
+
coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode).
|
| 1250 |
+
strict: If `True`, strict validation is applied to the field.
|
| 1251 |
+
See [Strict Mode](../concepts/strict_mode.md) for details.
|
| 1252 |
+
gt: Greater than. If set, value must be greater than this. Only applicable to numbers.
|
| 1253 |
+
ge: Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers.
|
| 1254 |
+
lt: Less than. If set, value must be less than this. Only applicable to numbers.
|
| 1255 |
+
le: Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers.
|
| 1256 |
+
multiple_of: Value must be a multiple of this. Only applicable to numbers.
|
| 1257 |
+
min_length: Minimum length for iterables.
|
| 1258 |
+
max_length: Maximum length for iterables.
|
| 1259 |
+
pattern: Pattern for strings (a regular expression).
|
| 1260 |
+
allow_inf_nan: Allow `inf`, `-inf`, `nan`. Only applicable to float and [`Decimal`][decimal.Decimal] numbers.
|
| 1261 |
+
max_digits: Maximum number of allow digits for strings.
|
| 1262 |
+
decimal_places: Maximum number of decimal places allowed for numbers.
|
| 1263 |
+
union_mode: The strategy to apply when validating a union. Can be `smart` (the default), or `left_to_right`.
|
| 1264 |
+
See [Union Mode](../concepts/unions.md#union-modes) for details.
|
| 1265 |
+
fail_fast: If `True`, validation will stop on the first error. If `False`, all validation errors will be collected.
|
| 1266 |
+
This option can be applied only to iterable types (list, tuple, set, and frozenset).
|
| 1267 |
+
extra: (Deprecated) Extra fields that will be included in the JSON schema.
|
| 1268 |
+
|
| 1269 |
+
!!! warning Deprecated
|
| 1270 |
+
The `extra` kwargs is deprecated. Use `json_schema_extra` instead.
|
| 1271 |
+
|
| 1272 |
+
Returns:
|
| 1273 |
+
A new [`FieldInfo`][pydantic.fields.FieldInfo]. The return annotation is `Any` so `Field` can be used on
|
| 1274 |
+
type-annotated fields without causing a type error.
|
| 1275 |
+
"""
|
| 1276 |
+
# Check deprecated and removed params from V1. This logic should eventually be removed.
|
| 1277 |
+
const = extra.pop('const', None) # type: ignore
|
| 1278 |
+
if const is not None:
|
| 1279 |
+
raise PydanticUserError('`const` is removed, use `Literal` instead', code='removed-kwargs')
|
| 1280 |
+
|
| 1281 |
+
min_items = extra.pop('min_items', None) # type: ignore
|
| 1282 |
+
if min_items is not None:
|
| 1283 |
+
warn(
|
| 1284 |
+
'`min_items` is deprecated and will be removed, use `min_length` instead',
|
| 1285 |
+
PydanticDeprecatedSince20,
|
| 1286 |
+
stacklevel=2,
|
| 1287 |
+
)
|
| 1288 |
+
if min_length in (None, _Unset):
|
| 1289 |
+
min_length = min_items # type: ignore
|
| 1290 |
+
|
| 1291 |
+
max_items = extra.pop('max_items', None) # type: ignore
|
| 1292 |
+
if max_items is not None:
|
| 1293 |
+
warn(
|
| 1294 |
+
'`max_items` is deprecated and will be removed, use `max_length` instead',
|
| 1295 |
+
PydanticDeprecatedSince20,
|
| 1296 |
+
stacklevel=2,
|
| 1297 |
+
)
|
| 1298 |
+
if max_length in (None, _Unset):
|
| 1299 |
+
max_length = max_items # type: ignore
|
| 1300 |
+
|
| 1301 |
+
unique_items = extra.pop('unique_items', None) # type: ignore
|
| 1302 |
+
if unique_items is not None:
|
| 1303 |
+
raise PydanticUserError(
|
| 1304 |
+
(
|
| 1305 |
+
'`unique_items` is removed, use `Set` instead'
|
| 1306 |
+
'(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)'
|
| 1307 |
+
),
|
| 1308 |
+
code='removed-kwargs',
|
| 1309 |
+
)
|
| 1310 |
+
|
| 1311 |
+
allow_mutation = extra.pop('allow_mutation', None) # type: ignore
|
| 1312 |
+
if allow_mutation is not None:
|
| 1313 |
+
warn(
|
| 1314 |
+
'`allow_mutation` is deprecated and will be removed. use `frozen` instead',
|
| 1315 |
+
PydanticDeprecatedSince20,
|
| 1316 |
+
stacklevel=2,
|
| 1317 |
+
)
|
| 1318 |
+
if allow_mutation is False:
|
| 1319 |
+
frozen = True
|
| 1320 |
+
|
| 1321 |
+
regex = extra.pop('regex', None) # type: ignore
|
| 1322 |
+
if regex is not None:
|
| 1323 |
+
raise PydanticUserError('`regex` is removed. use `pattern` instead', code='removed-kwargs')
|
| 1324 |
+
|
| 1325 |
+
if extra:
|
| 1326 |
+
warn(
|
| 1327 |
+
'Using extra keyword arguments on `Field` is deprecated and will be removed.'
|
| 1328 |
+
' Use `json_schema_extra` instead.'
|
| 1329 |
+
f' (Extra keys: {", ".join(k.__repr__() for k in extra.keys())})',
|
| 1330 |
+
PydanticDeprecatedSince20,
|
| 1331 |
+
stacklevel=2,
|
| 1332 |
+
)
|
| 1333 |
+
if not json_schema_extra or json_schema_extra is _Unset:
|
| 1334 |
+
json_schema_extra = extra # type: ignore
|
| 1335 |
+
|
| 1336 |
+
if (
|
| 1337 |
+
validation_alias
|
| 1338 |
+
and validation_alias is not _Unset
|
| 1339 |
+
and not isinstance(validation_alias, (str, AliasChoices, AliasPath))
|
| 1340 |
+
):
|
| 1341 |
+
raise TypeError('Invalid `validation_alias` type. it should be `str`, `AliasChoices`, or `AliasPath`')
|
| 1342 |
+
|
| 1343 |
+
if serialization_alias in (_Unset, None) and isinstance(alias, str):
|
| 1344 |
+
serialization_alias = alias
|
| 1345 |
+
|
| 1346 |
+
if validation_alias in (_Unset, None):
|
| 1347 |
+
validation_alias = alias
|
| 1348 |
+
|
| 1349 |
+
include = extra.pop('include', None) # type: ignore
|
| 1350 |
+
if include is not None:
|
| 1351 |
+
warn(
|
| 1352 |
+
'`include` is deprecated and does nothing. It will be removed, use `exclude` instead',
|
| 1353 |
+
PydanticDeprecatedSince20,
|
| 1354 |
+
stacklevel=2,
|
| 1355 |
+
)
|
| 1356 |
+
|
| 1357 |
+
return FieldInfo.from_field(
|
| 1358 |
+
default,
|
| 1359 |
+
default_factory=default_factory,
|
| 1360 |
+
alias=alias,
|
| 1361 |
+
alias_priority=alias_priority,
|
| 1362 |
+
validation_alias=validation_alias,
|
| 1363 |
+
serialization_alias=serialization_alias,
|
| 1364 |
+
title=title,
|
| 1365 |
+
field_title_generator=field_title_generator,
|
| 1366 |
+
description=description,
|
| 1367 |
+
examples=examples,
|
| 1368 |
+
exclude=exclude,
|
| 1369 |
+
exclude_if=exclude_if,
|
| 1370 |
+
discriminator=discriminator,
|
| 1371 |
+
deprecated=deprecated,
|
| 1372 |
+
json_schema_extra=json_schema_extra,
|
| 1373 |
+
frozen=frozen,
|
| 1374 |
+
pattern=pattern,
|
| 1375 |
+
validate_default=validate_default,
|
| 1376 |
+
repr=repr,
|
| 1377 |
+
init=init,
|
| 1378 |
+
init_var=init_var,
|
| 1379 |
+
kw_only=kw_only,
|
| 1380 |
+
coerce_numbers_to_str=coerce_numbers_to_str,
|
| 1381 |
+
strict=strict,
|
| 1382 |
+
gt=gt,
|
| 1383 |
+
ge=ge,
|
| 1384 |
+
lt=lt,
|
| 1385 |
+
le=le,
|
| 1386 |
+
multiple_of=multiple_of,
|
| 1387 |
+
min_length=min_length,
|
| 1388 |
+
max_length=max_length,
|
| 1389 |
+
allow_inf_nan=allow_inf_nan,
|
| 1390 |
+
max_digits=max_digits,
|
| 1391 |
+
decimal_places=decimal_places,
|
| 1392 |
+
union_mode=union_mode,
|
| 1393 |
+
fail_fast=fail_fast,
|
| 1394 |
+
)
|
| 1395 |
+
|
| 1396 |
+
|
| 1397 |
+
_FIELD_ARG_NAMES = set(inspect.signature(Field).parameters)
|
| 1398 |
+
_FIELD_ARG_NAMES.remove('extra') # do not include the varkwargs parameter
|
| 1399 |
+
|
| 1400 |
+
|
| 1401 |
+
class ModelPrivateAttr(_repr.Representation):
|
| 1402 |
+
"""A descriptor for private attributes in class models.
|
| 1403 |
+
|
| 1404 |
+
!!! warning
|
| 1405 |
+
You generally shouldn't be creating `ModelPrivateAttr` instances directly, instead use
|
| 1406 |
+
`pydantic.fields.PrivateAttr`. (This is similar to `FieldInfo` vs. `Field`.)
|
| 1407 |
+
|
| 1408 |
+
Attributes:
|
| 1409 |
+
default: The default value of the attribute if not provided.
|
| 1410 |
+
default_factory: A callable to generate the default value. The callable can either take 0 arguments
|
| 1411 |
+
(in which case it is called as is) or a single argument containing the validated data (the model's
|
| 1412 |
+
[`__dict__`][object.__dict__]) and the already initialized private attributes.
|
| 1413 |
+
"""
|
| 1414 |
+
|
| 1415 |
+
__slots__ = ('default', 'default_factory')
|
| 1416 |
+
|
| 1417 |
+
def __init__(
|
| 1418 |
+
self,
|
| 1419 |
+
default: Any = PydanticUndefined,
|
| 1420 |
+
*,
|
| 1421 |
+
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = None,
|
| 1422 |
+
) -> None:
|
| 1423 |
+
if default is Ellipsis:
|
| 1424 |
+
self.default = PydanticUndefined
|
| 1425 |
+
else:
|
| 1426 |
+
self.default = default
|
| 1427 |
+
self.default_factory = default_factory
|
| 1428 |
+
|
| 1429 |
+
if not TYPE_CHECKING:
|
| 1430 |
+
# We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
|
| 1431 |
+
|
| 1432 |
+
def __getattr__(self, item: str) -> Any:
|
| 1433 |
+
"""This function improves compatibility with custom descriptors by ensuring delegation happens
|
| 1434 |
+
as expected when the default value of a private attribute is a descriptor.
|
| 1435 |
+
"""
|
| 1436 |
+
if item in {'__get__', '__set__', '__delete__'}:
|
| 1437 |
+
if hasattr(self.default, item):
|
| 1438 |
+
return getattr(self.default, item)
|
| 1439 |
+
raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
|
| 1440 |
+
|
| 1441 |
+
def __set_name__(self, cls: type[Any], name: str) -> None:
|
| 1442 |
+
"""Preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487."""
|
| 1443 |
+
default = self.default
|
| 1444 |
+
if default is PydanticUndefined:
|
| 1445 |
+
return
|
| 1446 |
+
set_name = getattr(default, '__set_name__', None)
|
| 1447 |
+
if callable(set_name):
|
| 1448 |
+
set_name(cls, name)
|
| 1449 |
+
|
| 1450 |
+
@property
|
| 1451 |
+
def default_factory_takes_validated_data(self) -> bool | None:
|
| 1452 |
+
"""Whether the provided default factory callable has a validated data parameter.
|
| 1453 |
+
|
| 1454 |
+
Returns `None` if no default factory is set.
|
| 1455 |
+
"""
|
| 1456 |
+
if self.default_factory is not None:
|
| 1457 |
+
return _fields.takes_validated_data_argument(self.default_factory)
|
| 1458 |
+
|
| 1459 |
+
@overload
|
| 1460 |
+
def get_default(
|
| 1461 |
+
self, *, call_default_factory: Literal[True], validated_data: dict[str, Any] | None = None
|
| 1462 |
+
) -> Any: ...
|
| 1463 |
+
|
| 1464 |
+
@overload
|
| 1465 |
+
def get_default(self, *, call_default_factory: Literal[False] = ...) -> Any: ...
|
| 1466 |
+
|
| 1467 |
+
def get_default(self, *, call_default_factory: bool = False, validated_data: dict[str, Any] | None = None) -> Any:
|
| 1468 |
+
"""Get the default value.
|
| 1469 |
+
|
| 1470 |
+
We expose an option for whether to call the default_factory (if present), as calling it may
|
| 1471 |
+
result in side effects that we want to avoid. However, there are times when it really should
|
| 1472 |
+
be called (namely, when instantiating a model via `model_construct`).
|
| 1473 |
+
|
| 1474 |
+
Args:
|
| 1475 |
+
call_default_factory: Whether to call the default factory or not.
|
| 1476 |
+
validated_data: The already validated data to be passed to the default factory.
|
| 1477 |
+
|
| 1478 |
+
Returns:
|
| 1479 |
+
The default value, calling the default factory if requested or `None` if not set.
|
| 1480 |
+
"""
|
| 1481 |
+
return _fields.resolve_default_value(
|
| 1482 |
+
default=self.default,
|
| 1483 |
+
default_factory=self.default_factory,
|
| 1484 |
+
validated_data=validated_data,
|
| 1485 |
+
call_default_factory=call_default_factory,
|
| 1486 |
+
)
|
| 1487 |
+
|
| 1488 |
+
def __eq__(self, other: Any) -> bool:
|
| 1489 |
+
return isinstance(other, self.__class__) and (self.default, self.default_factory) == (
|
| 1490 |
+
other.default,
|
| 1491 |
+
other.default_factory,
|
| 1492 |
+
)
|
| 1493 |
+
|
| 1494 |
+
|
| 1495 |
+
# NOTE: Actual return type is 'ModelPrivateAttr', but we want to help type checkers
|
| 1496 |
+
# to understand the magic that happens at runtime.
|
| 1497 |
+
@overload # `default` argument set
|
| 1498 |
+
def PrivateAttr(
|
| 1499 |
+
default: _T,
|
| 1500 |
+
*,
|
| 1501 |
+
init: Literal[False] = False,
|
| 1502 |
+
) -> _T: ...
|
| 1503 |
+
@overload # `default_factory` argument set
|
| 1504 |
+
def PrivateAttr(
|
| 1505 |
+
*,
|
| 1506 |
+
default_factory: Callable[[], _T] | Callable[[dict[str, Any]], _T],
|
| 1507 |
+
init: Literal[False] = False,
|
| 1508 |
+
) -> _T: ...
|
| 1509 |
+
@overload # No default set
|
| 1510 |
+
def PrivateAttr(
|
| 1511 |
+
*,
|
| 1512 |
+
init: Literal[False] = False,
|
| 1513 |
+
) -> Any: ...
|
| 1514 |
+
def PrivateAttr(
|
| 1515 |
+
default: Any = PydanticUndefined,
|
| 1516 |
+
*,
|
| 1517 |
+
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = None,
|
| 1518 |
+
init: Literal[False] = False,
|
| 1519 |
+
) -> Any:
|
| 1520 |
+
"""!!! abstract "Usage Documentation"
|
| 1521 |
+
[Private Model Attributes](../concepts/models.md#private-model-attributes)
|
| 1522 |
+
|
| 1523 |
+
Indicates that an attribute is intended for private use and not handled during normal validation/serialization.
|
| 1524 |
+
|
| 1525 |
+
Private attributes are not validated by Pydantic, so it's up to you to ensure they are used in a type-safe manner.
|
| 1526 |
+
|
| 1527 |
+
Private attributes are stored in `__private_attributes__` on the model.
|
| 1528 |
+
|
| 1529 |
+
Args:
|
| 1530 |
+
default: The attribute's default value. Defaults to Undefined.
|
| 1531 |
+
default_factory: A callable to generate the default value. The callable can either take 0 arguments
|
| 1532 |
+
(in which case it is called as is) or a single argument containing the validated data (the model's
|
| 1533 |
+
[`__dict__`][object.__dict__]) and the already initialized private attributes.
|
| 1534 |
+
If both `default` and `default_factory` are set, an error will be raised.
|
| 1535 |
+
init: Whether the attribute should be included in the constructor of the dataclass. Always `False`.
|
| 1536 |
+
|
| 1537 |
+
Returns:
|
| 1538 |
+
An instance of [`ModelPrivateAttr`][pydantic.fields.ModelPrivateAttr] class.
|
| 1539 |
+
|
| 1540 |
+
Raises:
|
| 1541 |
+
ValueError: If both `default` and `default_factory` are set.
|
| 1542 |
+
"""
|
| 1543 |
+
if default is not PydanticUndefined and default_factory is not None:
|
| 1544 |
+
raise TypeError('cannot specify both default and default_factory')
|
| 1545 |
+
|
| 1546 |
+
return ModelPrivateAttr(
|
| 1547 |
+
default,
|
| 1548 |
+
default_factory=default_factory,
|
| 1549 |
+
)
|
| 1550 |
+
|
| 1551 |
+
|
| 1552 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 1553 |
+
class ComputedFieldInfo:
|
| 1554 |
+
"""A container for data from `@computed_field` so that we can access it while building the pydantic-core schema.
|
| 1555 |
+
|
| 1556 |
+
Attributes:
|
| 1557 |
+
decorator_repr: A class variable representing the decorator string, '@computed_field'.
|
| 1558 |
+
wrapped_property: The wrapped computed field property.
|
| 1559 |
+
return_type: The type of the computed field property's return value.
|
| 1560 |
+
alias: The alias of the property to be used during serialization.
|
| 1561 |
+
alias_priority: The priority of the alias. This affects whether an alias generator is used.
|
| 1562 |
+
title: Title of the computed field to include in the serialization JSON schema.
|
| 1563 |
+
field_title_generator: A callable that takes a field name and returns title for it.
|
| 1564 |
+
description: Description of the computed field to include in the serialization JSON schema.
|
| 1565 |
+
deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport,
|
| 1566 |
+
or a boolean. If `True`, a default deprecation message will be emitted when accessing the field.
|
| 1567 |
+
examples: Example values of the computed field to include in the serialization JSON schema.
|
| 1568 |
+
json_schema_extra: A dict or callable to provide extra JSON schema properties.
|
| 1569 |
+
repr: A boolean indicating whether to include the field in the __repr__ output.
|
| 1570 |
+
"""
|
| 1571 |
+
|
| 1572 |
+
decorator_repr: ClassVar[str] = '@computed_field'
|
| 1573 |
+
wrapped_property: property
|
| 1574 |
+
return_type: Any
|
| 1575 |
+
alias: str | None
|
| 1576 |
+
alias_priority: int | None
|
| 1577 |
+
exclude_if: Callable[[Any], bool] | None
|
| 1578 |
+
title: str | None
|
| 1579 |
+
field_title_generator: Callable[[str, ComputedFieldInfo], str] | None
|
| 1580 |
+
description: str | None
|
| 1581 |
+
deprecated: Deprecated | str | bool | None
|
| 1582 |
+
examples: list[Any] | None
|
| 1583 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None
|
| 1584 |
+
repr: bool
|
| 1585 |
+
# NOTE: if you add a new field, add it to the `__copy__()` implementation.
|
| 1586 |
+
|
| 1587 |
+
def __copy__(self) -> Self:
|
| 1588 |
+
return type(self)(
|
| 1589 |
+
wrapped_property=self.wrapped_property,
|
| 1590 |
+
return_type=self.return_type,
|
| 1591 |
+
alias=self.alias,
|
| 1592 |
+
alias_priority=self.alias_priority,
|
| 1593 |
+
exclude_if=self.exclude_if,
|
| 1594 |
+
title=self.title,
|
| 1595 |
+
field_title_generator=self.field_title_generator,
|
| 1596 |
+
description=self.description,
|
| 1597 |
+
deprecated=self.deprecated,
|
| 1598 |
+
examples=self.examples.copy() if isinstance(self.examples, list) else self.examples,
|
| 1599 |
+
json_schema_extra=self.json_schema_extra.copy()
|
| 1600 |
+
if isinstance(self.json_schema_extra, dict)
|
| 1601 |
+
else self.json_schema_extra,
|
| 1602 |
+
repr=self.repr,
|
| 1603 |
+
)
|
| 1604 |
+
|
| 1605 |
+
@property
|
| 1606 |
+
def deprecation_message(self) -> str | None:
|
| 1607 |
+
"""The deprecation message to be emitted, or `None` if not set."""
|
| 1608 |
+
if self.deprecated is None:
|
| 1609 |
+
return None
|
| 1610 |
+
if isinstance(self.deprecated, bool):
|
| 1611 |
+
return 'deprecated' if self.deprecated else None
|
| 1612 |
+
return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message
|
| 1613 |
+
|
| 1614 |
+
def _update_from_config(self, config_wrapper: ConfigWrapper, name: str) -> None:
|
| 1615 |
+
"""Update the instance from the configuration set on the class this computed field belongs to."""
|
| 1616 |
+
title_generator = self.field_title_generator or config_wrapper.field_title_generator
|
| 1617 |
+
if title_generator is not None and self.title is None:
|
| 1618 |
+
self.title = title_generator(name, self)
|
| 1619 |
+
if config_wrapper.alias_generator is not None:
|
| 1620 |
+
self._apply_alias_generator(config_wrapper.alias_generator, name)
|
| 1621 |
+
|
| 1622 |
+
def _apply_alias_generator(self, alias_generator: Callable[[str], str] | AliasGenerator, name: str) -> None:
|
| 1623 |
+
"""Apply an alias generator to aliases if appropriate.
|
| 1624 |
+
|
| 1625 |
+
Args:
|
| 1626 |
+
alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
|
| 1627 |
+
name: The name of the computed field from which to generate the alias.
|
| 1628 |
+
"""
|
| 1629 |
+
# Apply an alias_generator if
|
| 1630 |
+
# 1. An alias is not specified
|
| 1631 |
+
# 2. An alias is specified, but the priority is <= 1
|
| 1632 |
+
|
| 1633 |
+
if self.alias_priority is None or self.alias_priority <= 1 or self.alias is None:
|
| 1634 |
+
alias, _, serialization_alias = None, None, None
|
| 1635 |
+
|
| 1636 |
+
if isinstance(alias_generator, AliasGenerator):
|
| 1637 |
+
alias, _, serialization_alias = alias_generator.generate_aliases(name)
|
| 1638 |
+
elif callable(alias_generator):
|
| 1639 |
+
alias = alias_generator(name)
|
| 1640 |
+
|
| 1641 |
+
# if priority is not set, we set to 1
|
| 1642 |
+
# which supports the case where the alias_generator from a child class is used
|
| 1643 |
+
# to generate an alias for a field in a parent class
|
| 1644 |
+
if self.alias_priority is None or self.alias_priority <= 1:
|
| 1645 |
+
self.alias_priority = 1
|
| 1646 |
+
|
| 1647 |
+
# if the priority is 1, then we set the aliases to the generated alias
|
| 1648 |
+
# note that we use the serialization_alias with priority over alias, as computed_field
|
| 1649 |
+
# aliases are used for serialization only (not validation)
|
| 1650 |
+
if self.alias_priority == 1:
|
| 1651 |
+
self.alias = _utils.get_first_not_none(serialization_alias, alias)
|
| 1652 |
+
|
| 1653 |
+
|
| 1654 |
+
def _wrapped_property_is_private(property_: cached_property | property) -> bool: # type: ignore
|
| 1655 |
+
"""Returns true if provided property is private, False otherwise."""
|
| 1656 |
+
wrapped_name: str = ''
|
| 1657 |
+
|
| 1658 |
+
if isinstance(property_, property):
|
| 1659 |
+
wrapped_name = getattr(property_.fget, '__name__', '')
|
| 1660 |
+
elif isinstance(property_, cached_property): # type: ignore
|
| 1661 |
+
wrapped_name = getattr(property_.func, '__name__', '') # type: ignore
|
| 1662 |
+
|
| 1663 |
+
return wrapped_name.startswith('_') and not wrapped_name.startswith('__')
|
| 1664 |
+
|
| 1665 |
+
|
| 1666 |
+
# this should really be `property[T], cached_property[T]` but property is not generic unlike cached_property
|
| 1667 |
+
# See https://github.com/python/typing/issues/985 and linked issues
|
| 1668 |
+
PropertyT = TypeVar('PropertyT')
|
| 1669 |
+
|
| 1670 |
+
|
| 1671 |
+
@overload
|
| 1672 |
+
def computed_field(func: PropertyT, /) -> PropertyT: ...
|
| 1673 |
+
|
| 1674 |
+
|
| 1675 |
+
@overload
|
| 1676 |
+
def computed_field(
|
| 1677 |
+
*,
|
| 1678 |
+
alias: str | None = None,
|
| 1679 |
+
alias_priority: int | None = None,
|
| 1680 |
+
exclude_if: Callable[[Any], bool] | None = None,
|
| 1681 |
+
title: str | None = None,
|
| 1682 |
+
field_title_generator: Callable[[str, ComputedFieldInfo], str] | None = None,
|
| 1683 |
+
description: str | None = None,
|
| 1684 |
+
deprecated: Deprecated | str | bool | None = None,
|
| 1685 |
+
examples: list[Any] | None = None,
|
| 1686 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = None,
|
| 1687 |
+
repr: bool = True,
|
| 1688 |
+
return_type: Any = PydanticUndefined,
|
| 1689 |
+
) -> Callable[[PropertyT], PropertyT]: ...
|
| 1690 |
+
|
| 1691 |
+
|
| 1692 |
+
def computed_field(
|
| 1693 |
+
func: PropertyT | None = None,
|
| 1694 |
+
/,
|
| 1695 |
+
*,
|
| 1696 |
+
alias: str | None = None,
|
| 1697 |
+
alias_priority: int | None = None,
|
| 1698 |
+
exclude_if: Callable[[Any], bool] | None = None,
|
| 1699 |
+
title: str | None = None,
|
| 1700 |
+
field_title_generator: Callable[[str, ComputedFieldInfo], str] | None = None,
|
| 1701 |
+
description: str | None = None,
|
| 1702 |
+
deprecated: Deprecated | str | bool | None = None,
|
| 1703 |
+
examples: list[Any] | None = None,
|
| 1704 |
+
json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = None,
|
| 1705 |
+
repr: bool | None = None,
|
| 1706 |
+
return_type: Any = PydanticUndefined,
|
| 1707 |
+
) -> PropertyT | Callable[[PropertyT], PropertyT]:
|
| 1708 |
+
"""!!! abstract "Usage Documentation"
|
| 1709 |
+
[The `computed_field` decorator](../concepts/fields.md#the-computed_field-decorator)
|
| 1710 |
+
|
| 1711 |
+
Decorator to include `property` and `cached_property` when serializing models or dataclasses.
|
| 1712 |
+
|
| 1713 |
+
This is useful for fields that are computed from other fields, or for fields that are expensive to compute and should be cached.
|
| 1714 |
+
|
| 1715 |
+
```python
|
| 1716 |
+
from pydantic import BaseModel, computed_field
|
| 1717 |
+
|
| 1718 |
+
class Rectangle(BaseModel):
|
| 1719 |
+
width: int
|
| 1720 |
+
length: int
|
| 1721 |
+
|
| 1722 |
+
@computed_field
|
| 1723 |
+
@property
|
| 1724 |
+
def area(self) -> int:
|
| 1725 |
+
return self.width * self.length
|
| 1726 |
+
|
| 1727 |
+
print(Rectangle(width=3, length=2).model_dump())
|
| 1728 |
+
#> {'width': 3, 'length': 2, 'area': 6}
|
| 1729 |
+
```
|
| 1730 |
+
|
| 1731 |
+
If applied to functions not yet decorated with `@property` or `@cached_property`, the function is
|
| 1732 |
+
automatically wrapped with `property`. Although this is more concise, you will lose IntelliSense in your IDE,
|
| 1733 |
+
and confuse static type checkers, thus explicit use of `@property` is recommended.
|
| 1734 |
+
|
| 1735 |
+
!!! warning "Mypy Warning"
|
| 1736 |
+
Even with the `@property` or `@cached_property` applied to your function before `@computed_field`,
|
| 1737 |
+
mypy may throw a `Decorated property not supported` error.
|
| 1738 |
+
See [mypy issue #1362](https://github.com/python/mypy/issues/1362), for more information.
|
| 1739 |
+
To avoid this error message, add `# type: ignore[prop-decorator]` to the `@computed_field` line.
|
| 1740 |
+
|
| 1741 |
+
[pyright](https://github.com/microsoft/pyright) supports `@computed_field` without error.
|
| 1742 |
+
|
| 1743 |
+
```python
|
| 1744 |
+
import random
|
| 1745 |
+
|
| 1746 |
+
from pydantic import BaseModel, computed_field
|
| 1747 |
+
|
| 1748 |
+
class Square(BaseModel):
|
| 1749 |
+
width: float
|
| 1750 |
+
|
| 1751 |
+
@computed_field
|
| 1752 |
+
def area(self) -> float: # converted to a `property` by `computed_field`
|
| 1753 |
+
return round(self.width**2, 2)
|
| 1754 |
+
|
| 1755 |
+
@area.setter
|
| 1756 |
+
def area(self, new_area: float) -> None:
|
| 1757 |
+
self.width = new_area**0.5
|
| 1758 |
+
|
| 1759 |
+
@computed_field(alias='the magic number', repr=False)
|
| 1760 |
+
def random_number(self) -> int:
|
| 1761 |
+
return random.randint(0, 1_000)
|
| 1762 |
+
|
| 1763 |
+
square = Square(width=1.3)
|
| 1764 |
+
|
| 1765 |
+
# `random_number` does not appear in representation
|
| 1766 |
+
print(repr(square))
|
| 1767 |
+
#> Square(width=1.3, area=1.69)
|
| 1768 |
+
|
| 1769 |
+
print(square.random_number)
|
| 1770 |
+
#> 3
|
| 1771 |
+
|
| 1772 |
+
square.area = 4
|
| 1773 |
+
|
| 1774 |
+
print(square.model_dump_json(by_alias=True))
|
| 1775 |
+
#> {"width":2.0,"area":4.0,"the magic number":3}
|
| 1776 |
+
```
|
| 1777 |
+
|
| 1778 |
+
!!! warning "Overriding with `computed_field`"
|
| 1779 |
+
You can't override a field from a parent class with a `computed_field` in the child class.
|
| 1780 |
+
`mypy` complains about this behavior if allowed, and `dataclasses` doesn't allow this pattern either.
|
| 1781 |
+
See the example below:
|
| 1782 |
+
|
| 1783 |
+
```python
|
| 1784 |
+
from pydantic import BaseModel, computed_field
|
| 1785 |
+
|
| 1786 |
+
class Parent(BaseModel):
|
| 1787 |
+
a: str
|
| 1788 |
+
|
| 1789 |
+
try:
|
| 1790 |
+
|
| 1791 |
+
class Child(Parent):
|
| 1792 |
+
@computed_field
|
| 1793 |
+
@property
|
| 1794 |
+
def a(self) -> str:
|
| 1795 |
+
return 'new a'
|
| 1796 |
+
|
| 1797 |
+
except TypeError as e:
|
| 1798 |
+
print(e)
|
| 1799 |
+
'''
|
| 1800 |
+
Field 'a' of class 'Child' overrides symbol of same name in a parent class. This override with a computed_field is incompatible.
|
| 1801 |
+
'''
|
| 1802 |
+
```
|
| 1803 |
+
|
| 1804 |
+
Private properties decorated with `@computed_field` have `repr=False` by default.
|
| 1805 |
+
|
| 1806 |
+
```python
|
| 1807 |
+
from functools import cached_property
|
| 1808 |
+
|
| 1809 |
+
from pydantic import BaseModel, computed_field
|
| 1810 |
+
|
| 1811 |
+
class Model(BaseModel):
|
| 1812 |
+
foo: int
|
| 1813 |
+
|
| 1814 |
+
@computed_field
|
| 1815 |
+
@cached_property
|
| 1816 |
+
def _private_cached_property(self) -> int:
|
| 1817 |
+
return -self.foo
|
| 1818 |
+
|
| 1819 |
+
@computed_field
|
| 1820 |
+
@property
|
| 1821 |
+
def _private_property(self) -> int:
|
| 1822 |
+
return -self.foo
|
| 1823 |
+
|
| 1824 |
+
m = Model(foo=1)
|
| 1825 |
+
print(repr(m))
|
| 1826 |
+
#> Model(foo=1)
|
| 1827 |
+
```
|
| 1828 |
+
|
| 1829 |
+
Args:
|
| 1830 |
+
func: the function to wrap.
|
| 1831 |
+
alias: alias to use when serializing this computed field, only used when `by_alias=True`
|
| 1832 |
+
alias_priority: priority of the alias. This affects whether an alias generator is used
|
| 1833 |
+
exclude_if: A callable that determines whether to exclude this computed field during serialization based on its value.
|
| 1834 |
+
title: Title to use when including this computed field in JSON Schema
|
| 1835 |
+
field_title_generator: A callable that takes a field name and returns title for it.
|
| 1836 |
+
description: Description to use when including this computed field in JSON Schema, defaults to the function's
|
| 1837 |
+
docstring
|
| 1838 |
+
deprecated: A deprecation message (or an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport).
|
| 1839 |
+
to be emitted when accessing the field. Or a boolean. This will automatically be set if the property is decorated with the
|
| 1840 |
+
`deprecated` decorator.
|
| 1841 |
+
examples: Example values to use when including this computed field in JSON Schema
|
| 1842 |
+
json_schema_extra: A dict or callable to provide extra JSON schema properties.
|
| 1843 |
+
repr: whether to include this computed field in model repr.
|
| 1844 |
+
Default is `False` for private properties and `True` for public properties.
|
| 1845 |
+
return_type: optional return for serialization logic to expect when serializing to JSON, if included
|
| 1846 |
+
this must be correct, otherwise a `TypeError` is raised.
|
| 1847 |
+
If you don't include a return type Any is used, which does runtime introspection to handle arbitrary
|
| 1848 |
+
objects.
|
| 1849 |
+
|
| 1850 |
+
Returns:
|
| 1851 |
+
A proxy wrapper for the property.
|
| 1852 |
+
"""
|
| 1853 |
+
|
| 1854 |
+
def dec(f: Any) -> Any:
|
| 1855 |
+
nonlocal description, deprecated, return_type, alias_priority
|
| 1856 |
+
unwrapped = _decorators.unwrap_wrapped_function(f)
|
| 1857 |
+
|
| 1858 |
+
if description is None and unwrapped.__doc__:
|
| 1859 |
+
description = inspect.cleandoc(unwrapped.__doc__)
|
| 1860 |
+
|
| 1861 |
+
if deprecated is None and hasattr(unwrapped, '__deprecated__'):
|
| 1862 |
+
deprecated = unwrapped.__deprecated__
|
| 1863 |
+
|
| 1864 |
+
# if the function isn't already decorated with `@property` (or another descriptor), then we wrap it now
|
| 1865 |
+
f = _decorators.ensure_property(f)
|
| 1866 |
+
alias_priority = (alias_priority or 2) if alias is not None else None
|
| 1867 |
+
|
| 1868 |
+
if repr is None:
|
| 1869 |
+
repr_: bool = not _wrapped_property_is_private(property_=f)
|
| 1870 |
+
else:
|
| 1871 |
+
repr_ = repr
|
| 1872 |
+
|
| 1873 |
+
dec_info = ComputedFieldInfo(
|
| 1874 |
+
f,
|
| 1875 |
+
return_type,
|
| 1876 |
+
alias,
|
| 1877 |
+
alias_priority,
|
| 1878 |
+
exclude_if,
|
| 1879 |
+
title,
|
| 1880 |
+
field_title_generator,
|
| 1881 |
+
description,
|
| 1882 |
+
deprecated,
|
| 1883 |
+
examples,
|
| 1884 |
+
json_schema_extra,
|
| 1885 |
+
repr_,
|
| 1886 |
+
)
|
| 1887 |
+
return _decorators.PydanticDescriptorProxy(f, dec_info)
|
| 1888 |
+
|
| 1889 |
+
if func is None:
|
| 1890 |
+
return dec
|
| 1891 |
+
else:
|
| 1892 |
+
return dec(func)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/functional_serializers.py
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""This module contains related classes and functions for serialization."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
from functools import partial, partialmethod
|
| 7 |
+
from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, overload
|
| 8 |
+
|
| 9 |
+
from pydantic_core import PydanticUndefined, core_schema
|
| 10 |
+
from pydantic_core.core_schema import SerializationInfo, SerializerFunctionWrapHandler, WhenUsed
|
| 11 |
+
from typing_extensions import TypeAlias
|
| 12 |
+
|
| 13 |
+
from . import PydanticUndefinedAnnotation
|
| 14 |
+
from ._internal import _decorators, _internal_dataclass
|
| 15 |
+
from .annotated_handlers import GetCoreSchemaHandler
|
| 16 |
+
from .errors import PydanticUserError
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True)
|
| 20 |
+
class PlainSerializer:
|
| 21 |
+
"""Plain serializers use a function to modify the output of serialization.
|
| 22 |
+
|
| 23 |
+
This is particularly helpful when you want to customize the serialization for annotated types.
|
| 24 |
+
Consider an input of `list`, which will be serialized into a space-delimited string.
|
| 25 |
+
|
| 26 |
+
```python
|
| 27 |
+
from typing import Annotated
|
| 28 |
+
|
| 29 |
+
from pydantic import BaseModel, PlainSerializer
|
| 30 |
+
|
| 31 |
+
CustomStr = Annotated[
|
| 32 |
+
list, PlainSerializer(lambda x: ' '.join(x), return_type=str)
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
class StudentModel(BaseModel):
|
| 36 |
+
courses: CustomStr
|
| 37 |
+
|
| 38 |
+
student = StudentModel(courses=['Math', 'Chemistry', 'English'])
|
| 39 |
+
print(student.model_dump())
|
| 40 |
+
#> {'courses': 'Math Chemistry English'}
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Attributes:
|
| 44 |
+
func: The serializer function.
|
| 45 |
+
return_type: The return type for the function. If omitted it will be inferred from the type annotation.
|
| 46 |
+
when_used: Determines when this serializer should be used. Accepts a string with values `'always'`,
|
| 47 |
+
`'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'.
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
func: core_schema.SerializerFunction
|
| 51 |
+
return_type: Any = PydanticUndefined
|
| 52 |
+
when_used: WhenUsed = 'always'
|
| 53 |
+
|
| 54 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 55 |
+
"""Gets the Pydantic core schema.
|
| 56 |
+
|
| 57 |
+
Args:
|
| 58 |
+
source_type: The source type.
|
| 59 |
+
handler: The `GetCoreSchemaHandler` instance.
|
| 60 |
+
|
| 61 |
+
Returns:
|
| 62 |
+
The Pydantic core schema.
|
| 63 |
+
"""
|
| 64 |
+
schema = handler(source_type)
|
| 65 |
+
if self.return_type is not PydanticUndefined:
|
| 66 |
+
return_type = self.return_type
|
| 67 |
+
else:
|
| 68 |
+
try:
|
| 69 |
+
# Do not pass in globals as the function could be defined in a different module.
|
| 70 |
+
# Instead, let `get_callable_return_type` infer the globals to use, but still pass
|
| 71 |
+
# in locals that may contain a parent/rebuild namespace:
|
| 72 |
+
return_type = _decorators.get_callable_return_type(
|
| 73 |
+
self.func,
|
| 74 |
+
localns=handler._get_types_namespace().locals,
|
| 75 |
+
)
|
| 76 |
+
except NameError as e:
|
| 77 |
+
raise PydanticUndefinedAnnotation.from_name_error(e) from e
|
| 78 |
+
|
| 79 |
+
return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type)
|
| 80 |
+
schema['serialization'] = core_schema.plain_serializer_function_ser_schema(
|
| 81 |
+
function=self.func,
|
| 82 |
+
info_arg=_decorators.inspect_annotated_serializer(self.func, 'plain'),
|
| 83 |
+
return_schema=return_schema,
|
| 84 |
+
when_used=self.when_used,
|
| 85 |
+
)
|
| 86 |
+
return schema
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True)
|
| 90 |
+
class WrapSerializer:
|
| 91 |
+
"""Wrap serializers receive the raw inputs along with a handler function that applies the standard serialization
|
| 92 |
+
logic, and can modify the resulting value before returning it as the final output of serialization.
|
| 93 |
+
|
| 94 |
+
For example, here's a scenario in which a wrap serializer transforms timezones to UTC **and** utilizes the existing `datetime` serialization logic.
|
| 95 |
+
|
| 96 |
+
```python
|
| 97 |
+
from datetime import datetime, timezone
|
| 98 |
+
from typing import Annotated, Any
|
| 99 |
+
|
| 100 |
+
from pydantic import BaseModel, WrapSerializer
|
| 101 |
+
|
| 102 |
+
class EventDatetime(BaseModel):
|
| 103 |
+
start: datetime
|
| 104 |
+
end: datetime
|
| 105 |
+
|
| 106 |
+
def convert_to_utc(value: Any, handler, info) -> dict[str, datetime]:
|
| 107 |
+
# Note that `handler` can actually help serialize the `value` for
|
| 108 |
+
# further custom serialization in case it's a subclass.
|
| 109 |
+
partial_result = handler(value, info)
|
| 110 |
+
if info.mode == 'json':
|
| 111 |
+
return {
|
| 112 |
+
k: datetime.fromisoformat(v).astimezone(timezone.utc)
|
| 113 |
+
for k, v in partial_result.items()
|
| 114 |
+
}
|
| 115 |
+
return {k: v.astimezone(timezone.utc) for k, v in partial_result.items()}
|
| 116 |
+
|
| 117 |
+
UTCEventDatetime = Annotated[EventDatetime, WrapSerializer(convert_to_utc)]
|
| 118 |
+
|
| 119 |
+
class EventModel(BaseModel):
|
| 120 |
+
event_datetime: UTCEventDatetime
|
| 121 |
+
|
| 122 |
+
dt = EventDatetime(
|
| 123 |
+
start='2024-01-01T07:00:00-08:00', end='2024-01-03T20:00:00+06:00'
|
| 124 |
+
)
|
| 125 |
+
event = EventModel(event_datetime=dt)
|
| 126 |
+
print(event.model_dump())
|
| 127 |
+
'''
|
| 128 |
+
{
|
| 129 |
+
'event_datetime': {
|
| 130 |
+
'start': datetime.datetime(
|
| 131 |
+
2024, 1, 1, 15, 0, tzinfo=datetime.timezone.utc
|
| 132 |
+
),
|
| 133 |
+
'end': datetime.datetime(
|
| 134 |
+
2024, 1, 3, 14, 0, tzinfo=datetime.timezone.utc
|
| 135 |
+
),
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
'''
|
| 139 |
+
|
| 140 |
+
print(event.model_dump_json())
|
| 141 |
+
'''
|
| 142 |
+
{"event_datetime":{"start":"2024-01-01T15:00:00Z","end":"2024-01-03T14:00:00Z"}}
|
| 143 |
+
'''
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
Attributes:
|
| 147 |
+
func: The serializer function to be wrapped.
|
| 148 |
+
return_type: The return type for the function. If omitted it will be inferred from the type annotation.
|
| 149 |
+
when_used: Determines when this serializer should be used. Accepts a string with values `'always'`,
|
| 150 |
+
`'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'.
|
| 151 |
+
"""
|
| 152 |
+
|
| 153 |
+
func: core_schema.WrapSerializerFunction
|
| 154 |
+
return_type: Any = PydanticUndefined
|
| 155 |
+
when_used: WhenUsed = 'always'
|
| 156 |
+
|
| 157 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 158 |
+
"""This method is used to get the Pydantic core schema of the class.
|
| 159 |
+
|
| 160 |
+
Args:
|
| 161 |
+
source_type: Source type.
|
| 162 |
+
handler: Core schema handler.
|
| 163 |
+
|
| 164 |
+
Returns:
|
| 165 |
+
The generated core schema of the class.
|
| 166 |
+
"""
|
| 167 |
+
schema = handler(source_type)
|
| 168 |
+
if self.return_type is not PydanticUndefined:
|
| 169 |
+
return_type = self.return_type
|
| 170 |
+
else:
|
| 171 |
+
try:
|
| 172 |
+
# Do not pass in globals as the function could be defined in a different module.
|
| 173 |
+
# Instead, let `get_callable_return_type` infer the globals to use, but still pass
|
| 174 |
+
# in locals that may contain a parent/rebuild namespace:
|
| 175 |
+
return_type = _decorators.get_callable_return_type(
|
| 176 |
+
self.func,
|
| 177 |
+
localns=handler._get_types_namespace().locals,
|
| 178 |
+
)
|
| 179 |
+
except NameError as e:
|
| 180 |
+
raise PydanticUndefinedAnnotation.from_name_error(e) from e
|
| 181 |
+
|
| 182 |
+
return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type)
|
| 183 |
+
schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
|
| 184 |
+
function=self.func,
|
| 185 |
+
info_arg=_decorators.inspect_annotated_serializer(self.func, 'wrap'),
|
| 186 |
+
return_schema=return_schema,
|
| 187 |
+
when_used=self.when_used,
|
| 188 |
+
)
|
| 189 |
+
return schema
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
if TYPE_CHECKING:
|
| 193 |
+
_Partial: TypeAlias = 'partial[Any] | partialmethod[Any]'
|
| 194 |
+
|
| 195 |
+
FieldPlainSerializer: TypeAlias = 'core_schema.SerializerFunction | _Partial'
|
| 196 |
+
"""A field serializer method or function in `plain` mode."""
|
| 197 |
+
|
| 198 |
+
FieldWrapSerializer: TypeAlias = 'core_schema.WrapSerializerFunction | _Partial'
|
| 199 |
+
"""A field serializer method or function in `wrap` mode."""
|
| 200 |
+
|
| 201 |
+
FieldSerializer: TypeAlias = 'FieldPlainSerializer | FieldWrapSerializer'
|
| 202 |
+
"""A field serializer method or function."""
|
| 203 |
+
|
| 204 |
+
_FieldPlainSerializerT = TypeVar('_FieldPlainSerializerT', bound=FieldPlainSerializer)
|
| 205 |
+
_FieldWrapSerializerT = TypeVar('_FieldWrapSerializerT', bound=FieldWrapSerializer)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
@overload
|
| 209 |
+
def field_serializer(
|
| 210 |
+
field: str,
|
| 211 |
+
/,
|
| 212 |
+
*fields: str,
|
| 213 |
+
mode: Literal['wrap'],
|
| 214 |
+
return_type: Any = ...,
|
| 215 |
+
when_used: WhenUsed = ...,
|
| 216 |
+
check_fields: bool | None = ...,
|
| 217 |
+
) -> Callable[[_FieldWrapSerializerT], _FieldWrapSerializerT]: ...
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
@overload
|
| 221 |
+
def field_serializer(
|
| 222 |
+
field: str,
|
| 223 |
+
/,
|
| 224 |
+
*fields: str,
|
| 225 |
+
mode: Literal['plain'] = ...,
|
| 226 |
+
return_type: Any = ...,
|
| 227 |
+
when_used: WhenUsed = ...,
|
| 228 |
+
check_fields: bool | None = ...,
|
| 229 |
+
) -> Callable[[_FieldPlainSerializerT], _FieldPlainSerializerT]: ...
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def field_serializer( # noqa: D417
|
| 233 |
+
field: str,
|
| 234 |
+
/,
|
| 235 |
+
*fields: str,
|
| 236 |
+
mode: Literal['plain', 'wrap'] = 'plain',
|
| 237 |
+
# TODO PEP 747 (grep for 'return_type' on the whole code base):
|
| 238 |
+
return_type: Any = PydanticUndefined,
|
| 239 |
+
when_used: WhenUsed = 'always',
|
| 240 |
+
check_fields: bool | None = None,
|
| 241 |
+
) -> (
|
| 242 |
+
Callable[[_FieldWrapSerializerT], _FieldWrapSerializerT]
|
| 243 |
+
| Callable[[_FieldPlainSerializerT], _FieldPlainSerializerT]
|
| 244 |
+
):
|
| 245 |
+
"""Decorator that enables custom field serialization.
|
| 246 |
+
|
| 247 |
+
In the below example, a field of type `set` is used to mitigate duplication. A `field_serializer` is used to serialize the data as a sorted list.
|
| 248 |
+
|
| 249 |
+
```python
|
| 250 |
+
from pydantic import BaseModel, field_serializer
|
| 251 |
+
|
| 252 |
+
class StudentModel(BaseModel):
|
| 253 |
+
name: str = 'Jane'
|
| 254 |
+
courses: set[str]
|
| 255 |
+
|
| 256 |
+
@field_serializer('courses', when_used='json')
|
| 257 |
+
def serialize_courses_in_order(self, courses: set[str]):
|
| 258 |
+
return sorted(courses)
|
| 259 |
+
|
| 260 |
+
student = StudentModel(courses={'Math', 'Chemistry', 'English'})
|
| 261 |
+
print(student.model_dump_json())
|
| 262 |
+
#> {"name":"Jane","courses":["Chemistry","English","Math"]}
|
| 263 |
+
```
|
| 264 |
+
|
| 265 |
+
See [the usage documentation](../concepts/serialization.md#serializers) for more information.
|
| 266 |
+
|
| 267 |
+
Four signatures are supported for the decorated serializer:
|
| 268 |
+
|
| 269 |
+
- `(self, value: Any, info: FieldSerializationInfo)`
|
| 270 |
+
- `(self, value: Any, nxt: SerializerFunctionWrapHandler, info: FieldSerializationInfo)`
|
| 271 |
+
- `(value: Any, info: SerializationInfo)`
|
| 272 |
+
- `(value: Any, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)`
|
| 273 |
+
|
| 274 |
+
Args:
|
| 275 |
+
*fields: The field names the serializer should apply to.
|
| 276 |
+
mode: The serialization mode.
|
| 277 |
+
|
| 278 |
+
- `plain` means the function will be called instead of the default serialization logic,
|
| 279 |
+
- `wrap` means the function will be called with an argument to optionally call the
|
| 280 |
+
default serialization logic.
|
| 281 |
+
return_type: Optional return type for the function, if omitted it will be inferred from the type annotation.
|
| 282 |
+
when_used: Determines the serializer will be used for serialization.
|
| 283 |
+
check_fields: Whether to check that the fields actually exist on the model.
|
| 284 |
+
|
| 285 |
+
Raises:
|
| 286 |
+
PydanticUserError:
|
| 287 |
+
- If the decorator is used without any arguments (at least one field name must be provided).
|
| 288 |
+
- If the provided field names are not strings.
|
| 289 |
+
"""
|
| 290 |
+
if callable(field) or isinstance(field, classmethod):
|
| 291 |
+
raise PydanticUserError(
|
| 292 |
+
'The `@field_serializer` decorator cannot be used without arguments, at least one field must be provided. '
|
| 293 |
+
"For example: `@field_serializer('<field_name>', ...)`.",
|
| 294 |
+
code='decorator-missing-arguments',
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
fields = field, *fields
|
| 298 |
+
if not all(isinstance(field, str) for field in fields):
|
| 299 |
+
raise PydanticUserError(
|
| 300 |
+
'The provided field names to the `@field_serializer` decorator should be strings. '
|
| 301 |
+
"For example: `@field_serializer('<field_name_1>', '<field_name_2>', ...).`",
|
| 302 |
+
code='decorator-invalid-fields',
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
def dec(f: FieldSerializer) -> _decorators.PydanticDescriptorProxy[Any]:
|
| 306 |
+
dec_info = _decorators.FieldSerializerDecoratorInfo(
|
| 307 |
+
fields=fields,
|
| 308 |
+
mode=mode,
|
| 309 |
+
return_type=return_type,
|
| 310 |
+
when_used=when_used,
|
| 311 |
+
check_fields=check_fields,
|
| 312 |
+
)
|
| 313 |
+
return _decorators.PydanticDescriptorProxy(f, dec_info) # pyright: ignore[reportArgumentType]
|
| 314 |
+
|
| 315 |
+
return dec # pyright: ignore[reportReturnType]
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
if TYPE_CHECKING:
|
| 319 |
+
# The first argument in the following callables represent the `self` type:
|
| 320 |
+
|
| 321 |
+
ModelPlainSerializerWithInfo: TypeAlias = Callable[[Any, SerializationInfo[Any]], Any]
|
| 322 |
+
"""A model serializer method with the `info` argument, in `plain` mode."""
|
| 323 |
+
|
| 324 |
+
ModelPlainSerializerWithoutInfo: TypeAlias = Callable[[Any], Any]
|
| 325 |
+
"""A model serializer method without the `info` argument, in `plain` mode."""
|
| 326 |
+
|
| 327 |
+
ModelPlainSerializer: TypeAlias = 'ModelPlainSerializerWithInfo | ModelPlainSerializerWithoutInfo'
|
| 328 |
+
"""A model serializer method in `plain` mode."""
|
| 329 |
+
|
| 330 |
+
ModelWrapSerializerWithInfo: TypeAlias = Callable[[Any, SerializerFunctionWrapHandler, SerializationInfo[Any]], Any]
|
| 331 |
+
"""A model serializer method with the `info` argument, in `wrap` mode."""
|
| 332 |
+
|
| 333 |
+
ModelWrapSerializerWithoutInfo: TypeAlias = Callable[[Any, SerializerFunctionWrapHandler], Any]
|
| 334 |
+
"""A model serializer method without the `info` argument, in `wrap` mode."""
|
| 335 |
+
|
| 336 |
+
ModelWrapSerializer: TypeAlias = 'ModelWrapSerializerWithInfo | ModelWrapSerializerWithoutInfo'
|
| 337 |
+
"""A model serializer method in `wrap` mode."""
|
| 338 |
+
|
| 339 |
+
ModelSerializer: TypeAlias = 'ModelPlainSerializer | ModelWrapSerializer'
|
| 340 |
+
|
| 341 |
+
_ModelPlainSerializerT = TypeVar('_ModelPlainSerializerT', bound=ModelPlainSerializer)
|
| 342 |
+
_ModelWrapSerializerT = TypeVar('_ModelWrapSerializerT', bound=ModelWrapSerializer)
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
@overload
|
| 346 |
+
def model_serializer(f: _ModelPlainSerializerT, /) -> _ModelPlainSerializerT: ...
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
@overload
|
| 350 |
+
def model_serializer(
|
| 351 |
+
*, mode: Literal['wrap'], when_used: WhenUsed = 'always', return_type: Any = ...
|
| 352 |
+
) -> Callable[[_ModelWrapSerializerT], _ModelWrapSerializerT]: ...
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
@overload
|
| 356 |
+
def model_serializer(
|
| 357 |
+
*,
|
| 358 |
+
mode: Literal['plain'] = ...,
|
| 359 |
+
when_used: WhenUsed = 'always',
|
| 360 |
+
return_type: Any = ...,
|
| 361 |
+
) -> Callable[[_ModelPlainSerializerT], _ModelPlainSerializerT]: ...
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def model_serializer(
|
| 365 |
+
f: _ModelPlainSerializerT | _ModelWrapSerializerT | None = None,
|
| 366 |
+
/,
|
| 367 |
+
*,
|
| 368 |
+
mode: Literal['plain', 'wrap'] = 'plain',
|
| 369 |
+
when_used: WhenUsed = 'always',
|
| 370 |
+
return_type: Any = PydanticUndefined,
|
| 371 |
+
) -> (
|
| 372 |
+
_ModelPlainSerializerT
|
| 373 |
+
| Callable[[_ModelWrapSerializerT], _ModelWrapSerializerT]
|
| 374 |
+
| Callable[[_ModelPlainSerializerT], _ModelPlainSerializerT]
|
| 375 |
+
):
|
| 376 |
+
"""Decorator that enables custom model serialization.
|
| 377 |
+
|
| 378 |
+
This is useful when a model need to be serialized in a customized manner, allowing for flexibility beyond just specific fields.
|
| 379 |
+
|
| 380 |
+
An example would be to serialize temperature to the same temperature scale, such as degrees Celsius.
|
| 381 |
+
|
| 382 |
+
```python
|
| 383 |
+
from typing import Literal
|
| 384 |
+
|
| 385 |
+
from pydantic import BaseModel, model_serializer
|
| 386 |
+
|
| 387 |
+
class TemperatureModel(BaseModel):
|
| 388 |
+
unit: Literal['C', 'F']
|
| 389 |
+
value: int
|
| 390 |
+
|
| 391 |
+
@model_serializer()
|
| 392 |
+
def serialize_model(self):
|
| 393 |
+
if self.unit == 'F':
|
| 394 |
+
return {'unit': 'C', 'value': int((self.value - 32) / 1.8)}
|
| 395 |
+
return {'unit': self.unit, 'value': self.value}
|
| 396 |
+
|
| 397 |
+
temperature = TemperatureModel(unit='F', value=212)
|
| 398 |
+
print(temperature.model_dump())
|
| 399 |
+
#> {'unit': 'C', 'value': 100}
|
| 400 |
+
```
|
| 401 |
+
|
| 402 |
+
Two signatures are supported for `mode='plain'`, which is the default:
|
| 403 |
+
|
| 404 |
+
- `(self)`
|
| 405 |
+
- `(self, info: SerializationInfo)`
|
| 406 |
+
|
| 407 |
+
And two other signatures for `mode='wrap'`:
|
| 408 |
+
|
| 409 |
+
- `(self, nxt: SerializerFunctionWrapHandler)`
|
| 410 |
+
- `(self, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)`
|
| 411 |
+
|
| 412 |
+
See [the usage documentation](../concepts/serialization.md#serializers) for more information.
|
| 413 |
+
|
| 414 |
+
Args:
|
| 415 |
+
f: The function to be decorated.
|
| 416 |
+
mode: The serialization mode.
|
| 417 |
+
|
| 418 |
+
- `'plain'` means the function will be called instead of the default serialization logic
|
| 419 |
+
- `'wrap'` means the function will be called with an argument to optionally call the default
|
| 420 |
+
serialization logic.
|
| 421 |
+
when_used: Determines when this serializer should be used.
|
| 422 |
+
return_type: The return type for the function. If omitted it will be inferred from the type annotation.
|
| 423 |
+
|
| 424 |
+
Returns:
|
| 425 |
+
The decorator function.
|
| 426 |
+
"""
|
| 427 |
+
|
| 428 |
+
def dec(f: ModelSerializer) -> _decorators.PydanticDescriptorProxy[Any]:
|
| 429 |
+
dec_info = _decorators.ModelSerializerDecoratorInfo(mode=mode, return_type=return_type, when_used=when_used)
|
| 430 |
+
return _decorators.PydanticDescriptorProxy(f, dec_info)
|
| 431 |
+
|
| 432 |
+
if f is None:
|
| 433 |
+
return dec # pyright: ignore[reportReturnType]
|
| 434 |
+
else:
|
| 435 |
+
return dec(f) # pyright: ignore[reportReturnType]
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
AnyType = TypeVar('AnyType')
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
if TYPE_CHECKING:
|
| 442 |
+
SerializeAsAny = Annotated[AnyType, ...] # SerializeAsAny[list[str]] will be treated by type checkers as list[str]
|
| 443 |
+
"""Annotation used to mark a type as having duck-typing serialization behavior.
|
| 444 |
+
|
| 445 |
+
See [usage documentation](../concepts/serialization.md#serializing-with-duck-typing) for more details.
|
| 446 |
+
"""
|
| 447 |
+
else:
|
| 448 |
+
|
| 449 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 450 |
+
class SerializeAsAny:
|
| 451 |
+
"""Annotation used to mark a type as having duck-typing serialization behavior.
|
| 452 |
+
|
| 453 |
+
See [usage documentation](../concepts/serialization.md#serializing-with-duck-typing) for more details.
|
| 454 |
+
"""
|
| 455 |
+
|
| 456 |
+
def __class_getitem__(cls, item: Any) -> Any:
|
| 457 |
+
return Annotated[item, SerializeAsAny()]
|
| 458 |
+
|
| 459 |
+
def __get_pydantic_core_schema__(
|
| 460 |
+
self, source_type: Any, handler: GetCoreSchemaHandler
|
| 461 |
+
) -> core_schema.CoreSchema:
|
| 462 |
+
schema = handler(source_type)
|
| 463 |
+
schema_to_update = schema
|
| 464 |
+
while schema_to_update['type'] == 'definitions':
|
| 465 |
+
schema_to_update = schema_to_update.copy()
|
| 466 |
+
schema_to_update = schema_to_update['schema']
|
| 467 |
+
schema_to_update['serialization'] = core_schema.simple_ser_schema('any')
|
| 468 |
+
return schema
|
| 469 |
+
|
| 470 |
+
__hash__ = object.__hash__
|
.mcp-venv/lib/python3.13/site-packages/pydantic/functional_validators.py
ADDED
|
@@ -0,0 +1,889 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""This module contains related classes and functions for validation."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations as _annotations
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
import sys
|
| 7 |
+
import warnings
|
| 8 |
+
from functools import partialmethod
|
| 9 |
+
from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, Union, cast, overload
|
| 10 |
+
|
| 11 |
+
from pydantic_core import PydanticUndefined, core_schema
|
| 12 |
+
from typing_extensions import Self, TypeAlias
|
| 13 |
+
|
| 14 |
+
from ._internal import _decorators, _generics, _internal_dataclass
|
| 15 |
+
from .annotated_handlers import GetCoreSchemaHandler
|
| 16 |
+
from .errors import PydanticUserError
|
| 17 |
+
from .version import version_short
|
| 18 |
+
from .warnings import ArbitraryTypeWarning, PydanticDeprecatedSince212
|
| 19 |
+
|
| 20 |
+
if sys.version_info < (3, 11):
|
| 21 |
+
from typing_extensions import Protocol
|
| 22 |
+
else:
|
| 23 |
+
from typing import Protocol
|
| 24 |
+
|
| 25 |
+
_inspect_validator = _decorators.inspect_validator
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
|
| 29 |
+
class AfterValidator:
|
| 30 |
+
"""!!! abstract "Usage Documentation"
|
| 31 |
+
[field *after* validators](../concepts/validators.md#field-after-validator)
|
| 32 |
+
|
| 33 |
+
A metadata class that indicates that a validation should be applied **after** the inner validation logic.
|
| 34 |
+
|
| 35 |
+
Attributes:
|
| 36 |
+
func: The validator function.
|
| 37 |
+
|
| 38 |
+
Example:
|
| 39 |
+
```python
|
| 40 |
+
from typing import Annotated
|
| 41 |
+
|
| 42 |
+
from pydantic import AfterValidator, BaseModel, ValidationError
|
| 43 |
+
|
| 44 |
+
MyInt = Annotated[int, AfterValidator(lambda v: v + 1)]
|
| 45 |
+
|
| 46 |
+
class Model(BaseModel):
|
| 47 |
+
a: MyInt
|
| 48 |
+
|
| 49 |
+
print(Model(a=1).a)
|
| 50 |
+
#> 2
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
Model(a='a')
|
| 54 |
+
except ValidationError as e:
|
| 55 |
+
print(e.json(indent=2))
|
| 56 |
+
'''
|
| 57 |
+
[
|
| 58 |
+
{
|
| 59 |
+
"type": "int_parsing",
|
| 60 |
+
"loc": [
|
| 61 |
+
"a"
|
| 62 |
+
],
|
| 63 |
+
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
| 64 |
+
"input": "a",
|
| 65 |
+
"url": "https://errors.pydantic.dev/2/v/int_parsing"
|
| 66 |
+
}
|
| 67 |
+
]
|
| 68 |
+
'''
|
| 69 |
+
```
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
|
| 73 |
+
|
| 74 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 75 |
+
schema = handler(source_type)
|
| 76 |
+
info_arg = _inspect_validator(self.func, mode='after', type='field')
|
| 77 |
+
if info_arg:
|
| 78 |
+
func = cast(core_schema.WithInfoValidatorFunction, self.func)
|
| 79 |
+
return core_schema.with_info_after_validator_function(func, schema=schema)
|
| 80 |
+
else:
|
| 81 |
+
func = cast(core_schema.NoInfoValidatorFunction, self.func)
|
| 82 |
+
return core_schema.no_info_after_validator_function(func, schema=schema)
|
| 83 |
+
|
| 84 |
+
@classmethod
|
| 85 |
+
def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
|
| 86 |
+
return cls(func=decorator.func)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
|
| 90 |
+
class BeforeValidator:
|
| 91 |
+
"""!!! abstract "Usage Documentation"
|
| 92 |
+
[field *before* validators](../concepts/validators.md#field-before-validator)
|
| 93 |
+
|
| 94 |
+
A metadata class that indicates that a validation should be applied **before** the inner validation logic.
|
| 95 |
+
|
| 96 |
+
Attributes:
|
| 97 |
+
func: The validator function.
|
| 98 |
+
json_schema_input_type: The input type used to generate the appropriate
|
| 99 |
+
JSON Schema (in validation mode). The actual input type is `Any`.
|
| 100 |
+
|
| 101 |
+
Example:
|
| 102 |
+
```python
|
| 103 |
+
from typing import Annotated
|
| 104 |
+
|
| 105 |
+
from pydantic import BaseModel, BeforeValidator
|
| 106 |
+
|
| 107 |
+
MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)]
|
| 108 |
+
|
| 109 |
+
class Model(BaseModel):
|
| 110 |
+
a: MyInt
|
| 111 |
+
|
| 112 |
+
print(Model(a=1).a)
|
| 113 |
+
#> 2
|
| 114 |
+
|
| 115 |
+
try:
|
| 116 |
+
Model(a='a')
|
| 117 |
+
except TypeError as e:
|
| 118 |
+
print(e)
|
| 119 |
+
#> can only concatenate str (not "int") to str
|
| 120 |
+
```
|
| 121 |
+
"""
|
| 122 |
+
|
| 123 |
+
func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
|
| 124 |
+
json_schema_input_type: Any = PydanticUndefined
|
| 125 |
+
|
| 126 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 127 |
+
schema = handler(source_type)
|
| 128 |
+
input_schema = (
|
| 129 |
+
None
|
| 130 |
+
if self.json_schema_input_type is PydanticUndefined
|
| 131 |
+
else handler.generate_schema(self.json_schema_input_type)
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
info_arg = _inspect_validator(self.func, mode='before', type='field')
|
| 135 |
+
if info_arg:
|
| 136 |
+
func = cast(core_schema.WithInfoValidatorFunction, self.func)
|
| 137 |
+
return core_schema.with_info_before_validator_function(
|
| 138 |
+
func,
|
| 139 |
+
schema=schema,
|
| 140 |
+
json_schema_input_schema=input_schema,
|
| 141 |
+
)
|
| 142 |
+
else:
|
| 143 |
+
func = cast(core_schema.NoInfoValidatorFunction, self.func)
|
| 144 |
+
return core_schema.no_info_before_validator_function(
|
| 145 |
+
func, schema=schema, json_schema_input_schema=input_schema
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
@classmethod
|
| 149 |
+
def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
|
| 150 |
+
return cls(
|
| 151 |
+
func=decorator.func,
|
| 152 |
+
json_schema_input_type=decorator.info.json_schema_input_type,
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
|
| 157 |
+
class PlainValidator:
|
| 158 |
+
"""!!! abstract "Usage Documentation"
|
| 159 |
+
[field *plain* validators](../concepts/validators.md#field-plain-validator)
|
| 160 |
+
|
| 161 |
+
A metadata class that indicates that a validation should be applied **instead** of the inner validation logic.
|
| 162 |
+
|
| 163 |
+
!!! note
|
| 164 |
+
Before v2.9, `PlainValidator` wasn't always compatible with JSON Schema generation for `mode='validation'`.
|
| 165 |
+
You can now use the `json_schema_input_type` argument to specify the input type of the function
|
| 166 |
+
to be used in the JSON schema when `mode='validation'` (the default). See the example below for more details.
|
| 167 |
+
|
| 168 |
+
Attributes:
|
| 169 |
+
func: The validator function.
|
| 170 |
+
json_schema_input_type: The input type used to generate the appropriate
|
| 171 |
+
JSON Schema (in validation mode). The actual input type is `Any`.
|
| 172 |
+
|
| 173 |
+
Example:
|
| 174 |
+
```python
|
| 175 |
+
from typing import Annotated, Union
|
| 176 |
+
|
| 177 |
+
from pydantic import BaseModel, PlainValidator
|
| 178 |
+
|
| 179 |
+
def validate(v: object) -> int:
|
| 180 |
+
if not isinstance(v, (int, str)):
|
| 181 |
+
raise ValueError(f'Expected int or str, got {type(v)}')
|
| 182 |
+
|
| 183 |
+
return int(v) + 1
|
| 184 |
+
|
| 185 |
+
MyInt = Annotated[
|
| 186 |
+
int,
|
| 187 |
+
PlainValidator(validate, json_schema_input_type=Union[str, int]), # (1)!
|
| 188 |
+
]
|
| 189 |
+
|
| 190 |
+
class Model(BaseModel):
|
| 191 |
+
a: MyInt
|
| 192 |
+
|
| 193 |
+
print(Model(a='1').a)
|
| 194 |
+
#> 2
|
| 195 |
+
|
| 196 |
+
print(Model(a=1).a)
|
| 197 |
+
#> 2
|
| 198 |
+
```
|
| 199 |
+
|
| 200 |
+
1. In this example, we've specified the `json_schema_input_type` as `Union[str, int]` which indicates to the JSON schema
|
| 201 |
+
generator that in validation mode, the input type for the `a` field can be either a [`str`][] or an [`int`][].
|
| 202 |
+
"""
|
| 203 |
+
|
| 204 |
+
func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
|
| 205 |
+
json_schema_input_type: Any = Any
|
| 206 |
+
|
| 207 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 208 |
+
# Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the
|
| 209 |
+
# source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper
|
| 210 |
+
# serialization schema. To work around this for use cases that will not involve serialization, we simply
|
| 211 |
+
# catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema
|
| 212 |
+
# and abort any attempts to handle special serialization.
|
| 213 |
+
from pydantic import PydanticSchemaGenerationError
|
| 214 |
+
|
| 215 |
+
try:
|
| 216 |
+
schema = handler(source_type)
|
| 217 |
+
# TODO if `schema['serialization']` is one of `'include-exclude-dict/sequence',
|
| 218 |
+
# schema validation will fail. That's why we use 'type ignore' comments below.
|
| 219 |
+
serialization = schema.get(
|
| 220 |
+
'serialization',
|
| 221 |
+
core_schema.wrap_serializer_function_ser_schema(
|
| 222 |
+
function=lambda v, h: h(v),
|
| 223 |
+
schema=schema,
|
| 224 |
+
return_schema=handler.generate_schema(source_type),
|
| 225 |
+
),
|
| 226 |
+
)
|
| 227 |
+
except PydanticSchemaGenerationError:
|
| 228 |
+
serialization = None
|
| 229 |
+
|
| 230 |
+
input_schema = handler.generate_schema(self.json_schema_input_type)
|
| 231 |
+
|
| 232 |
+
info_arg = _inspect_validator(self.func, mode='plain', type='field')
|
| 233 |
+
if info_arg:
|
| 234 |
+
func = cast(core_schema.WithInfoValidatorFunction, self.func)
|
| 235 |
+
return core_schema.with_info_plain_validator_function(
|
| 236 |
+
func,
|
| 237 |
+
serialization=serialization, # pyright: ignore[reportArgumentType]
|
| 238 |
+
json_schema_input_schema=input_schema,
|
| 239 |
+
)
|
| 240 |
+
else:
|
| 241 |
+
func = cast(core_schema.NoInfoValidatorFunction, self.func)
|
| 242 |
+
return core_schema.no_info_plain_validator_function(
|
| 243 |
+
func,
|
| 244 |
+
serialization=serialization, # pyright: ignore[reportArgumentType]
|
| 245 |
+
json_schema_input_schema=input_schema,
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
@classmethod
|
| 249 |
+
def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
|
| 250 |
+
return cls(
|
| 251 |
+
func=decorator.func,
|
| 252 |
+
json_schema_input_type=decorator.info.json_schema_input_type,
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
|
| 257 |
+
class WrapValidator:
|
| 258 |
+
"""!!! abstract "Usage Documentation"
|
| 259 |
+
[field *wrap* validators](../concepts/validators.md#field-wrap-validator)
|
| 260 |
+
|
| 261 |
+
A metadata class that indicates that a validation should be applied **around** the inner validation logic.
|
| 262 |
+
|
| 263 |
+
Attributes:
|
| 264 |
+
func: The validator function.
|
| 265 |
+
json_schema_input_type: The input type used to generate the appropriate
|
| 266 |
+
JSON Schema (in validation mode). The actual input type is `Any`.
|
| 267 |
+
|
| 268 |
+
```python
|
| 269 |
+
from datetime import datetime
|
| 270 |
+
from typing import Annotated
|
| 271 |
+
|
| 272 |
+
from pydantic import BaseModel, ValidationError, WrapValidator
|
| 273 |
+
|
| 274 |
+
def validate_timestamp(v, handler):
|
| 275 |
+
if v == 'now':
|
| 276 |
+
# we don't want to bother with further validation, just return the new value
|
| 277 |
+
return datetime.now()
|
| 278 |
+
try:
|
| 279 |
+
return handler(v)
|
| 280 |
+
except ValidationError:
|
| 281 |
+
# validation failed, in this case we want to return a default value
|
| 282 |
+
return datetime(2000, 1, 1)
|
| 283 |
+
|
| 284 |
+
MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)]
|
| 285 |
+
|
| 286 |
+
class Model(BaseModel):
|
| 287 |
+
a: MyTimestamp
|
| 288 |
+
|
| 289 |
+
print(Model(a='now').a)
|
| 290 |
+
#> 2032-01-02 03:04:05.000006
|
| 291 |
+
print(Model(a='invalid').a)
|
| 292 |
+
#> 2000-01-01 00:00:00
|
| 293 |
+
```
|
| 294 |
+
"""
|
| 295 |
+
|
| 296 |
+
func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction
|
| 297 |
+
json_schema_input_type: Any = PydanticUndefined
|
| 298 |
+
|
| 299 |
+
def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 300 |
+
schema = handler(source_type)
|
| 301 |
+
input_schema = (
|
| 302 |
+
None
|
| 303 |
+
if self.json_schema_input_type is PydanticUndefined
|
| 304 |
+
else handler.generate_schema(self.json_schema_input_type)
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
info_arg = _inspect_validator(self.func, mode='wrap', type='field')
|
| 308 |
+
if info_arg:
|
| 309 |
+
func = cast(core_schema.WithInfoWrapValidatorFunction, self.func)
|
| 310 |
+
return core_schema.with_info_wrap_validator_function(
|
| 311 |
+
func,
|
| 312 |
+
schema=schema,
|
| 313 |
+
json_schema_input_schema=input_schema,
|
| 314 |
+
)
|
| 315 |
+
else:
|
| 316 |
+
func = cast(core_schema.NoInfoWrapValidatorFunction, self.func)
|
| 317 |
+
return core_schema.no_info_wrap_validator_function(
|
| 318 |
+
func,
|
| 319 |
+
schema=schema,
|
| 320 |
+
json_schema_input_schema=input_schema,
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
@classmethod
|
| 324 |
+
def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
|
| 325 |
+
return cls(
|
| 326 |
+
func=decorator.func,
|
| 327 |
+
json_schema_input_type=decorator.info.json_schema_input_type,
|
| 328 |
+
)
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
if TYPE_CHECKING:
|
| 332 |
+
|
| 333 |
+
class _OnlyValueValidatorClsMethod(Protocol):
|
| 334 |
+
def __call__(self, cls: Any, value: Any, /) -> Any: ...
|
| 335 |
+
|
| 336 |
+
class _V2ValidatorClsMethod(Protocol):
|
| 337 |
+
def __call__(self, cls: Any, value: Any, info: core_schema.ValidationInfo[Any], /) -> Any: ...
|
| 338 |
+
|
| 339 |
+
class _OnlyValueWrapValidatorClsMethod(Protocol):
|
| 340 |
+
def __call__(self, cls: Any, value: Any, handler: core_schema.ValidatorFunctionWrapHandler, /) -> Any: ...
|
| 341 |
+
|
| 342 |
+
class _V2WrapValidatorClsMethod(Protocol):
|
| 343 |
+
def __call__(
|
| 344 |
+
self,
|
| 345 |
+
cls: Any,
|
| 346 |
+
value: Any,
|
| 347 |
+
handler: core_schema.ValidatorFunctionWrapHandler,
|
| 348 |
+
info: core_schema.ValidationInfo[Any],
|
| 349 |
+
/,
|
| 350 |
+
) -> Any: ...
|
| 351 |
+
|
| 352 |
+
_V2Validator = Union[
|
| 353 |
+
_V2ValidatorClsMethod,
|
| 354 |
+
core_schema.WithInfoValidatorFunction,
|
| 355 |
+
_OnlyValueValidatorClsMethod,
|
| 356 |
+
core_schema.NoInfoValidatorFunction,
|
| 357 |
+
]
|
| 358 |
+
|
| 359 |
+
_V2WrapValidator = Union[
|
| 360 |
+
_V2WrapValidatorClsMethod,
|
| 361 |
+
core_schema.WithInfoWrapValidatorFunction,
|
| 362 |
+
_OnlyValueWrapValidatorClsMethod,
|
| 363 |
+
core_schema.NoInfoWrapValidatorFunction,
|
| 364 |
+
]
|
| 365 |
+
|
| 366 |
+
_PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
|
| 367 |
+
|
| 368 |
+
_V2BeforeAfterOrPlainValidatorType = TypeVar(
|
| 369 |
+
'_V2BeforeAfterOrPlainValidatorType',
|
| 370 |
+
bound=Union[_V2Validator, _PartialClsOrStaticMethod],
|
| 371 |
+
)
|
| 372 |
+
_V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=Union[_V2WrapValidator, _PartialClsOrStaticMethod])
|
| 373 |
+
|
| 374 |
+
FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain']
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
@overload
|
| 378 |
+
def field_validator(
|
| 379 |
+
field: str,
|
| 380 |
+
/,
|
| 381 |
+
*fields: str,
|
| 382 |
+
mode: Literal['wrap'],
|
| 383 |
+
check_fields: bool | None = ...,
|
| 384 |
+
json_schema_input_type: Any = ...,
|
| 385 |
+
) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: ...
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
@overload
|
| 389 |
+
def field_validator(
|
| 390 |
+
field: str,
|
| 391 |
+
/,
|
| 392 |
+
*fields: str,
|
| 393 |
+
mode: Literal['before', 'plain'],
|
| 394 |
+
check_fields: bool | None = ...,
|
| 395 |
+
json_schema_input_type: Any = ...,
|
| 396 |
+
) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
@overload
|
| 400 |
+
def field_validator(
|
| 401 |
+
field: str,
|
| 402 |
+
/,
|
| 403 |
+
*fields: str,
|
| 404 |
+
mode: Literal['after'] = ...,
|
| 405 |
+
check_fields: bool | None = ...,
|
| 406 |
+
) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
def field_validator( # noqa: D417
|
| 410 |
+
field: str,
|
| 411 |
+
/,
|
| 412 |
+
*fields: str,
|
| 413 |
+
mode: FieldValidatorModes = 'after',
|
| 414 |
+
check_fields: bool | None = None,
|
| 415 |
+
json_schema_input_type: Any = PydanticUndefined,
|
| 416 |
+
) -> Callable[[Any], Any]:
|
| 417 |
+
"""!!! abstract "Usage Documentation"
|
| 418 |
+
[field validators](../concepts/validators.md#field-validators)
|
| 419 |
+
|
| 420 |
+
Decorate methods on the class indicating that they should be used to validate fields.
|
| 421 |
+
|
| 422 |
+
Example usage:
|
| 423 |
+
```python
|
| 424 |
+
from typing import Any
|
| 425 |
+
|
| 426 |
+
from pydantic import (
|
| 427 |
+
BaseModel,
|
| 428 |
+
ValidationError,
|
| 429 |
+
field_validator,
|
| 430 |
+
)
|
| 431 |
+
|
| 432 |
+
class Model(BaseModel):
|
| 433 |
+
a: str
|
| 434 |
+
|
| 435 |
+
@field_validator('a')
|
| 436 |
+
@classmethod
|
| 437 |
+
def ensure_foobar(cls, v: Any):
|
| 438 |
+
if 'foobar' not in v:
|
| 439 |
+
raise ValueError('"foobar" not found in a')
|
| 440 |
+
return v
|
| 441 |
+
|
| 442 |
+
print(repr(Model(a='this is foobar good')))
|
| 443 |
+
#> Model(a='this is foobar good')
|
| 444 |
+
|
| 445 |
+
try:
|
| 446 |
+
Model(a='snap')
|
| 447 |
+
except ValidationError as exc_info:
|
| 448 |
+
print(exc_info)
|
| 449 |
+
'''
|
| 450 |
+
1 validation error for Model
|
| 451 |
+
a
|
| 452 |
+
Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str]
|
| 453 |
+
'''
|
| 454 |
+
```
|
| 455 |
+
|
| 456 |
+
For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators).
|
| 457 |
+
|
| 458 |
+
Args:
|
| 459 |
+
*fields: The field names the validator should apply to.
|
| 460 |
+
mode: Specifies whether to validate the fields before or after validation.
|
| 461 |
+
check_fields: Whether to check that the fields actually exist on the model.
|
| 462 |
+
json_schema_input_type: The input type of the function. This is only used to generate
|
| 463 |
+
the appropriate JSON Schema (in validation mode) and can only specified
|
| 464 |
+
when `mode` is either `'before'`, `'plain'` or `'wrap'`.
|
| 465 |
+
|
| 466 |
+
Raises:
|
| 467 |
+
PydanticUserError:
|
| 468 |
+
- If the decorator is used without any arguments (at least one field name must be provided).
|
| 469 |
+
- If the provided field names are not strings.
|
| 470 |
+
- If `json_schema_input_type` is provided with an unsupported `mode`.
|
| 471 |
+
- If the decorator is applied to an instance method.
|
| 472 |
+
"""
|
| 473 |
+
if callable(field) or isinstance(field, classmethod):
|
| 474 |
+
raise PydanticUserError(
|
| 475 |
+
'The `@field_validator` decorator cannot be used without arguments, at least one field must be provided. '
|
| 476 |
+
"For example: `@field_validator('<field_name>', ...)`.",
|
| 477 |
+
code='decorator-missing-arguments',
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined:
|
| 481 |
+
raise PydanticUserError(
|
| 482 |
+
f"`json_schema_input_type` can't be used when mode is set to {mode!r}",
|
| 483 |
+
code='validator-input-type',
|
| 484 |
+
)
|
| 485 |
+
|
| 486 |
+
if json_schema_input_type is PydanticUndefined and mode == 'plain':
|
| 487 |
+
json_schema_input_type = Any
|
| 488 |
+
|
| 489 |
+
fields = field, *fields
|
| 490 |
+
if not all(isinstance(field, str) for field in fields):
|
| 491 |
+
raise PydanticUserError(
|
| 492 |
+
'The provided field names to the `@field_validator` decorator should be strings. '
|
| 493 |
+
"For example: `@field_validator('<field_name_1>', '<field_name_2>', ...).`",
|
| 494 |
+
code='decorator-invalid-fields',
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
def dec(
|
| 498 |
+
f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any],
|
| 499 |
+
) -> _decorators.PydanticDescriptorProxy[Any]:
|
| 500 |
+
if _decorators.is_instance_method_from_sig(f):
|
| 501 |
+
raise PydanticUserError(
|
| 502 |
+
'The `@field_validator` decorator cannot be applied to instance methods',
|
| 503 |
+
code='validator-instance-method',
|
| 504 |
+
)
|
| 505 |
+
|
| 506 |
+
# auto apply the @classmethod decorator
|
| 507 |
+
f = _decorators.ensure_classmethod_based_on_signature(f)
|
| 508 |
+
|
| 509 |
+
dec_info = _decorators.FieldValidatorDecoratorInfo(
|
| 510 |
+
fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type
|
| 511 |
+
)
|
| 512 |
+
return _decorators.PydanticDescriptorProxy(f, dec_info)
|
| 513 |
+
|
| 514 |
+
return dec
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
_ModelType = TypeVar('_ModelType')
|
| 518 |
+
_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True)
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
class ModelWrapValidatorHandler(core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]):
|
| 522 |
+
"""`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`."""
|
| 523 |
+
|
| 524 |
+
def __call__( # noqa: D102
|
| 525 |
+
self,
|
| 526 |
+
value: Any,
|
| 527 |
+
outer_location: str | int | None = None,
|
| 528 |
+
/,
|
| 529 |
+
) -> _ModelTypeCo: # pragma: no cover
|
| 530 |
+
...
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]):
|
| 534 |
+
"""A `@model_validator` decorated function signature.
|
| 535 |
+
This is used when `mode='wrap'` and the function does not have info argument.
|
| 536 |
+
"""
|
| 537 |
+
|
| 538 |
+
def __call__( # noqa: D102
|
| 539 |
+
self,
|
| 540 |
+
cls: type[_ModelType],
|
| 541 |
+
# this can be a dict, a model instance
|
| 542 |
+
# or anything else that gets passed to validate_python
|
| 543 |
+
# thus validators _must_ handle all cases
|
| 544 |
+
value: Any,
|
| 545 |
+
handler: ModelWrapValidatorHandler[_ModelType],
|
| 546 |
+
/,
|
| 547 |
+
) -> _ModelType: ...
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
class ModelWrapValidator(Protocol[_ModelType]):
|
| 551 |
+
"""A `@model_validator` decorated function signature. This is used when `mode='wrap'`."""
|
| 552 |
+
|
| 553 |
+
def __call__( # noqa: D102
|
| 554 |
+
self,
|
| 555 |
+
cls: type[_ModelType],
|
| 556 |
+
# this can be a dict, a model instance
|
| 557 |
+
# or anything else that gets passed to validate_python
|
| 558 |
+
# thus validators _must_ handle all cases
|
| 559 |
+
value: Any,
|
| 560 |
+
handler: ModelWrapValidatorHandler[_ModelType],
|
| 561 |
+
info: core_schema.ValidationInfo,
|
| 562 |
+
/,
|
| 563 |
+
) -> _ModelType: ...
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
class FreeModelBeforeValidatorWithoutInfo(Protocol):
|
| 567 |
+
"""A `@model_validator` decorated function signature.
|
| 568 |
+
This is used when `mode='before'` and the function does not have info argument.
|
| 569 |
+
"""
|
| 570 |
+
|
| 571 |
+
def __call__( # noqa: D102
|
| 572 |
+
self,
|
| 573 |
+
# this can be a dict, a model instance
|
| 574 |
+
# or anything else that gets passed to validate_python
|
| 575 |
+
# thus validators _must_ handle all cases
|
| 576 |
+
value: Any,
|
| 577 |
+
/,
|
| 578 |
+
) -> Any: ...
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
class ModelBeforeValidatorWithoutInfo(Protocol):
|
| 582 |
+
"""A `@model_validator` decorated function signature.
|
| 583 |
+
This is used when `mode='before'` and the function does not have info argument.
|
| 584 |
+
"""
|
| 585 |
+
|
| 586 |
+
def __call__( # noqa: D102
|
| 587 |
+
self,
|
| 588 |
+
cls: Any,
|
| 589 |
+
# this can be a dict, a model instance
|
| 590 |
+
# or anything else that gets passed to validate_python
|
| 591 |
+
# thus validators _must_ handle all cases
|
| 592 |
+
value: Any,
|
| 593 |
+
/,
|
| 594 |
+
) -> Any: ...
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
class FreeModelBeforeValidator(Protocol):
|
| 598 |
+
"""A `@model_validator` decorated function signature. This is used when `mode='before'`."""
|
| 599 |
+
|
| 600 |
+
def __call__( # noqa: D102
|
| 601 |
+
self,
|
| 602 |
+
# this can be a dict, a model instance
|
| 603 |
+
# or anything else that gets passed to validate_python
|
| 604 |
+
# thus validators _must_ handle all cases
|
| 605 |
+
value: Any,
|
| 606 |
+
info: core_schema.ValidationInfo[Any],
|
| 607 |
+
/,
|
| 608 |
+
) -> Any: ...
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
class ModelBeforeValidator(Protocol):
|
| 612 |
+
"""A `@model_validator` decorated function signature. This is used when `mode='before'`."""
|
| 613 |
+
|
| 614 |
+
def __call__( # noqa: D102
|
| 615 |
+
self,
|
| 616 |
+
cls: Any,
|
| 617 |
+
# this can be a dict, a model instance
|
| 618 |
+
# or anything else that gets passed to validate_python
|
| 619 |
+
# thus validators _must_ handle all cases
|
| 620 |
+
value: Any,
|
| 621 |
+
info: core_schema.ValidationInfo[Any],
|
| 622 |
+
/,
|
| 623 |
+
) -> Any: ...
|
| 624 |
+
|
| 625 |
+
|
| 626 |
+
ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType]
|
| 627 |
+
"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not
|
| 628 |
+
have info argument.
|
| 629 |
+
"""
|
| 630 |
+
|
| 631 |
+
ModelAfterValidator = Callable[[_ModelType, core_schema.ValidationInfo[Any]], _ModelType]
|
| 632 |
+
"""A `@model_validator` decorated function signature. This is used when `mode='after'`."""
|
| 633 |
+
|
| 634 |
+
_AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]]
|
| 635 |
+
_AnyModelBeforeValidator = Union[
|
| 636 |
+
FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo
|
| 637 |
+
]
|
| 638 |
+
_AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]]
|
| 639 |
+
|
| 640 |
+
|
| 641 |
+
@overload
|
| 642 |
+
def model_validator(
|
| 643 |
+
*,
|
| 644 |
+
mode: Literal['wrap'],
|
| 645 |
+
) -> Callable[
|
| 646 |
+
[_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
|
| 647 |
+
]: ...
|
| 648 |
+
|
| 649 |
+
|
| 650 |
+
@overload
|
| 651 |
+
def model_validator(
|
| 652 |
+
*,
|
| 653 |
+
mode: Literal['before'],
|
| 654 |
+
) -> Callable[
|
| 655 |
+
[_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
|
| 656 |
+
]: ...
|
| 657 |
+
|
| 658 |
+
|
| 659 |
+
@overload
|
| 660 |
+
def model_validator(
|
| 661 |
+
*,
|
| 662 |
+
mode: Literal['after'],
|
| 663 |
+
) -> Callable[
|
| 664 |
+
[_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
|
| 665 |
+
]: ...
|
| 666 |
+
|
| 667 |
+
|
| 668 |
+
def model_validator(
|
| 669 |
+
*,
|
| 670 |
+
mode: Literal['wrap', 'before', 'after'],
|
| 671 |
+
) -> Any:
|
| 672 |
+
"""!!! abstract "Usage Documentation"
|
| 673 |
+
[Model Validators](../concepts/validators.md#model-validators)
|
| 674 |
+
|
| 675 |
+
Decorate model methods for validation purposes.
|
| 676 |
+
|
| 677 |
+
Example usage:
|
| 678 |
+
```python
|
| 679 |
+
from typing_extensions import Self
|
| 680 |
+
|
| 681 |
+
from pydantic import BaseModel, ValidationError, model_validator
|
| 682 |
+
|
| 683 |
+
class Square(BaseModel):
|
| 684 |
+
width: float
|
| 685 |
+
height: float
|
| 686 |
+
|
| 687 |
+
@model_validator(mode='after')
|
| 688 |
+
def verify_square(self) -> Self:
|
| 689 |
+
if self.width != self.height:
|
| 690 |
+
raise ValueError('width and height do not match')
|
| 691 |
+
return self
|
| 692 |
+
|
| 693 |
+
s = Square(width=1, height=1)
|
| 694 |
+
print(repr(s))
|
| 695 |
+
#> Square(width=1.0, height=1.0)
|
| 696 |
+
|
| 697 |
+
try:
|
| 698 |
+
Square(width=1, height=2)
|
| 699 |
+
except ValidationError as e:
|
| 700 |
+
print(e)
|
| 701 |
+
'''
|
| 702 |
+
1 validation error for Square
|
| 703 |
+
Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict]
|
| 704 |
+
'''
|
| 705 |
+
```
|
| 706 |
+
|
| 707 |
+
For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators).
|
| 708 |
+
|
| 709 |
+
Args:
|
| 710 |
+
mode: A required string literal that specifies the validation mode.
|
| 711 |
+
It can be one of the following: 'wrap', 'before', or 'after'.
|
| 712 |
+
|
| 713 |
+
Returns:
|
| 714 |
+
A decorator that can be used to decorate a function to be used as a model validator.
|
| 715 |
+
"""
|
| 716 |
+
|
| 717 |
+
def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
|
| 718 |
+
# auto apply the @classmethod decorator. NOTE: in V3, do not apply the conversion for 'after' validators:
|
| 719 |
+
f = _decorators.ensure_classmethod_based_on_signature(f)
|
| 720 |
+
if mode == 'after' and isinstance(f, classmethod):
|
| 721 |
+
warnings.warn(
|
| 722 |
+
category=PydanticDeprecatedSince212,
|
| 723 |
+
message=(
|
| 724 |
+
"Using `@model_validator` with mode='after' on a classmethod is deprecated. Instead, use an instance method. "
|
| 725 |
+
f'See the documentation at https://docs.pydantic.dev/{version_short()}/concepts/validators/#model-after-validator.'
|
| 726 |
+
),
|
| 727 |
+
stacklevel=2,
|
| 728 |
+
)
|
| 729 |
+
|
| 730 |
+
dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode)
|
| 731 |
+
return _decorators.PydanticDescriptorProxy(f, dec_info)
|
| 732 |
+
|
| 733 |
+
return dec
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
AnyType = TypeVar('AnyType')
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
if TYPE_CHECKING:
|
| 740 |
+
# If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this
|
| 741 |
+
InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence`
|
| 742 |
+
|
| 743 |
+
else:
|
| 744 |
+
|
| 745 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 746 |
+
class InstanceOf:
|
| 747 |
+
'''Generic type for annotating a type that is an instance of a given class.
|
| 748 |
+
|
| 749 |
+
Example:
|
| 750 |
+
```python
|
| 751 |
+
from pydantic import BaseModel, InstanceOf
|
| 752 |
+
|
| 753 |
+
class Foo:
|
| 754 |
+
...
|
| 755 |
+
|
| 756 |
+
class Bar(BaseModel):
|
| 757 |
+
foo: InstanceOf[Foo]
|
| 758 |
+
|
| 759 |
+
Bar(foo=Foo())
|
| 760 |
+
try:
|
| 761 |
+
Bar(foo=42)
|
| 762 |
+
except ValidationError as e:
|
| 763 |
+
print(e)
|
| 764 |
+
"""
|
| 765 |
+
[
|
| 766 |
+
β {
|
| 767 |
+
β β 'type': 'is_instance_of',
|
| 768 |
+
β β 'loc': ('foo',),
|
| 769 |
+
β β 'msg': 'Input should be an instance of Foo',
|
| 770 |
+
β β 'input': 42,
|
| 771 |
+
β β 'ctx': {'class': 'Foo'},
|
| 772 |
+
β β 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of'
|
| 773 |
+
β }
|
| 774 |
+
]
|
| 775 |
+
"""
|
| 776 |
+
```
|
| 777 |
+
'''
|
| 778 |
+
|
| 779 |
+
@classmethod
|
| 780 |
+
def __class_getitem__(cls, item: AnyType) -> AnyType:
|
| 781 |
+
return Annotated[item, cls()]
|
| 782 |
+
|
| 783 |
+
@classmethod
|
| 784 |
+
def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 785 |
+
from pydantic._internal._generate_schema import GENERATE_SCHEMA_ERRORS
|
| 786 |
+
|
| 787 |
+
# use the generic _origin_ as the second argument to isinstance when appropriate
|
| 788 |
+
instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source)
|
| 789 |
+
|
| 790 |
+
try:
|
| 791 |
+
# Try to generate the "standard" schema, which will be used when loading from JSON
|
| 792 |
+
original_schema = handler(source)
|
| 793 |
+
except GENERATE_SCHEMA_ERRORS:
|
| 794 |
+
# If that fails, just produce a schema that can validate from python
|
| 795 |
+
return instance_of_schema
|
| 796 |
+
else:
|
| 797 |
+
# Use the "original" approach to serialization
|
| 798 |
+
instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
|
| 799 |
+
function=lambda v, h: h(v), schema=original_schema
|
| 800 |
+
)
|
| 801 |
+
return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema)
|
| 802 |
+
|
| 803 |
+
__hash__ = object.__hash__
|
| 804 |
+
|
| 805 |
+
|
| 806 |
+
if TYPE_CHECKING:
|
| 807 |
+
SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str]
|
| 808 |
+
else:
|
| 809 |
+
|
| 810 |
+
@dataclasses.dataclass(**_internal_dataclass.slots_true)
|
| 811 |
+
class SkipValidation:
|
| 812 |
+
"""If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be
|
| 813 |
+
skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`.
|
| 814 |
+
|
| 815 |
+
This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes,
|
| 816 |
+
and know that it is safe to skip validation for one or more of the fields.
|
| 817 |
+
|
| 818 |
+
Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations
|
| 819 |
+
may not have the expected effects. Therefore, when used, this annotation should generally be the final
|
| 820 |
+
annotation applied to a type.
|
| 821 |
+
"""
|
| 822 |
+
|
| 823 |
+
def __class_getitem__(cls, item: Any) -> Any:
|
| 824 |
+
return Annotated[item, SkipValidation()]
|
| 825 |
+
|
| 826 |
+
@classmethod
|
| 827 |
+
def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 828 |
+
with warnings.catch_warnings():
|
| 829 |
+
warnings.simplefilter('ignore', ArbitraryTypeWarning)
|
| 830 |
+
original_schema = handler(source)
|
| 831 |
+
metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]}
|
| 832 |
+
return core_schema.any_schema(
|
| 833 |
+
metadata=metadata,
|
| 834 |
+
serialization=core_schema.wrap_serializer_function_ser_schema(
|
| 835 |
+
function=lambda v, h: h(v), schema=original_schema
|
| 836 |
+
),
|
| 837 |
+
)
|
| 838 |
+
|
| 839 |
+
__hash__ = object.__hash__
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
_FromTypeT = TypeVar('_FromTypeT')
|
| 843 |
+
|
| 844 |
+
|
| 845 |
+
class ValidateAs:
|
| 846 |
+
"""A helper class to validate a custom type from a type that is natively supported by Pydantic.
|
| 847 |
+
|
| 848 |
+
Args:
|
| 849 |
+
from_type: The type natively supported by Pydantic to use to perform validation.
|
| 850 |
+
instantiation_hook: A callable taking the validated type as an argument, and returning
|
| 851 |
+
the populated custom type.
|
| 852 |
+
|
| 853 |
+
Example:
|
| 854 |
+
```python {lint="skip"}
|
| 855 |
+
from typing import Annotated
|
| 856 |
+
|
| 857 |
+
from pydantic import BaseModel, TypeAdapter, ValidateAs
|
| 858 |
+
|
| 859 |
+
class MyCls:
|
| 860 |
+
def __init__(self, a: int) -> None:
|
| 861 |
+
self.a = a
|
| 862 |
+
|
| 863 |
+
def __repr__(self) -> str:
|
| 864 |
+
return f"MyCls(a={self.a})"
|
| 865 |
+
|
| 866 |
+
class Model(BaseModel):
|
| 867 |
+
a: int
|
| 868 |
+
|
| 869 |
+
|
| 870 |
+
ta = TypeAdapter(
|
| 871 |
+
Annotated[MyCls, ValidateAs(Model, lambda v: MyCls(a=v.a))]
|
| 872 |
+
)
|
| 873 |
+
|
| 874 |
+
print(ta.validate_python({'a': 1}))
|
| 875 |
+
#> MyCls(a=1)
|
| 876 |
+
```
|
| 877 |
+
"""
|
| 878 |
+
|
| 879 |
+
# TODO: make use of PEP 747
|
| 880 |
+
def __init__(self, from_type: type[_FromTypeT], /, instantiation_hook: Callable[[_FromTypeT], Any]) -> None:
|
| 881 |
+
self.from_type = from_type
|
| 882 |
+
self.instantiation_hook = instantiation_hook
|
| 883 |
+
|
| 884 |
+
def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
| 885 |
+
schema = handler(self.from_type)
|
| 886 |
+
return core_schema.no_info_after_validator_function(
|
| 887 |
+
self.instantiation_hook,
|
| 888 |
+
schema=schema,
|
| 889 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/generics.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The `generics` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/json.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The `json` module is a backport module from V1."""
|
| 2 |
+
|
| 3 |
+
from ._migration import getattr_migration
|
| 4 |
+
|
| 5 |
+
__getattr__ = getattr_migration(__name__)
|
.mcp-venv/lib/python3.13/site-packages/pydantic/json_schema.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|