Coverage for pydantic/_internal/_decorators.py: 98.25%

311 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-03 19:29 +0000

1"""Logic related to validators applied to models etc. via the `@field_validator` and `@model_validator` decorators.""" 

2 

3from __future__ import annotations as _annotations 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

4 

5from collections import deque 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

6from dataclasses import dataclass, field 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

7from functools import cached_property, partial, partialmethod 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

8from inspect import Parameter, Signature, isdatadescriptor, ismethoddescriptor, signature 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

9from itertools import islice 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

10from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, Iterable, TypeVar, Union 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

11 

12from pydantic_core import PydanticUndefined, core_schema 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

13from typing_extensions import Literal, TypeAlias, is_typeddict 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

14 

15from ..errors import PydanticUserError 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

16from ._core_utils import get_type_ref 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

17from ._internal_dataclass import slots_true 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

18from ._typing_extra import get_function_type_hints 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

19 

20if TYPE_CHECKING: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

21 from ..fields import ComputedFieldInfo 

22 from ..functional_validators import FieldValidatorModes 

23 

24 

25@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

26class ValidatorDecoratorInfo: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

27 """A container for data from `@validator` so that we can access it 

28 while building the pydantic-core schema. 

29 

30 Attributes: 

31 decorator_repr: A class variable representing the decorator string, '@validator'. 

32 fields: A tuple of field names the validator should be called on. 

33 mode: The proposed validator mode. 

34 each_item: For complex objects (sets, lists etc.) whether to validate individual 

35 elements rather than the whole object. 

36 always: Whether this method and other validators should be called even if the value is missing. 

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

38 """ 

39 

40 decorator_repr: ClassVar[str] = '@validator' 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

41 

42 fields: tuple[str, ...] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

43 mode: Literal['before', 'after'] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

44 each_item: bool 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

45 always: bool 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

46 check_fields: bool | None 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

47 

48 

49@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

50class FieldValidatorDecoratorInfo: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

51 """A container for data from `@field_validator` so that we can access it 

52 while building the pydantic-core schema. 

53 

54 Attributes: 

55 decorator_repr: A class variable representing the decorator string, '@field_validator'. 

56 fields: A tuple of field names the validator should be called on. 

57 mode: The proposed validator mode. 

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

59 """ 

60 

61 decorator_repr: ClassVar[str] = '@field_validator' 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

62 

63 fields: tuple[str, ...] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

64 mode: FieldValidatorModes 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

65 check_fields: bool | None 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

66 

67 

68@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

69class RootValidatorDecoratorInfo: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

70 """A container for data from `@root_validator` so that we can access it 

71 while building the pydantic-core schema. 

72 

73 Attributes: 

74 decorator_repr: A class variable representing the decorator string, '@root_validator'. 

75 mode: The proposed validator mode. 

76 """ 

77 

78 decorator_repr: ClassVar[str] = '@root_validator' 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

79 mode: Literal['before', 'after'] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

80 

81 

82@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

83class FieldSerializerDecoratorInfo: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

84 """A container for data from `@field_serializer` so that we can access it 

85 while building the pydantic-core schema. 

86 

87 Attributes: 

88 decorator_repr: A class variable representing the decorator string, '@field_serializer'. 

89 fields: A tuple of field names the serializer should be called on. 

90 mode: The proposed serializer mode. 

91 return_type: The type of the serializer's return value. 

92 when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`, 

93 and `'json-unless-none'`. 

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

95 """ 

96 

97 decorator_repr: ClassVar[str] = '@field_serializer' 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

98 fields: tuple[str, ...] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

99 mode: Literal['plain', 'wrap'] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

100 return_type: Any 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

101 when_used: core_schema.WhenUsed 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

102 check_fields: bool | None 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

103 

104 

105@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

106class ModelSerializerDecoratorInfo: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

107 """A container for data from `@model_serializer` so that we can access it 

108 while building the pydantic-core schema. 

109 

110 Attributes: 

111 decorator_repr: A class variable representing the decorator string, '@model_serializer'. 

112 mode: The proposed serializer mode. 

113 return_type: The type of the serializer's return value. 

114 when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`, 

115 and `'json-unless-none'`. 

116 """ 

117 

118 decorator_repr: ClassVar[str] = '@model_serializer' 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

119 mode: Literal['plain', 'wrap'] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

120 return_type: Any 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

121 when_used: core_schema.WhenUsed 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

122 

123 

124@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

125class ModelValidatorDecoratorInfo: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

126 """A container for data from `@model_validator` so that we can access it 

127 while building the pydantic-core schema. 

128 

129 Attributes: 

130 decorator_repr: A class variable representing the decorator string, '@model_serializer'. 

131 mode: The proposed serializer mode. 

132 """ 

133 

134 decorator_repr: ClassVar[str] = '@model_validator' 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

135 mode: Literal['wrap', 'before', 'after'] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

136 

137 

138DecoratorInfo: TypeAlias = """Union[ 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

139 ValidatorDecoratorInfo, 

140 FieldValidatorDecoratorInfo, 

141 RootValidatorDecoratorInfo, 

142 FieldSerializerDecoratorInfo, 

143 ModelSerializerDecoratorInfo, 

144 ModelValidatorDecoratorInfo, 

145 ComputedFieldInfo, 

146]""" 

147 

148ReturnType = TypeVar('ReturnType') 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

149DecoratedType: TypeAlias = ( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

150 'Union[classmethod[Any, Any, ReturnType], staticmethod[Any, ReturnType], Callable[..., ReturnType], property]' 

151) 

152 

153 

154@dataclass # can't use slots here since we set attributes on `__post_init__` 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

155class PydanticDescriptorProxy(Generic[ReturnType]): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

156 """Wrap a classmethod, staticmethod, property or unbound function 

157 and act as a descriptor that allows us to detect decorated items 

158 from the class' attributes. 

159 

160 This class' __get__ returns the wrapped item's __get__ result, 

161 which makes it transparent for classmethods and staticmethods. 

162 

163 Attributes: 

164 wrapped: The decorator that has to be wrapped. 

165 decorator_info: The decorator info. 

166 shim: A wrapper function to wrap V1 style function. 

167 """ 

168 

169 wrapped: DecoratedType[ReturnType] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

170 decorator_info: DecoratorInfo 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

171 shim: Callable[[Callable[..., Any]], Callable[..., Any]] | None = None 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

172 

173 def __post_init__(self): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

174 for attr in 'setter', 'deleter': 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

175 if hasattr(self.wrapped, attr): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

176 f = partial(self._call_wrapped_attr, name=attr) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

177 setattr(self, attr, f) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

178 

179 def _call_wrapped_attr(self, func: Callable[[Any], None], *, name: str) -> PydanticDescriptorProxy[ReturnType]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

180 self.wrapped = getattr(self.wrapped, name)(func) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

181 return self 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

182 

183 def __get__(self, obj: object | None, obj_type: type[object] | None = None) -> PydanticDescriptorProxy[ReturnType]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

184 try: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

185 return self.wrapped.__get__(obj, obj_type) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

186 except AttributeError: 1zAstabcdefByCDuvghijklEFwxmnopqr

187 # not a descriptor, e.g. a partial object 

188 return self.wrapped # type: ignore[return-value] 1zAstabcdefByCDuvghijklEFwxmnopqr

189 

190 def __set_name__(self, instance: Any, name: str) -> None: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

191 if hasattr(self.wrapped, '__set_name__'): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

192 self.wrapped.__set_name__(instance, name) # pyright: ignore[reportFunctionMemberAccess] 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

193 

194 def __getattr__(self, __name: str) -> Any: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

195 """Forward checks for __isabstractmethod__ and such.""" 

196 return getattr(self.wrapped, __name) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

197 

198 

199DecoratorInfoType = TypeVar('DecoratorInfoType', bound=DecoratorInfo) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

200 

201 

202@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

203class Decorator(Generic[DecoratorInfoType]): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

204 """A generic container class to join together the decorator metadata 

205 (metadata from decorator itself, which we have when the 

206 decorator is called but not when we are building the core-schema) 

207 and the bound function (which we have after the class itself is created). 

208 

209 Attributes: 

210 cls_ref: The class ref. 

211 cls_var_name: The decorated function name. 

212 func: The decorated function. 

213 shim: A wrapper function to wrap V1 style function. 

214 info: The decorator info. 

215 """ 

216 

217 cls_ref: str 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

218 cls_var_name: str 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

219 func: Callable[..., Any] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

220 shim: Callable[[Any], Any] | None 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

221 info: DecoratorInfoType 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

222 

223 @staticmethod 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

224 def build( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

225 cls_: Any, 

226 *, 

227 cls_var_name: str, 

228 shim: Callable[[Any], Any] | None, 

229 info: DecoratorInfoType, 

230 ) -> Decorator[DecoratorInfoType]: 

231 """Build a new decorator. 

232 

233 Args: 

234 cls_: The class. 

235 cls_var_name: The decorated function name. 

236 shim: A wrapper function to wrap V1 style function. 

237 info: The decorator info. 

238 

239 Returns: 

240 The new decorator instance. 

241 """ 

242 func = get_attribute_from_bases(cls_, cls_var_name) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

243 if shim is not None: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

244 func = shim(func) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

245 func = unwrap_wrapped_function(func, unwrap_partial=False) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

246 if not callable(func): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

247 # This branch will get hit for classmethod properties 

248 attribute = get_attribute_from_base_dicts(cls_, cls_var_name) # prevents the binding call to `__get__` 1stabcdefByuvghijklwxmnopqr

249 if isinstance(attribute, PydanticDescriptorProxy): 249 ↛ 251line 249 didn't jump to line 251 because the condition on line 249 was always true1stabcdefByuvghijklwxmnopqr

250 func = unwrap_wrapped_function(attribute.wrapped) 1stabcdefByuvghijklwxmnopqr

251 return Decorator( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

252 cls_ref=get_type_ref(cls_), 

253 cls_var_name=cls_var_name, 

254 func=func, 

255 shim=shim, 

256 info=info, 

257 ) 

258 

259 def bind_to_cls(self, cls: Any) -> Decorator[DecoratorInfoType]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

260 """Bind the decorator to a class. 

261 

262 Args: 

263 cls: the class. 

264 

265 Returns: 

266 The new decorator instance. 

267 """ 

268 return self.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

269 cls, 

270 cls_var_name=self.cls_var_name, 

271 shim=self.shim, 

272 info=self.info, 

273 ) 

274 

275 

276def get_bases(tp: type[Any]) -> tuple[type[Any], ...]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

277 """Get the base classes of a class or typeddict. 

278 

279 Args: 

280 tp: The type or class to get the bases. 

281 

282 Returns: 

283 The base classes. 

284 """ 

285 if is_typeddict(tp): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

286 return tp.__orig_bases__ # type: ignore 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

287 try: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

288 return tp.__bases__ 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

289 except AttributeError: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

290 return () 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

291 

292 

293def mro(tp: type[Any]) -> tuple[type[Any], ...]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

294 """Calculate the Method Resolution Order of bases using the C3 algorithm. 

295 

296 See https://www.python.org/download/releases/2.3/mro/ 

297 """ 

298 # try to use the existing mro, for performance mainly 

299 # but also because it helps verify the implementation below 

300 if not is_typeddict(tp): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

301 try: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

302 return tp.__mro__ 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

303 except AttributeError: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

304 # GenericAlias and some other cases 

305 pass 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

306 

307 bases = get_bases(tp) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

308 return (tp,) + mro_for_bases(bases) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

309 

310 

311def mro_for_bases(bases: tuple[type[Any], ...]) -> tuple[type[Any], ...]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

312 def merge_seqs(seqs: list[deque[type[Any]]]) -> Iterable[type[Any]]: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

313 while True: 1abcdefGHyghijklIJMNOPQRSTmnopqrKL

314 non_empty = [seq for seq in seqs if seq] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

315 if not non_empty: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

316 # Nothing left to process, we're done. 

317 return 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

318 candidate: type[Any] | None = None 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

319 for seq in non_empty: # Find merge candidates among seq heads. 319 ↛ 327line 319 didn't jump to line 327 because the loop on line 319 didn't complete1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

320 candidate = seq[0] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

321 not_head = [s for s in non_empty if candidate in islice(s, 1, None)] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

322 if not_head: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

323 # Reject the candidate. 

324 candidate = None 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

325 else: 

326 break 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

327 if not candidate: 327 ↛ 328line 327 didn't jump to line 328 because the condition on line 327 was never true1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

328 raise TypeError('Inconsistent hierarchy, no C3 MRO is possible') 

329 yield candidate 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

330 for seq in non_empty: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

331 # Remove candidate. 

332 if seq[0] == candidate: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

333 seq.popleft() 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

334 

335 seqs = [deque(mro(base)) for base in bases] + [deque(bases)] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

336 return tuple(merge_seqs(seqs)) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

337 

338 

339_sentinel = object() 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

340 

341 

342def get_attribute_from_bases(tp: type[Any] | tuple[type[Any], ...], name: str) -> Any: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

343 """Get the attribute from the next class in the MRO that has it, 

344 aiming to simulate calling the method on the actual class. 

345 

346 The reason for iterating over the mro instead of just getting 

347 the attribute (which would do that for us) is to support TypedDict, 

348 which lacks a real __mro__, but can have a virtual one constructed 

349 from its bases (as done here). 

350 

351 Args: 

352 tp: The type or class to search for the attribute. If a tuple, this is treated as a set of base classes. 

353 name: The name of the attribute to retrieve. 

354 

355 Returns: 

356 Any: The attribute value, if found. 

357 

358 Raises: 

359 AttributeError: If the attribute is not found in any class in the MRO. 

360 """ 

361 if isinstance(tp, tuple): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

362 for base in mro_for_bases(tp): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

363 attribute = base.__dict__.get(name, _sentinel) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

364 if attribute is not _sentinel: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

365 attribute_get = getattr(attribute, '__get__', None) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

366 if attribute_get is not None: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

367 return attribute_get(None, tp) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

368 return attribute 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

369 raise AttributeError(f'{name} not found in {tp}') 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

370 else: 

371 try: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

372 return getattr(tp, name) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

373 except AttributeError: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

374 return get_attribute_from_bases(mro(tp), name) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

375 

376 

377def get_attribute_from_base_dicts(tp: type[Any], name: str) -> Any: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

378 """Get an attribute out of the `__dict__` following the MRO. 

379 This prevents the call to `__get__` on the descriptor, and allows 

380 us to get the original function for classmethod properties. 

381 

382 Args: 

383 tp: The type or class to search for the attribute. 

384 name: The name of the attribute to retrieve. 

385 

386 Returns: 

387 Any: The attribute value, if found. 

388 

389 Raises: 

390 KeyError: If the attribute is not found in any class's `__dict__` in the MRO. 

391 """ 

392 for base in reversed(mro(tp)): 392 ↛ 395line 392 didn't jump to line 395 because the loop on line 392 didn't complete1stabcdefByuvghijklwxmnopqr

393 if name in base.__dict__: 1stabcdefByuvghijklwxmnopqr

394 return base.__dict__[name] 1stabcdefByuvghijklwxmnopqr

395 return tp.__dict__[name] # raise the error 

396 

397 

398@dataclass(**slots_true) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

399class DecoratorInfos: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

400 """Mapping of name in the class namespace to decorator info. 

401 

402 note that the name in the class namespace is the function or attribute name 

403 not the field name! 

404 """ 

405 

406 validators: dict[str, Decorator[ValidatorDecoratorInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

407 field_validators: dict[str, Decorator[FieldValidatorDecoratorInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

408 root_validators: dict[str, Decorator[RootValidatorDecoratorInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

409 field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

410 model_serializers: dict[str, Decorator[ModelSerializerDecoratorInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

411 model_validators: dict[str, Decorator[ModelValidatorDecoratorInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

412 computed_fields: dict[str, Decorator[ComputedFieldInfo]] = field(default_factory=dict) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

413 

414 @staticmethod 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

415 def build(model_dc: type[Any]) -> DecoratorInfos: # noqa: C901 (ignore complexity) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

416 """We want to collect all DecFunc instances that exist as 

417 attributes in the namespace of the class (a BaseModel or dataclass) 

418 that called us 

419 But we want to collect these in the order of the bases 

420 So instead of getting them all from the leaf class (the class that called us), 

421 we traverse the bases from root (the oldest ancestor class) to leaf 

422 and collect all of the instances as we go, taking care to replace 

423 any duplicate ones with the last one we see to mimic how function overriding 

424 works with inheritance. 

425 If we do replace any functions we put the replacement into the position 

426 the replaced function was in; that is, we maintain the order. 

427 """ 

428 # reminder: dicts are ordered and replacement does not alter the order 

429 res = DecoratorInfos() 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

430 for base in reversed(mro(model_dc)[1:]): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

431 existing: DecoratorInfos | None = base.__dict__.get('__pydantic_decorators__') 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

432 if existing is None: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

433 existing = DecoratorInfos.build(base) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

434 res.validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.validators.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

435 res.field_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.field_validators.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

436 res.root_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.root_validators.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

437 res.field_serializers.update({k: v.bind_to_cls(model_dc) for k, v in existing.field_serializers.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

438 res.model_serializers.update({k: v.bind_to_cls(model_dc) for k, v in existing.model_serializers.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

439 res.model_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.model_validators.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

440 res.computed_fields.update({k: v.bind_to_cls(model_dc) for k, v in existing.computed_fields.items()}) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

441 

442 to_replace: list[tuple[str, Any]] = [] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

443 

444 for var_name, var_value in vars(model_dc).items(): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

445 if isinstance(var_value, PydanticDescriptorProxy): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

446 info = var_value.decorator_info 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

447 if isinstance(info, ValidatorDecoratorInfo): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

448 res.validators[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

449 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info 

450 ) 

451 elif isinstance(info, FieldValidatorDecoratorInfo): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

452 res.field_validators[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

453 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info 

454 ) 

455 elif isinstance(info, RootValidatorDecoratorInfo): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

456 res.root_validators[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

457 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info 

458 ) 

459 elif isinstance(info, FieldSerializerDecoratorInfo): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

460 # check whether a serializer function is already registered for fields 

461 for field_serializer_decorator in res.field_serializers.values(): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

462 # check that each field has at most one serializer function. 

463 # serializer functions for the same field in subclasses are allowed, 

464 # and are treated as overrides 

465 if field_serializer_decorator.cls_var_name == var_name: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

466 continue 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

467 for f in info.fields: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

468 if f in field_serializer_decorator.info.fields: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

469 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

470 'Multiple field serializer functions were defined ' 

471 f'for field {f!r}, this is not allowed.', 

472 code='multiple-field-serializers', 

473 ) 

474 res.field_serializers[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

475 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info 

476 ) 

477 elif isinstance(info, ModelValidatorDecoratorInfo): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

478 res.model_validators[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

479 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info 

480 ) 

481 elif isinstance(info, ModelSerializerDecoratorInfo): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

482 res.model_serializers[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

483 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info 

484 ) 

485 else: 

486 from ..fields import ComputedFieldInfo 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

487 

488 isinstance(var_value, ComputedFieldInfo) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

489 res.computed_fields[var_name] = Decorator.build( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

490 model_dc, cls_var_name=var_name, shim=None, info=info 

491 ) 

492 to_replace.append((var_name, var_value.wrapped)) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

493 if to_replace: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

494 # If we can save `__pydantic_decorators__` on the class we'll be able to check for it above 

495 # so then we don't need to re-process the type, which means we can discard our descriptor wrappers 

496 # and replace them with the thing they are wrapping (see the other setattr call below) 

497 # which allows validator class methods to also function as regular class methods 

498 setattr(model_dc, '__pydantic_decorators__', res) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

499 for name, value in to_replace: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

500 setattr(model_dc, name, value) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

501 return res 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

502 

503 

504def inspect_validator(validator: Callable[..., Any], mode: FieldValidatorModes) -> bool: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

505 """Look at a field or model validator function and determine whether it takes an info argument. 

506 

507 An error is raised if the function has an invalid signature. 

508 

509 Args: 

510 validator: The validator function to inspect. 

511 mode: The proposed validator mode. 

512 

513 Returns: 

514 Whether the validator takes an info argument. 

515 """ 

516 try: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

517 sig = signature(validator) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

518 except (ValueError, TypeError): 1zAstabcdefGHCDuvghijklIJEFwxmnopqrKL

519 # `inspect.signature` might not be able to infer a signature, e.g. with C objects. 

520 # In this case, we assume no info argument is present: 

521 return False 1zAstabcdefGHCDuvghijklIJEFwxmnopqrKL

522 n_positional = count_positional_required_params(sig) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

523 if mode == 'wrap': 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

524 if n_positional == 3: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

525 return True 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

526 elif n_positional == 2: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

527 return False 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

528 else: 

529 assert mode in {'before', 'after', 'plain'}, f"invalid mode: {mode!r}, expected 'before', 'after' or 'plain" 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

530 if n_positional == 2: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

531 return True 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

532 elif n_positional == 1: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

533 return False 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

534 

535 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

536 f'Unrecognized field_validator function signature for {validator} with `mode={mode}`:{sig}', 

537 code='validator-signature', 

538 ) 

539 

540 

541def inspect_field_serializer( 1zAstabcdefGHCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

542 serializer: Callable[..., Any], mode: Literal['plain', 'wrap'], computed_field: bool = False 

543) -> tuple[bool, bool]: 

544 """Look at a field serializer function and determine if it is a field serializer, 

545 and whether it takes an info argument. 

546 

547 An error is raised if the function has an invalid signature. 

548 

549 Args: 

550 serializer: The serializer function to inspect. 

551 mode: The serializer mode, either 'plain' or 'wrap'. 

552 computed_field: When serializer is applied on computed_field. It doesn't require 

553 info signature. 

554 

555 Returns: 

556 Tuple of (is_field_serializer, info_arg). 

557 """ 

558 try: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

559 sig = signature(serializer) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

560 except (ValueError, TypeError): 

561 # `inspect.signature` might not be able to infer a signature, e.g. with C objects. 

562 # In this case, we assume no info argument is present and this is not a method: 

563 return (False, False) 

564 

565 first = next(iter(sig.parameters.values()), None) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

566 is_field_serializer = first is not None and first.name == 'self' 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

567 

568 n_positional = count_positional_required_params(sig) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

569 if is_field_serializer: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

570 # -1 to correct for self parameter 

571 info_arg = _serializer_info_arg(mode, n_positional - 1) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

572 else: 

573 info_arg = _serializer_info_arg(mode, n_positional) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

574 

575 if info_arg is None: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

576 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

577 f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}', 

578 code='field-serializer-signature', 

579 ) 

580 if info_arg and computed_field: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

581 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

582 'field_serializer on computed_field does not use info signature', code='field-serializer-signature' 

583 ) 

584 

585 else: 

586 return is_field_serializer, info_arg 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

587 

588 

589def inspect_annotated_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

590 """Look at a serializer function used via `Annotated` and determine whether it takes an info argument. 

591 

592 An error is raised if the function has an invalid signature. 

593 

594 Args: 

595 serializer: The serializer function to check. 

596 mode: The serializer mode, either 'plain' or 'wrap'. 

597 

598 Returns: 

599 info_arg 

600 """ 

601 try: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

602 sig = signature(serializer) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

603 except (ValueError, TypeError): 1zAstabcdefGHCDuvghijklIJEFwxmnopqrKL

604 # `inspect.signature` might not be able to infer a signature, e.g. with C objects. 

605 # In this case, we assume no info argument is present: 

606 return False 1zAstabcdefGHCDuvghijklIJEFwxmnopqrKL

607 info_arg = _serializer_info_arg(mode, count_positional_required_params(sig)) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

608 if info_arg is None: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

609 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

610 f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}', 

611 code='field-serializer-signature', 

612 ) 

613 else: 

614 return info_arg 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

615 

616 

617def inspect_model_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

618 """Look at a model serializer function and determine whether it takes an info argument. 

619 

620 An error is raised if the function has an invalid signature. 

621 

622 Args: 

623 serializer: The serializer function to check. 

624 mode: The serializer mode, either 'plain' or 'wrap'. 

625 

626 Returns: 

627 `info_arg` - whether the function expects an info argument. 

628 """ 

629 if isinstance(serializer, (staticmethod, classmethod)) or not is_instance_method_from_sig(serializer): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

630 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

631 '`@model_serializer` must be applied to instance methods', code='model-serializer-instance-method' 

632 ) 

633 

634 sig = signature(serializer) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

635 info_arg = _serializer_info_arg(mode, count_positional_required_params(sig)) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

636 if info_arg is None: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

637 raise PydanticUserError( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

638 f'Unrecognized model_serializer function signature for {serializer} with `mode={mode}`:{sig}', 

639 code='model-serializer-signature', 

640 ) 

641 else: 

642 return info_arg 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

643 

644 

645def _serializer_info_arg(mode: Literal['plain', 'wrap'], n_positional: int) -> bool | None: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

646 if mode == 'plain': 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

647 if n_positional == 1: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

648 # (input_value: Any, /) -> Any 

649 return False 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

650 elif n_positional == 2: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

651 # (model: Any, input_value: Any, /) -> Any 

652 return True 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

653 else: 

654 assert mode == 'wrap', f"invalid mode: {mode!r}, expected 'plain' or 'wrap'" 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

655 if n_positional == 2: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

656 # (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any 

657 return False 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

658 elif n_positional == 3: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

659 # (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any 

660 return True 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

661 

662 return None 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

663 

664 

665AnyDecoratorCallable: TypeAlias = ( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

666 'Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any], Callable[..., Any]]' 

667) 

668 

669 

670def is_instance_method_from_sig(function: AnyDecoratorCallable) -> bool: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

671 """Whether the function is an instance method. 

672 

673 It will consider a function as instance method if the first parameter of 

674 function is `self`. 

675 

676 Args: 

677 function: The function to check. 

678 

679 Returns: 

680 `True` if the function is an instance method, `False` otherwise. 

681 """ 

682 sig = signature(unwrap_wrapped_function(function)) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

683 first = next(iter(sig.parameters.values()), None) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

684 if first and first.name == 'self': 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

685 return True 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

686 return False 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

687 

688 

689def ensure_classmethod_based_on_signature(function: AnyDecoratorCallable) -> Any: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

690 """Apply the `@classmethod` decorator on the function. 

691 

692 Args: 

693 function: The function to apply the decorator on. 

694 

695 Return: 

696 The `@classmethod` decorator applied function. 

697 """ 

698 if not isinstance( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

699 unwrap_wrapped_function(function, unwrap_class_static_method=False), classmethod 

700 ) and _is_classmethod_from_sig(function): 

701 return classmethod(function) # type: ignore[arg-type] 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

702 return function 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

703 

704 

705def _is_classmethod_from_sig(function: AnyDecoratorCallable) -> bool: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

706 sig = signature(unwrap_wrapped_function(function)) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

707 first = next(iter(sig.parameters.values()), None) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

708 if first and first.name == 'cls': 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

709 return True 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

710 return False 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

711 

712 

713def unwrap_wrapped_function( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

714 func: Any, 

715 *, 

716 unwrap_partial: bool = True, 

717 unwrap_class_static_method: bool = True, 

718) -> Any: 

719 """Recursively unwraps a wrapped function until the underlying function is reached. 

720 This handles property, functools.partial, functools.partialmethod, staticmethod, and classmethod. 

721 

722 Args: 

723 func: The function to unwrap. 

724 unwrap_partial: If True (default), unwrap partial and partialmethod decorators. 

725 unwrap_class_static_method: If True (default), also unwrap classmethod and staticmethod 

726 decorators. If False, only unwrap partial and partialmethod decorators. 

727 

728 Returns: 

729 The underlying function of the wrapped function. 

730 """ 

731 # Define the types we want to check against as a single tuple. 

732 unwrap_types = ( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

733 (property, cached_property) 

734 + ((partial, partialmethod) if unwrap_partial else ()) 

735 + ((staticmethod, classmethod) if unwrap_class_static_method else ()) 

736 ) 

737 

738 while isinstance(func, unwrap_types): 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

739 if unwrap_class_static_method and isinstance(func, (classmethod, staticmethod)): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

740 func = func.__func__ 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

741 elif isinstance(func, (partial, partialmethod)): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

742 func = func.func 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

743 elif isinstance(func, property): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

744 func = func.fget # arbitrary choice, convenient for computed fields 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

745 else: 

746 # Make coverage happy as it can only get here in the last possible case 

747 assert isinstance(func, cached_property) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

748 func = func.func # type: ignore 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

749 

750 return func 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

751 

752 

753def get_function_return_type( 1zAstabcdefGHCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

754 func: Any, explicit_return_type: Any, types_namespace: dict[str, Any] | None = None 

755) -> Any: 

756 """Get the function return type. 

757 

758 It gets the return type from the type annotation if `explicit_return_type` is `None`. 

759 Otherwise, it returns `explicit_return_type`. 

760 

761 Args: 

762 func: The function to get its return type. 

763 explicit_return_type: The explicit return type. 

764 types_namespace: The types namespace, defaults to `None`. 

765 

766 Returns: 

767 The function return type. 

768 """ 

769 if explicit_return_type is PydanticUndefined: 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

770 # try to get it from the type annotation 

771 hints = get_function_type_hints( 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

772 unwrap_wrapped_function(func), include_keys={'return'}, types_namespace=types_namespace 

773 ) 

774 return hints.get('return', PydanticUndefined) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

775 else: 

776 return explicit_return_type 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

777 

778 

779def count_positional_required_params(sig: Signature) -> int: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

780 """Get the number of positional (required) arguments of a signature. 

781 

782 This function should only be used to inspect signatures of validation and serialization functions. 

783 The first argument (the value being serialized or validated) is counted as a required argument 

784 even if a default value exists. 

785 

786 Returns: 

787 The number of positional arguments of a signature. 

788 """ 

789 parameters = list(sig.parameters.values()) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

790 return sum( 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

791 1 

792 for param in parameters 

793 if can_be_positional(param) 

794 # First argument is the value being validated/serialized, and can have a default value 

795 # (e.g. `float`, which has signature `(x=0, /)`). We assume other parameters (the info arg 

796 # for instance) should be required, and thus without any default value. 

797 and (param.default is Parameter.empty or param == parameters[0]) 

798 ) 

799 

800 

801def can_be_positional(param: Parameter) -> bool: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

802 return param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

803 

804 

805def ensure_property(f: Any) -> Any: 1zAstabcdefGHByCDuvghijklIJUVMNOPQRSTEFwxmnopqrKL

806 """Ensure that a function is a `property` or `cached_property`, or is a valid descriptor. 

807 

808 Args: 

809 f: The function to check. 

810 

811 Returns: 

812 The function, or a `property` or `cached_property` instance wrapping the function. 

813 """ 

814 if ismethoddescriptor(f) or isdatadescriptor(f): 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

815 return f 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL

816 else: 

817 return property(f) 1zAstabcdefGHByCDuvghijklIJEFwxmnopqrKL