1- """Utility functions and Pydantic version compatibility helpers.
2-
3- Provides Pydantic version detection (PYDANTIC_V2), YAML/TOML loading,
4- and version-compatible decorators (model_validator, field_validator).
5- """
1+ """Utility functions for YAML/TOML loading and lazy BaseModel access."""
62
73from __future__ import annotations
84
95import re
106import warnings
117from functools import lru_cache
12- from typing import TYPE_CHECKING , Any , Literal , TypeVar , overload
8+ from typing import TYPE_CHECKING , Any
139
1410if TYPE_CHECKING :
1511 from collections .abc import Callable
1612 from pathlib import Path
1713
18- from pydantic import BaseModel as _BaseModel
19-
2014try :
2115 from tomllib import load as load_tomllib # type: ignore[ignoreMissingImports]
2216except ImportError : # pragma: no cover
@@ -29,28 +23,6 @@ def load_toml(path: Path) -> dict[str, Any]:
2923 return load_tomllib (f )
3024
3125
32- @lru_cache (maxsize = 1 )
33- def get_pydantic_version () -> tuple [Any , bool , bool ]:
34- """Get pydantic version info lazily. Returns (version, is_v2, is_v2_11)."""
35- # Apply pydantic patch before importing pydantic
36- from datamodel_code_generator .pydantic_patch import apply_patch # noqa: PLC0415
37-
38- apply_patch ()
39-
40- import pydantic # noqa: PLC0415
41- from packaging import version # noqa: PLC0415
42-
43- pydantic_version = version .parse (pydantic .VERSION if isinstance (pydantic .VERSION , str ) else str (pydantic .VERSION ))
44- is_v2 = version .parse ("2.0b3" ) <= pydantic_version
45- is_v2_11 = version .parse ("2.11" ) <= pydantic_version
46- return pydantic_version , is_v2 , is_v2_11
47-
48-
49- def is_pydantic_v2 () -> bool :
50- """Check if pydantic v2 is installed."""
51- return get_pydantic_version ()[1 ]
52-
53-
5426_YAML_1_2_BOOL_PATTERN = re .compile (r"^(?:true|false|True|False|TRUE|FALSE)$" )
5527_YAML_DEPRECATED_BOOL_VALUES = {"True" , "False" , "TRUE" , "FALSE" }
5628# Pattern for scientific notation without decimal point (e.g., 1e-5, 1E+10)
@@ -111,121 +83,16 @@ class CustomSafeLoader(_SafeLoader): # type: ignore[valid-type,misc]
11183 return CustomSafeLoader
11284
11385
114- Model = TypeVar ("Model" , bound = "_BaseModel" ) # ty: ignore
115- T = TypeVar ("T" )
116-
117-
118- @overload
119- def model_validator (
120- mode : Literal ["before" ],
121- ) -> (
122- Callable [[Callable [[type [Model ], T ], T ]], Callable [[type [Model ], T ], T ]]
123- | Callable [[Callable [[Model , T ], T ]], Callable [[Model , T ], T ]]
124- ): ...
125-
126-
127- @overload
128- def model_validator (
129- mode : Literal ["after" ],
130- ) -> (
131- Callable [[Callable [[type [Model ], T ], T ]], Callable [[type [Model ], T ], T ]]
132- | Callable [[Callable [[Model , T ], T ]], Callable [[Model , T ], T ]]
133- | Callable [[Callable [[Model ], Model ]], Callable [[Model ], Model ]]
134- ): ...
135-
136-
137- @overload
138- def model_validator () -> (
139- Callable [[Callable [[type [Model ], T ], T ]], Callable [[type [Model ], T ], T ]]
140- | Callable [[Callable [[Model , T ], T ]], Callable [[Model , T ], T ]]
141- | Callable [[Callable [[Model ], Model ]], Callable [[Model ], Model ]]
142- ): ...
143-
144-
145- def model_validator ( # ty: ignore
146- mode : Literal ["before" , "after" ] = "after" ,
147- ) -> (
148- Callable [[Callable [[type [Model ], T ], T ]], Callable [[type [Model ], T ], T ]]
149- | Callable [[Callable [[Model , T ], T ]], Callable [[Model , T ], T ]]
150- | Callable [[Callable [[Model ], Model ]], Callable [[Model ], Model ]]
151- ):
152- """Decorate model validators for both Pydantic v1 and v2."""
153-
154- @overload
155- def inner (method : Callable [[type [Model ], T ], T ]) -> Callable [[type [Model ], T ], T ]: ...
156-
157- @overload
158- def inner (method : Callable [[Model , T ], T ]) -> Callable [[Model , T ], T ]: ...
159-
160- @overload
161- def inner (method : Callable [[Model ], Model ]) -> Callable [[Model ], Model ]: ...
162-
163- def inner ( # pragma: no cover
164- method : Callable [[type [Model ], T ], T ] | Callable [[Model , T ], T ] | Callable [[Model ], Model ],
165- ) -> Callable [[type [Model ], T ], T ] | Callable [[Model , T ], T ] | Callable [[Model ], Model ]:
166- if is_pydantic_v2 ():
167- from pydantic import model_validator as model_validator_v2 # noqa: PLC0415
168-
169- if mode == "before" :
170- return model_validator_v2 (mode = mode )(classmethod (method )) # type: ignore[reportReturnType]
171- return model_validator_v2 (mode = mode )(method ) # type: ignore[reportReturnType]
172- from pydantic import root_validator # noqa: PLC0415 # pragma: no cover
173-
174- return root_validator (method , pre = mode == "before" ) # ty: ignore # pragma: no cover
175-
176- return inner # pragma: no cover
177-
178-
179- def field_validator (
180- field_name : str ,
181- * fields : str ,
182- mode : Literal ["before" , "after" ] = "after" ,
183- ) -> Callable [[Any ], Callable [[Any , Any ], Any ]]:
184- """Decorate field validators for both Pydantic v1 and v2."""
185-
186- def inner (method : Callable [[Model , Any ], Any ]) -> Callable [[Model , Any ], Any ]: # pragma: no cover
187- if is_pydantic_v2 ():
188- from pydantic import field_validator as field_validator_v2 # noqa: PLC0415
189-
190- return field_validator_v2 (field_name , * fields , mode = mode )(method ) # ty: ignore
191- from pydantic import validator # noqa: PLC0415 # pragma: no cover
192-
193- return validator (field_name , * fields , pre = mode == "before" )(method ) # ty: ignore # pragma: no cover
194-
195- return inner # pragma: no cover
196-
197-
198- @lru_cache (maxsize = 1 )
199- def _get_config_dict () -> type : # pragma: no cover
200- """Get ConfigDict type lazily. Only used with pydantic v2."""
201- from pydantic import ConfigDict # noqa: PLC0415
202-
203- return ConfigDict
204-
205-
206- class _ConfigDictProxy :
207- """Proxy for lazy ConfigDict access."""
208-
209- def __call__ (self , ** kwargs : Any ) -> Any : # pragma: no cover
210- return _get_config_dict ()(** kwargs )
211-
212-
213- ConfigDict : type = _ConfigDictProxy () # type: ignore[assignment]
214-
215-
21686@lru_cache (maxsize = 1 )
21787def _get_base_model_class () -> type :
218- """Get version-compatible BaseModel class lazily."""
88+ """Get BaseModel class with strict=False config lazily."""
21989 from pydantic import BaseModel as _PydanticBaseModel # noqa: PLC0415
90+ from pydantic import ConfigDict as _ConfigDict # noqa: PLC0415
22091
221- if is_pydantic_v2 ():
222- from pydantic import ConfigDict as _ConfigDict # noqa: PLC0415
223-
224- class _BaseModelV2 (_PydanticBaseModel ):
225- model_config = _ConfigDict (strict = False )
92+ class _BaseModelV2 (_PydanticBaseModel ):
93+ model_config = _ConfigDict (strict = False )
22694
227- return _BaseModelV2
228- return _PydanticBaseModel # pragma: no cover
95+ return _BaseModelV2
22996
23097
23198_BaseModel : type | None = None
0 commit comments