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

321 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-26 07:45 +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 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

4 

5import types 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

6from collections import deque 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

7from collections.abc import Iterable 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

8from dataclasses import dataclass, field 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

9from functools import cached_property, partial, partialmethod 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

10from inspect import Parameter, Signature, isdatadescriptor, ismethoddescriptor, signature 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

11from itertools import islice 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

12from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, Literal, TypeVar, Union 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

13 

14from pydantic_core import PydanticUndefined, PydanticUndefinedType, core_schema 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

15from typing_extensions import TypeAlias, is_typeddict 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

16 

17from ..errors import PydanticUserError 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

18from ._core_utils import get_type_ref 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

19from ._internal_dataclass import slots_true 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

20from ._namespace_utils import GlobalsNamespace, MappingNamespace 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

21from ._typing_extra import get_function_type_hints 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

22from ._utils import can_be_positional 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

23 

24if TYPE_CHECKING: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

25 from ..fields import ComputedFieldInfo 

26 from ..functional_validators import FieldValidatorModes 

27 

28 

29@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

30class ValidatorDecoratorInfo: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

32 while building the pydantic-core schema. 

33 

34 Attributes: 

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

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

37 mode: The proposed validator mode. 

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

39 elements rather than the whole object. 

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

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

42 """ 

43 

44 decorator_repr: ClassVar[str] = '@validator' 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

45 

46 fields: tuple[str, ...] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

47 mode: Literal['before', 'after'] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

48 each_item: bool 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

49 always: bool 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

50 check_fields: bool | None 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

51 

52 

53@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

54class FieldValidatorDecoratorInfo: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

56 while building the pydantic-core schema. 

57 

58 Attributes: 

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

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

61 mode: The proposed validator mode. 

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

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

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

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

66 """ 

67 

68 decorator_repr: ClassVar[str] = '@field_validator' 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

69 

70 fields: tuple[str, ...] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

71 mode: FieldValidatorModes 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

72 check_fields: bool | None 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

73 json_schema_input_type: Any 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

74 

75 

76@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

77class RootValidatorDecoratorInfo: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

79 while building the pydantic-core schema. 

80 

81 Attributes: 

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

83 mode: The proposed validator mode. 

84 """ 

85 

86 decorator_repr: ClassVar[str] = '@root_validator' 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

87 mode: Literal['before', 'after'] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

88 

89 

90@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

91class FieldSerializerDecoratorInfo: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

93 while building the pydantic-core schema. 

94 

95 Attributes: 

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

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

98 mode: The proposed serializer mode. 

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

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

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

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

103 """ 

104 

105 decorator_repr: ClassVar[str] = '@field_serializer' 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

106 fields: tuple[str, ...] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

107 mode: Literal['plain', 'wrap'] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

108 return_type: Any 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

109 when_used: core_schema.WhenUsed 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

110 check_fields: bool | None 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

111 

112 

113@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

114class ModelSerializerDecoratorInfo: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

116 while building the pydantic-core schema. 

117 

118 Attributes: 

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

120 mode: The proposed serializer mode. 

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

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

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

124 """ 

125 

126 decorator_repr: ClassVar[str] = '@model_serializer' 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

127 mode: Literal['plain', 'wrap'] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

128 return_type: Any 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

129 when_used: core_schema.WhenUsed 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

130 

131 

132@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

133class ModelValidatorDecoratorInfo: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

135 while building the pydantic-core schema. 

136 

137 Attributes: 

138 decorator_repr: A class variable representing the decorator string, '@model_validator'. 

139 mode: The proposed serializer mode. 

140 """ 

141 

142 decorator_repr: ClassVar[str] = '@model_validator' 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

143 mode: Literal['wrap', 'before', 'after'] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

144 

145 

146DecoratorInfo: TypeAlias = """Union[ 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

147 ValidatorDecoratorInfo, 

148 FieldValidatorDecoratorInfo, 

149 RootValidatorDecoratorInfo, 

150 FieldSerializerDecoratorInfo, 

151 ModelSerializerDecoratorInfo, 

152 ModelValidatorDecoratorInfo, 

153 ComputedFieldInfo, 

154]""" 

155 

156ReturnType = TypeVar('ReturnType') 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

157DecoratedType: TypeAlias = ( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

159) 

160 

161 

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

163class PydanticDescriptorProxy(Generic[ReturnType]): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

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

166 from the class' attributes. 

167 

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

169 which makes it transparent for classmethods and staticmethods. 

170 

171 Attributes: 

172 wrapped: The decorator that has to be wrapped. 

173 decorator_info: The decorator info. 

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

175 """ 

176 

177 wrapped: DecoratedType[ReturnType] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

178 decorator_info: DecoratorInfo 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

179 shim: Callable[[Callable[..., Any]], Callable[..., Any]] | None = None 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

180 

181 def __post_init__(self): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

182 for attr in 'setter', 'deleter': 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

183 if hasattr(self.wrapped, attr): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

184 f = partial(self._call_wrapped_attr, name=attr) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

185 setattr(self, attr, f) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

186 

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

188 self.wrapped = getattr(self.wrapped, name)(func) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

189 if isinstance(self.wrapped, property): 189 ↛ 195line 189 didn't jump to line 195 because the condition on line 189 was always true1stabcdefABCzyuvghijklDEFwxmnopqrGHI

190 # update ComputedFieldInfo.wrapped_property 

191 from ..fields import ComputedFieldInfo 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

192 

193 if isinstance(self.decorator_info, ComputedFieldInfo): 193 ↛ 195line 193 didn't jump to line 195 because the condition on line 193 was always true1stabcdefABCzyuvghijklDEFwxmnopqrGHI

194 self.decorator_info.wrapped_property = self.wrapped 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

195 return self 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

196 

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

198 try: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

199 return self.wrapped.__get__(obj, obj_type) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

200 except AttributeError: 1stabcdefzyuvghijklwxmnopqr

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

202 return self.wrapped # type: ignore[return-value] 1stabcdefzyuvghijklwxmnopqr

203 

204 def __set_name__(self, instance: Any, name: str) -> None: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

205 if hasattr(self.wrapped, '__set_name__'): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

207 

208 def __getattr__(self, name: str, /) -> Any: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

210 return getattr(self.wrapped, name) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

211 

212 

213DecoratorInfoType = TypeVar('DecoratorInfoType', bound=DecoratorInfo) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

214 

215 

216@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

217class Decorator(Generic[DecoratorInfoType]): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

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

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

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

222 

223 Attributes: 

224 cls_ref: The class ref. 

225 cls_var_name: The decorated function name. 

226 func: The decorated function. 

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

228 info: The decorator info. 

229 """ 

230 

231 cls_ref: str 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

232 cls_var_name: str 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

233 func: Callable[..., Any] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

234 shim: Callable[[Any], Any] | None 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

235 info: DecoratorInfoType 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

236 

237 @staticmethod 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

238 def build( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

239 cls_: Any, 

240 *, 

241 cls_var_name: str, 

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

243 info: DecoratorInfoType, 

244 ) -> Decorator[DecoratorInfoType]: 

245 """Build a new decorator. 

246 

247 Args: 

248 cls_: The class. 

249 cls_var_name: The decorated function name. 

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

251 info: The decorator info. 

252 

253 Returns: 

254 The new decorator instance. 

255 """ 

256 func = get_attribute_from_bases(cls_, cls_var_name) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

257 if shim is not None: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

258 func = shim(func) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

259 func = unwrap_wrapped_function(func, unwrap_partial=False) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

260 if not callable(func): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

261 # This branch will get hit for classmethod properties 

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

263 if isinstance(attribute, PydanticDescriptorProxy): 263 ↛ 265line 263 didn't jump to line 265 because the condition on line 263 was always true1stabcdefzyuvghijklwxmnopqr

264 func = unwrap_wrapped_function(attribute.wrapped) 1stabcdefzyuvghijklwxmnopqr

265 return Decorator( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

266 cls_ref=get_type_ref(cls_), 

267 cls_var_name=cls_var_name, 

268 func=func, 

269 shim=shim, 

270 info=info, 

271 ) 

272 

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

274 """Bind the decorator to a class. 

275 

276 Args: 

277 cls: the class. 

278 

279 Returns: 

280 The new decorator instance. 

281 """ 

282 return self.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

283 cls, 

284 cls_var_name=self.cls_var_name, 

285 shim=self.shim, 

286 info=self.info, 

287 ) 

288 

289 

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

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

292 

293 Args: 

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

295 

296 Returns: 

297 The base classes. 

298 """ 

299 if is_typeddict(tp): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

300 return tp.__orig_bases__ # type: ignore 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

301 try: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

302 return tp.__bases__ 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

303 except AttributeError: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

304 return () 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

305 

306 

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

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

309 

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

311 """ 

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

313 # but also because it helps verify the implementation below 

314 if not is_typeddict(tp): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

315 try: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

316 return tp.__mro__ 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

317 except AttributeError: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

318 # GenericAlias and some other cases 

319 pass 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

320 

321 bases = get_bases(tp) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

322 return (tp,) + mro_for_bases(bases) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

323 

324 

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

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

327 while True: 1abcdefABCyghijklDEFJKLMNOmnopqrGHI

328 non_empty = [seq for seq in seqs if seq] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

329 if not non_empty: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

331 return 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

332 candidate: type[Any] | None = None 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

333 for seq in non_empty: # Find merge candidates among seq heads. 333 ↛ 341line 333 didn't jump to line 341 because the loop on line 333 didn't complete1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

334 candidate = seq[0] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

336 if not_head: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

337 # Reject the candidate. 

338 candidate = None 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

339 else: 

340 break 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

341 if not candidate: 341 ↛ 342line 341 didn't jump to line 342 because the condition on line 341 was never true1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

343 yield candidate 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

344 for seq in non_empty: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

345 # Remove candidate. 

346 if seq[0] == candidate: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

347 seq.popleft() 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

348 

349 seqs = [deque(mro(base)) for base in bases] + [deque(bases)] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

350 return tuple(merge_seqs(seqs)) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

351 

352 

353_sentinel = object() 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

354 

355 

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

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

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

359 

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

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

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

363 from its bases (as done here). 

364 

365 Args: 

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

367 name: The name of the attribute to retrieve. 

368 

369 Returns: 

370 Any: The attribute value, if found. 

371 

372 Raises: 

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

374 """ 

375 if isinstance(tp, tuple): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

376 for base in mro_for_bases(tp): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

377 attribute = base.__dict__.get(name, _sentinel) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

378 if attribute is not _sentinel: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

379 attribute_get = getattr(attribute, '__get__', None) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

380 if attribute_get is not None: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

381 return attribute_get(None, tp) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

382 return attribute 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

383 raise AttributeError(f'{name} not found in {tp}') 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

384 else: 

385 try: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

386 return getattr(tp, name) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

387 except AttributeError: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

388 return get_attribute_from_bases(mro(tp), name) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

389 

390 

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

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

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

394 us to get the original function for classmethod properties. 

395 

396 Args: 

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

398 name: The name of the attribute to retrieve. 

399 

400 Returns: 

401 Any: The attribute value, if found. 

402 

403 Raises: 

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

405 """ 

406 for base in reversed(mro(tp)): 406 ↛ 409line 406 didn't jump to line 409 because the loop on line 406 didn't complete1stabcdefzyuvghijklwxmnopqr

407 if name in base.__dict__: 1stabcdefzyuvghijklwxmnopqr

408 return base.__dict__[name] 1stabcdefzyuvghijklwxmnopqr

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

410 

411 

412@dataclass(**slots_true) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

413class DecoratorInfos: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

415 

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

417 not the field name! 

418 """ 

419 

420 validators: dict[str, Decorator[ValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

421 field_validators: dict[str, Decorator[FieldValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

422 root_validators: dict[str, Decorator[RootValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

423 field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

424 model_serializers: dict[str, Decorator[ModelSerializerDecoratorInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

425 model_validators: dict[str, Decorator[ModelValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

426 computed_fields: dict[str, Decorator[ComputedFieldInfo]] = field(default_factory=dict) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

427 

428 @staticmethod 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

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

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

432 that called us 

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

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

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

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

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

438 works with inheritance. 

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

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

441 """ 

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

443 res = DecoratorInfos() 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

444 for base in reversed(mro(model_dc)[1:]): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

445 existing: DecoratorInfos | None = base.__dict__.get('__pydantic_decorators__') 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

446 if existing is None: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

447 existing = DecoratorInfos.build(base) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

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

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

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

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

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

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

455 

456 to_replace: list[tuple[str, Any]] = [] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

457 

458 for var_name, var_value in vars(model_dc).items(): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

459 if isinstance(var_value, PydanticDescriptorProxy): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

460 info = var_value.decorator_info 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

461 if isinstance(info, ValidatorDecoratorInfo): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

462 res.validators[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

464 ) 

465 elif isinstance(info, FieldValidatorDecoratorInfo): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

466 res.field_validators[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

468 ) 

469 elif isinstance(info, RootValidatorDecoratorInfo): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

470 res.root_validators[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

472 ) 

473 elif isinstance(info, FieldSerializerDecoratorInfo): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

475 for field_serializer_decorator in res.field_serializers.values(): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

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

478 # and are treated as overrides 

479 if field_serializer_decorator.cls_var_name == var_name: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

480 continue 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

481 for f in info.fields: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

482 if f in field_serializer_decorator.info.fields: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

483 raise PydanticUserError( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

484 'Multiple field serializer functions were defined ' 

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

486 code='multiple-field-serializers', 

487 ) 

488 res.field_serializers[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

490 ) 

491 elif isinstance(info, ModelValidatorDecoratorInfo): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

492 res.model_validators[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

494 ) 

495 elif isinstance(info, ModelSerializerDecoratorInfo): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

496 res.model_serializers[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

498 ) 

499 else: 

500 from ..fields import ComputedFieldInfo 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

501 

502 isinstance(var_value, ComputedFieldInfo) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

503 res.computed_fields[var_name] = Decorator.build( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

505 ) 

506 to_replace.append((var_name, var_value.wrapped)) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

507 if to_replace: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

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

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

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

512 model_dc.__pydantic_decorators__ = res 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

513 for name, value in to_replace: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

514 setattr(model_dc, name, value) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

515 return res 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

516 

517 

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

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

520 

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

522 

523 Args: 

524 validator: The validator function to inspect. 

525 mode: The proposed validator mode. 

526 

527 Returns: 

528 Whether the validator takes an info argument. 

529 """ 

530 try: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

531 sig = signature(validator) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

532 except (ValueError, TypeError): 1stabcdefABCuvghijklDEFwxmnopqrGHI

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

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

535 return False 1stabcdefABCuvghijklDEFwxmnopqrGHI

536 n_positional = count_positional_required_params(sig) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

537 if mode == 'wrap': 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

538 if n_positional == 3: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

539 return True 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

540 elif n_positional == 2: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

541 return False 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

542 else: 

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

544 if n_positional == 2: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

545 return True 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

546 elif n_positional == 1: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

547 return False 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

548 

549 raise PydanticUserError( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

551 code='validator-signature', 

552 ) 

553 

554 

555def inspect_field_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> tuple[bool, bool]: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

557 and whether it takes an info argument. 

558 

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

560 

561 Args: 

562 serializer: The serializer function to inspect. 

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

564 

565 Returns: 

566 Tuple of (is_field_serializer, info_arg). 

567 """ 

568 try: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

569 sig = signature(serializer) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

570 except (ValueError, TypeError): 

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

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

573 return (False, False) 

574 

575 first = next(iter(sig.parameters.values()), None) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

576 is_field_serializer = first is not None and first.name == 'self' 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

577 

578 n_positional = count_positional_required_params(sig) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

579 if is_field_serializer: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

580 # -1 to correct for self parameter 

581 info_arg = _serializer_info_arg(mode, n_positional - 1) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

582 else: 

583 info_arg = _serializer_info_arg(mode, n_positional) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

584 

585 if info_arg is None: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

586 raise PydanticUserError( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

588 code='field-serializer-signature', 

589 ) 

590 

591 return is_field_serializer, info_arg 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

592 

593 

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

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

596 

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

598 

599 Args: 

600 serializer: The serializer function to check. 

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

602 

603 Returns: 

604 info_arg 

605 """ 

606 try: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

607 sig = signature(serializer) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

608 except (ValueError, TypeError): 1stabcdefABCuvghijklDEFwxmnopqrGHI

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

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

611 return False 1stabcdefABCuvghijklDEFwxmnopqrGHI

612 info_arg = _serializer_info_arg(mode, count_positional_required_params(sig)) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

613 if info_arg is None: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

614 raise PydanticUserError( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

616 code='field-serializer-signature', 

617 ) 

618 else: 

619 return info_arg 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

620 

621 

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

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

624 

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

626 

627 Args: 

628 serializer: The serializer function to check. 

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

630 

631 Returns: 

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

633 """ 

634 if isinstance(serializer, (staticmethod, classmethod)) or not is_instance_method_from_sig(serializer): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

635 raise PydanticUserError( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

637 ) 

638 

639 sig = signature(serializer) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

640 info_arg = _serializer_info_arg(mode, count_positional_required_params(sig)) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

641 if info_arg is None: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

642 raise PydanticUserError( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

644 code='model-serializer-signature', 

645 ) 

646 else: 

647 return info_arg 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

648 

649 

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

651 if mode == 'plain': 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

652 if n_positional == 1: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

654 return False 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

655 elif n_positional == 2: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

657 return True 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

658 else: 

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

660 if n_positional == 2: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

662 return False 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

663 elif n_positional == 3: 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

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

665 return True 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

666 

667 return None 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

668 

669 

670AnyDecoratorCallable: TypeAlias = ( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

672) 

673 

674 

675def is_instance_method_from_sig(function: AnyDecoratorCallable) -> bool: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

677 

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

679 function is `self`. 

680 

681 Args: 

682 function: The function to check. 

683 

684 Returns: 

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

686 """ 

687 sig = signature(unwrap_wrapped_function(function)) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

688 first = next(iter(sig.parameters.values()), None) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

689 if first and first.name == 'self': 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

690 return True 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

691 return False 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

692 

693 

694def ensure_classmethod_based_on_signature(function: AnyDecoratorCallable) -> Any: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

696 

697 Args: 

698 function: The function to apply the decorator on. 

699 

700 Return: 

701 The `@classmethod` decorator applied function. 

702 """ 

703 if not isinstance( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

704 unwrap_wrapped_function(function, unwrap_class_static_method=False), classmethod 

705 ) and _is_classmethod_from_sig(function): 

706 return classmethod(function) # type: ignore[arg-type] 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

707 return function 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

708 

709 

710def _is_classmethod_from_sig(function: AnyDecoratorCallable) -> bool: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

711 sig = signature(unwrap_wrapped_function(function)) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

712 first = next(iter(sig.parameters.values()), None) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

713 if first and first.name == 'cls': 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

714 return True 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

715 return False 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

716 

717 

718def unwrap_wrapped_function( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

719 func: Any, 

720 *, 

721 unwrap_partial: bool = True, 

722 unwrap_class_static_method: bool = True, 

723) -> Any: 

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

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

726 

727 Args: 

728 func: The function to unwrap. 

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

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

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

732 

733 Returns: 

734 The underlying function of the wrapped function. 

735 """ 

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

737 unwrap_types = ( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

738 (property, cached_property) 

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

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

741 ) 

742 

743 while isinstance(func, unwrap_types): 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

744 if unwrap_class_static_method and isinstance(func, (classmethod, staticmethod)): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

745 func = func.__func__ 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

746 elif isinstance(func, (partial, partialmethod)): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

747 func = func.func 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

748 elif isinstance(func, property): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

749 func = func.fget # arbitrary choice, convenient for computed fields 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

750 else: 

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

752 assert isinstance(func, cached_property) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

753 func = func.func # type: ignore 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

754 

755 return func 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

756 

757 

758_function_like = ( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

759 partial, 

760 partialmethod, 

761 types.FunctionType, 

762 types.BuiltinFunctionType, 

763 types.MethodType, 

764 types.WrapperDescriptorType, 

765 types.MethodWrapperType, 

766 types.MemberDescriptorType, 

767) 

768 

769 

770def get_callable_return_type( 1stabcdefABCuvghijklDEFPJKLMNOwxmnopqrGHI

771 callable_obj: Any, 

772 globalns: GlobalsNamespace | None = None, 

773 localns: MappingNamespace | None = None, 

774) -> Any | PydanticUndefinedType: 

775 """Get the callable return type. 

776 

777 Args: 

778 callable_obj: The callable to analyze. 

779 globalns: The globals namespace to use during type annotation evaluation. 

780 localns: The locals namespace to use during type annotation evaluation. 

781 

782 Returns: 

783 The function return type. 

784 """ 

785 if isinstance(callable_obj, type): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

786 # types are callables, and we assume the return type 

787 # is the type itself (e.g. `int()` results in an instance of `int`). 

788 return callable_obj 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

789 

790 if not isinstance(callable_obj, _function_like): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

791 call_func = getattr(type(callable_obj), '__call__', None) # noqa: B004 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

792 if call_func is not None: 792 ↛ 795line 792 didn't jump to line 795 because the condition on line 792 was always true1stabcdefABCzyuvghijklDEFwxmnopqrGHI

793 callable_obj = call_func 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

794 

795 hints = get_function_type_hints( 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

796 unwrap_wrapped_function(callable_obj), 

797 include_keys={'return'}, 

798 globalns=globalns, 

799 localns=localns, 

800 ) 

801 return hints.get('return', PydanticUndefined) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

802 

803 

804def count_positional_required_params(sig: Signature) -> int: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

806 

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

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

809 even if a default value exists. 

810 

811 Returns: 

812 The number of positional arguments of a signature. 

813 """ 

814 parameters = list(sig.parameters.values()) 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

815 return sum( 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

816 1 

817 for param in parameters 

818 if can_be_positional(param) 

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

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

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

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

823 ) 

824 

825 

826def ensure_property(f: Any) -> Any: 1stabcdefABCzyuvghijklDEFPJKLMNOwxmnopqrGHI

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

828 

829 Args: 

830 f: The function to check. 

831 

832 Returns: 

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

834 """ 

835 if ismethoddescriptor(f) or isdatadescriptor(f): 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

836 return f 1stabcdefABCzyuvghijklDEFwxmnopqrGHI

837 else: 

838 return property(f) 1stabcdefABCzyuvghijklDEFwxmnopqrGHI