Coverage for pydantic/_internal/_decorators.py: 97.72%
315 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
1"""Logic related to validators applied to models etc. via the `@field_validator` and `@model_validator` decorators."""
3from __future__ import annotations as _annotations 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
5from collections import deque 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
6from collections.abc import Iterable 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
7from dataclasses import dataclass, field 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
8from functools import cached_property, partial, partialmethod 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
9from inspect import Parameter, Signature, isdatadescriptor, ismethoddescriptor, signature 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
10from itertools import islice 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
11from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, Literal, TypeVar, Union 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
13from pydantic_core import PydanticUndefined, core_schema 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
14from typing_extensions import TypeAlias, is_typeddict 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
16from ..errors import PydanticUserError 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
17from ._core_utils import get_type_ref 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
18from ._internal_dataclass import slots_true 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
19from ._namespace_utils import GlobalsNamespace, MappingNamespace 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
20from ._typing_extra import get_function_type_hints 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
21from ._utils import can_be_positional 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
23if TYPE_CHECKING: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
24 from ..fields import ComputedFieldInfo
25 from ..functional_validators import FieldValidatorModes
28@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
29class ValidatorDecoratorInfo: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
30 """A container for data from `@validator` so that we can access it
31 while building the pydantic-core schema.
33 Attributes:
34 decorator_repr: A class variable representing the decorator string, '@validator'.
35 fields: A tuple of field names the validator should be called on.
36 mode: The proposed validator mode.
37 each_item: For complex objects (sets, lists etc.) whether to validate individual
38 elements rather than the whole object.
39 always: Whether this method and other validators should be called even if the value is missing.
40 check_fields: Whether to check that the fields actually exist on the model.
41 """
43 decorator_repr: ClassVar[str] = '@validator' 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
45 fields: tuple[str, ...] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
46 mode: Literal['before', 'after'] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
47 each_item: bool 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
48 always: bool 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
49 check_fields: bool | None 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
52@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
53class FieldValidatorDecoratorInfo: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
54 """A container for data from `@field_validator` so that we can access it
55 while building the pydantic-core schema.
57 Attributes:
58 decorator_repr: A class variable representing the decorator string, '@field_validator'.
59 fields: A tuple of field names the validator should be called on.
60 mode: The proposed validator mode.
61 check_fields: Whether to check that the fields actually exist on the model.
62 json_schema_input_type: The input type of the function. This is only used to generate
63 the appropriate JSON Schema (in validation mode) and can only specified
64 when `mode` is either `'before'`, `'plain'` or `'wrap'`.
65 """
67 decorator_repr: ClassVar[str] = '@field_validator' 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
69 fields: tuple[str, ...] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
70 mode: FieldValidatorModes 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
71 check_fields: bool | None 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
72 json_schema_input_type: Any 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
75@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
76class RootValidatorDecoratorInfo: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
77 """A container for data from `@root_validator` so that we can access it
78 while building the pydantic-core schema.
80 Attributes:
81 decorator_repr: A class variable representing the decorator string, '@root_validator'.
82 mode: The proposed validator mode.
83 """
85 decorator_repr: ClassVar[str] = '@root_validator' 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
86 mode: Literal['before', 'after'] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
89@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
90class FieldSerializerDecoratorInfo: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
91 """A container for data from `@field_serializer` so that we can access it
92 while building the pydantic-core schema.
94 Attributes:
95 decorator_repr: A class variable representing the decorator string, '@field_serializer'.
96 fields: A tuple of field names the serializer should be called on.
97 mode: The proposed serializer mode.
98 return_type: The type of the serializer's return value.
99 when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`,
100 and `'json-unless-none'`.
101 check_fields: Whether to check that the fields actually exist on the model.
102 """
104 decorator_repr: ClassVar[str] = '@field_serializer' 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
105 fields: tuple[str, ...] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
106 mode: Literal['plain', 'wrap'] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
107 return_type: Any 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
108 when_used: core_schema.WhenUsed 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
109 check_fields: bool | None 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
112@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
113class ModelSerializerDecoratorInfo: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
114 """A container for data from `@model_serializer` so that we can access it
115 while building the pydantic-core schema.
117 Attributes:
118 decorator_repr: A class variable representing the decorator string, '@model_serializer'.
119 mode: The proposed serializer mode.
120 return_type: The type of the serializer's return value.
121 when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`,
122 and `'json-unless-none'`.
123 """
125 decorator_repr: ClassVar[str] = '@model_serializer' 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
126 mode: Literal['plain', 'wrap'] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
127 return_type: Any 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
128 when_used: core_schema.WhenUsed 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
131@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
132class ModelValidatorDecoratorInfo: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
133 """A container for data from `@model_validator` so that we can access it
134 while building the pydantic-core schema.
136 Attributes:
137 decorator_repr: A class variable representing the decorator string, '@model_validator'.
138 mode: The proposed serializer mode.
139 """
141 decorator_repr: ClassVar[str] = '@model_validator' 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
142 mode: Literal['wrap', 'before', 'after'] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
145DecoratorInfo: TypeAlias = """Union[ 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
146 ValidatorDecoratorInfo,
147 FieldValidatorDecoratorInfo,
148 RootValidatorDecoratorInfo,
149 FieldSerializerDecoratorInfo,
150 ModelSerializerDecoratorInfo,
151 ModelValidatorDecoratorInfo,
152 ComputedFieldInfo,
153]"""
155ReturnType = TypeVar('ReturnType') 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
156DecoratedType: TypeAlias = ( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
157 'Union[classmethod[Any, Any, ReturnType], staticmethod[Any, ReturnType], Callable[..., ReturnType], property]'
158)
161@dataclass # can't use slots here since we set attributes on `__post_init__` 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
162class PydanticDescriptorProxy(Generic[ReturnType]): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
163 """Wrap a classmethod, staticmethod, property or unbound function
164 and act as a descriptor that allows us to detect decorated items
165 from the class' attributes.
167 This class' __get__ returns the wrapped item's __get__ result,
168 which makes it transparent for classmethods and staticmethods.
170 Attributes:
171 wrapped: The decorator that has to be wrapped.
172 decorator_info: The decorator info.
173 shim: A wrapper function to wrap V1 style function.
174 """
176 wrapped: DecoratedType[ReturnType] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
177 decorator_info: DecoratorInfo 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
178 shim: Callable[[Callable[..., Any]], Callable[..., Any]] | None = None 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
180 def __post_init__(self): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
181 for attr in 'setter', 'deleter': 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
182 if hasattr(self.wrapped, attr): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
183 f = partial(self._call_wrapped_attr, name=attr) 1stabcdefABzyuvghijklCDwxmnopqrEF
184 setattr(self, attr, f) 1stabcdefABzyuvghijklCDwxmnopqrEF
186 def _call_wrapped_attr(self, func: Callable[[Any], None], *, name: str) -> PydanticDescriptorProxy[ReturnType]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
187 self.wrapped = getattr(self.wrapped, name)(func) 1stabcdefABzyuvghijklCDwxmnopqrEF
188 if isinstance(self.wrapped, property): 188 ↛ 194line 188 didn't jump to line 194 because the condition on line 188 was always true1stabcdefABzyuvghijklCDwxmnopqrEF
189 # update ComputedFieldInfo.wrapped_property
190 from ..fields import ComputedFieldInfo 1stabcdefABzyuvghijklCDwxmnopqrEF
192 if isinstance(self.decorator_info, ComputedFieldInfo): 192 ↛ 194line 192 didn't jump to line 194 because the condition on line 192 was always true1stabcdefABzyuvghijklCDwxmnopqrEF
193 self.decorator_info.wrapped_property = self.wrapped 1stabcdefABzyuvghijklCDwxmnopqrEF
194 return self 1stabcdefABzyuvghijklCDwxmnopqrEF
196 def __get__(self, obj: object | None, obj_type: type[object] | None = None) -> PydanticDescriptorProxy[ReturnType]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
197 try: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
198 return self.wrapped.__get__(obj, obj_type) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
199 except AttributeError: 1stabcdefzyuvghijklwxmnopqr
200 # not a descriptor, e.g. a partial object
201 return self.wrapped # type: ignore[return-value] 1stabcdefzyuvghijklwxmnopqr
203 def __set_name__(self, instance: Any, name: str) -> None: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
204 if hasattr(self.wrapped, '__set_name__'): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
205 self.wrapped.__set_name__(instance, name) # pyright: ignore[reportFunctionMemberAccess] 1stabcdefABzyuvghijklCDwxmnopqrEF
207 def __getattr__(self, name: str, /) -> Any: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
208 """Forward checks for __isabstractmethod__ and such."""
209 return getattr(self.wrapped, name) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
212DecoratorInfoType = TypeVar('DecoratorInfoType', bound=DecoratorInfo) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
215@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
216class Decorator(Generic[DecoratorInfoType]): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
217 """A generic container class to join together the decorator metadata
218 (metadata from decorator itself, which we have when the
219 decorator is called but not when we are building the core-schema)
220 and the bound function (which we have after the class itself is created).
222 Attributes:
223 cls_ref: The class ref.
224 cls_var_name: The decorated function name.
225 func: The decorated function.
226 shim: A wrapper function to wrap V1 style function.
227 info: The decorator info.
228 """
230 cls_ref: str 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
231 cls_var_name: str 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
232 func: Callable[..., Any] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
233 shim: Callable[[Any], Any] | None 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
234 info: DecoratorInfoType 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
236 @staticmethod 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
237 def build( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
238 cls_: Any,
239 *,
240 cls_var_name: str,
241 shim: Callable[[Any], Any] | None,
242 info: DecoratorInfoType,
243 ) -> Decorator[DecoratorInfoType]:
244 """Build a new decorator.
246 Args:
247 cls_: The class.
248 cls_var_name: The decorated function name.
249 shim: A wrapper function to wrap V1 style function.
250 info: The decorator info.
252 Returns:
253 The new decorator instance.
254 """
255 func = get_attribute_from_bases(cls_, cls_var_name) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
256 if shim is not None: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
257 func = shim(func) 1stabcdefABzyuvghijklCDwxmnopqrEF
258 func = unwrap_wrapped_function(func, unwrap_partial=False) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
259 if not callable(func): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
260 # This branch will get hit for classmethod properties
261 attribute = get_attribute_from_base_dicts(cls_, cls_var_name) # prevents the binding call to `__get__` 1stabcdefzyuvghijklwxmnopqr
262 if isinstance(attribute, PydanticDescriptorProxy): 262 ↛ 264line 262 didn't jump to line 264 because the condition on line 262 was always true1stabcdefzyuvghijklwxmnopqr
263 func = unwrap_wrapped_function(attribute.wrapped) 1stabcdefzyuvghijklwxmnopqr
264 return Decorator( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
265 cls_ref=get_type_ref(cls_),
266 cls_var_name=cls_var_name,
267 func=func,
268 shim=shim,
269 info=info,
270 )
272 def bind_to_cls(self, cls: Any) -> Decorator[DecoratorInfoType]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
273 """Bind the decorator to a class.
275 Args:
276 cls: the class.
278 Returns:
279 The new decorator instance.
280 """
281 return self.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
282 cls,
283 cls_var_name=self.cls_var_name,
284 shim=self.shim,
285 info=self.info,
286 )
289def get_bases(tp: type[Any]) -> tuple[type[Any], ...]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
290 """Get the base classes of a class or typeddict.
292 Args:
293 tp: The type or class to get the bases.
295 Returns:
296 The base classes.
297 """
298 if is_typeddict(tp): 1stabcdefABzyuvghijklCDwxmnopqrEF
299 return tp.__orig_bases__ # type: ignore 1stabcdefABzyuvghijklCDwxmnopqrEF
300 try: 1stabcdefABzyuvghijklCDwxmnopqrEF
301 return tp.__bases__ 1stabcdefABzyuvghijklCDwxmnopqrEF
302 except AttributeError: 1stabcdefABzyuvghijklCDwxmnopqrEF
303 return () 1stabcdefABzyuvghijklCDwxmnopqrEF
306def mro(tp: type[Any]) -> tuple[type[Any], ...]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
307 """Calculate the Method Resolution Order of bases using the C3 algorithm.
309 See https://www.python.org/download/releases/2.3/mro/
310 """
311 # try to use the existing mro, for performance mainly
312 # but also because it helps verify the implementation below
313 if not is_typeddict(tp): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
314 try: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
315 return tp.__mro__ 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
316 except AttributeError: 1stabcdefABzyuvghijklCDwxmnopqrEF
317 # GenericAlias and some other cases
318 pass 1stabcdefABzyuvghijklCDwxmnopqrEF
320 bases = get_bases(tp) 1stabcdefABzyuvghijklCDwxmnopqrEF
321 return (tp,) + mro_for_bases(bases) 1stabcdefABzyuvghijklCDwxmnopqrEF
324def mro_for_bases(bases: tuple[type[Any], ...]) -> tuple[type[Any], ...]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
325 def merge_seqs(seqs: list[deque[type[Any]]]) -> Iterable[type[Any]]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
326 while True: 1abcdefAByghijklCDGHIJKLmnopqrEF
327 non_empty = [seq for seq in seqs if seq] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
328 if not non_empty: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
329 # Nothing left to process, we're done.
330 return 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
331 candidate: type[Any] | None = None 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
332 for seq in non_empty: # Find merge candidates among seq heads. 332 ↛ 340line 332 didn't jump to line 340 because the loop on line 332 didn't complete1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
333 candidate = seq[0] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
334 not_head = [s for s in non_empty if candidate in islice(s, 1, None)] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
335 if not_head: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
336 # Reject the candidate.
337 candidate = None 1stabcdefABzyuvghijklCDwxmnopqrEF
338 else:
339 break 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
340 if not candidate: 340 ↛ 341line 340 didn't jump to line 341 because the condition on line 340 was never true1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
341 raise TypeError('Inconsistent hierarchy, no C3 MRO is possible')
342 yield candidate 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
343 for seq in non_empty: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
344 # Remove candidate.
345 if seq[0] == candidate: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
346 seq.popleft() 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
348 seqs = [deque(mro(base)) for base in bases] + [deque(bases)] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
349 return tuple(merge_seqs(seqs)) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
352_sentinel = object() 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
355def get_attribute_from_bases(tp: type[Any] | tuple[type[Any], ...], name: str) -> Any: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
356 """Get the attribute from the next class in the MRO that has it,
357 aiming to simulate calling the method on the actual class.
359 The reason for iterating over the mro instead of just getting
360 the attribute (which would do that for us) is to support TypedDict,
361 which lacks a real __mro__, but can have a virtual one constructed
362 from its bases (as done here).
364 Args:
365 tp: The type or class to search for the attribute. If a tuple, this is treated as a set of base classes.
366 name: The name of the attribute to retrieve.
368 Returns:
369 Any: The attribute value, if found.
371 Raises:
372 AttributeError: If the attribute is not found in any class in the MRO.
373 """
374 if isinstance(tp, tuple): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
375 for base in mro_for_bases(tp): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
376 attribute = base.__dict__.get(name, _sentinel) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
377 if attribute is not _sentinel: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
378 attribute_get = getattr(attribute, '__get__', None) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
379 if attribute_get is not None: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
380 return attribute_get(None, tp) 1stabcdefABzyuvghijklCDwxmnopqrEF
381 return attribute 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
382 raise AttributeError(f'{name} not found in {tp}') 1stabcdefABzyuvghijklCDwxmnopqrEF
383 else:
384 try: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
385 return getattr(tp, name) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
386 except AttributeError: 1stabcdefABzyuvghijklCDwxmnopqrEF
387 return get_attribute_from_bases(mro(tp), name) 1stabcdefABzyuvghijklCDwxmnopqrEF
390def get_attribute_from_base_dicts(tp: type[Any], name: str) -> Any: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
391 """Get an attribute out of the `__dict__` following the MRO.
392 This prevents the call to `__get__` on the descriptor, and allows
393 us to get the original function for classmethod properties.
395 Args:
396 tp: The type or class to search for the attribute.
397 name: The name of the attribute to retrieve.
399 Returns:
400 Any: The attribute value, if found.
402 Raises:
403 KeyError: If the attribute is not found in any class's `__dict__` in the MRO.
404 """
405 for base in reversed(mro(tp)): 405 ↛ 408line 405 didn't jump to line 408 because the loop on line 405 didn't complete1stabcdefzyuvghijklwxmnopqr
406 if name in base.__dict__: 1stabcdefzyuvghijklwxmnopqr
407 return base.__dict__[name] 1stabcdefzyuvghijklwxmnopqr
408 return tp.__dict__[name] # raise the error
411@dataclass(**slots_true) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
412class DecoratorInfos: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
413 """Mapping of name in the class namespace to decorator info.
415 note that the name in the class namespace is the function or attribute name
416 not the field name!
417 """
419 validators: dict[str, Decorator[ValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
420 field_validators: dict[str, Decorator[FieldValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
421 root_validators: dict[str, Decorator[RootValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
422 field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
423 model_serializers: dict[str, Decorator[ModelSerializerDecoratorInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
424 model_validators: dict[str, Decorator[ModelValidatorDecoratorInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
425 computed_fields: dict[str, Decorator[ComputedFieldInfo]] = field(default_factory=dict) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
427 @staticmethod 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
428 def build(model_dc: type[Any]) -> DecoratorInfos: # noqa: C901 (ignore complexity) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
429 """We want to collect all DecFunc instances that exist as
430 attributes in the namespace of the class (a BaseModel or dataclass)
431 that called us
432 But we want to collect these in the order of the bases
433 So instead of getting them all from the leaf class (the class that called us),
434 we traverse the bases from root (the oldest ancestor class) to leaf
435 and collect all of the instances as we go, taking care to replace
436 any duplicate ones with the last one we see to mimic how function overriding
437 works with inheritance.
438 If we do replace any functions we put the replacement into the position
439 the replaced function was in; that is, we maintain the order.
440 """
441 # reminder: dicts are ordered and replacement does not alter the order
442 res = DecoratorInfos() 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
443 for base in reversed(mro(model_dc)[1:]): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
444 existing: DecoratorInfos | None = base.__dict__.get('__pydantic_decorators__') 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
445 if existing is None: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
446 existing = DecoratorInfos.build(base) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
447 res.validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.validators.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
448 res.field_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.field_validators.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
449 res.root_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.root_validators.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
450 res.field_serializers.update({k: v.bind_to_cls(model_dc) for k, v in existing.field_serializers.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
451 res.model_serializers.update({k: v.bind_to_cls(model_dc) for k, v in existing.model_serializers.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
452 res.model_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.model_validators.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
453 res.computed_fields.update({k: v.bind_to_cls(model_dc) for k, v in existing.computed_fields.items()}) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
455 to_replace: list[tuple[str, Any]] = [] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
457 for var_name, var_value in vars(model_dc).items(): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
458 if isinstance(var_value, PydanticDescriptorProxy): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
459 info = var_value.decorator_info 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
460 if isinstance(info, ValidatorDecoratorInfo): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
461 res.validators[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
462 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info
463 )
464 elif isinstance(info, FieldValidatorDecoratorInfo): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
465 res.field_validators[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
466 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info
467 )
468 elif isinstance(info, RootValidatorDecoratorInfo): 1stabcdefABzyuvghijklCDwxmnopqrEF
469 res.root_validators[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
470 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info
471 )
472 elif isinstance(info, FieldSerializerDecoratorInfo): 1stabcdefABzyuvghijklCDwxmnopqrEF
473 # check whether a serializer function is already registered for fields
474 for field_serializer_decorator in res.field_serializers.values(): 1stabcdefABzyuvghijklCDwxmnopqrEF
475 # check that each field has at most one serializer function.
476 # serializer functions for the same field in subclasses are allowed,
477 # and are treated as overrides
478 if field_serializer_decorator.cls_var_name == var_name: 1stabcdefABzyuvghijklCDwxmnopqrEF
479 continue 1stabcdefABzyuvghijklCDwxmnopqrEF
480 for f in info.fields: 1stabcdefABzyuvghijklCDwxmnopqrEF
481 if f in field_serializer_decorator.info.fields: 1stabcdefABzyuvghijklCDwxmnopqrEF
482 raise PydanticUserError( 1stabcdefABzyuvghijklCDwxmnopqrEF
483 'Multiple field serializer functions were defined '
484 f'for field {f!r}, this is not allowed.',
485 code='multiple-field-serializers',
486 )
487 res.field_serializers[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
488 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info
489 )
490 elif isinstance(info, ModelValidatorDecoratorInfo): 1stabcdefABzyuvghijklCDwxmnopqrEF
491 res.model_validators[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
492 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info
493 )
494 elif isinstance(info, ModelSerializerDecoratorInfo): 1stabcdefABzyuvghijklCDwxmnopqrEF
495 res.model_serializers[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
496 model_dc, cls_var_name=var_name, shim=var_value.shim, info=info
497 )
498 else:
499 from ..fields import ComputedFieldInfo 1stabcdefABzyuvghijklCDwxmnopqrEF
501 isinstance(var_value, ComputedFieldInfo) 1stabcdefABzyuvghijklCDwxmnopqrEF
502 res.computed_fields[var_name] = Decorator.build( 1stabcdefABzyuvghijklCDwxmnopqrEF
503 model_dc, cls_var_name=var_name, shim=None, info=info
504 )
505 to_replace.append((var_name, var_value.wrapped)) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
506 if to_replace: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
507 # If we can save `__pydantic_decorators__` on the class we'll be able to check for it above
508 # so then we don't need to re-process the type, which means we can discard our descriptor wrappers
509 # and replace them with the thing they are wrapping (see the other setattr call below)
510 # which allows validator class methods to also function as regular class methods
511 model_dc.__pydantic_decorators__ = res 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
512 for name, value in to_replace: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
513 setattr(model_dc, name, value) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
514 return res 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
517def inspect_validator(validator: Callable[..., Any], mode: FieldValidatorModes) -> bool: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
518 """Look at a field or model validator function and determine whether it takes an info argument.
520 An error is raised if the function has an invalid signature.
522 Args:
523 validator: The validator function to inspect.
524 mode: The proposed validator mode.
526 Returns:
527 Whether the validator takes an info argument.
528 """
529 try: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
530 sig = signature(validator) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
531 except (ValueError, TypeError): 1stabcdefABuvghijklCDwxmnopqrEF
532 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
533 # In this case, we assume no info argument is present:
534 return False 1stabcdefABuvghijklCDwxmnopqrEF
535 n_positional = count_positional_required_params(sig) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
536 if mode == 'wrap': 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
537 if n_positional == 3: 1stabcdefABzyuvghijklCDwxmnopqrEF
538 return True 1stabcdefABzyuvghijklCDwxmnopqrEF
539 elif n_positional == 2: 1stabcdefABzyuvghijklCDwxmnopqrEF
540 return False 1stabcdefABzyuvghijklCDwxmnopqrEF
541 else:
542 assert mode in {'before', 'after', 'plain'}, f"invalid mode: {mode!r}, expected 'before', 'after' or 'plain" 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
543 if n_positional == 2: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
544 return True 1stabcdefABzyuvghijklCDwxmnopqrEF
545 elif n_positional == 1: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
546 return False 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
548 raise PydanticUserError( 1stabcdefABzyuvghijklCDwxmnopqrEF
549 f'Unrecognized field_validator function signature for {validator} with `mode={mode}`:{sig}',
550 code='validator-signature',
551 )
554def inspect_field_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> tuple[bool, bool]: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
555 """Look at a field serializer function and determine if it is a field serializer,
556 and whether it takes an info argument.
558 An error is raised if the function has an invalid signature.
560 Args:
561 serializer: The serializer function to inspect.
562 mode: The serializer mode, either 'plain' or 'wrap'.
564 Returns:
565 Tuple of (is_field_serializer, info_arg).
566 """
567 try: 1stabcdefABzyuvghijklCDwxmnopqrEF
568 sig = signature(serializer) 1stabcdefABzyuvghijklCDwxmnopqrEF
569 except (ValueError, TypeError):
570 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
571 # In this case, we assume no info argument is present and this is not a method:
572 return (False, False)
574 first = next(iter(sig.parameters.values()), None) 1stabcdefABzyuvghijklCDwxmnopqrEF
575 is_field_serializer = first is not None and first.name == 'self' 1stabcdefABzyuvghijklCDwxmnopqrEF
577 n_positional = count_positional_required_params(sig) 1stabcdefABzyuvghijklCDwxmnopqrEF
578 if is_field_serializer: 1stabcdefABzyuvghijklCDwxmnopqrEF
579 # -1 to correct for self parameter
580 info_arg = _serializer_info_arg(mode, n_positional - 1) 1stabcdefABzyuvghijklCDwxmnopqrEF
581 else:
582 info_arg = _serializer_info_arg(mode, n_positional) 1stabcdefABzyuvghijklCDwxmnopqrEF
584 if info_arg is None: 1stabcdefABzyuvghijklCDwxmnopqrEF
585 raise PydanticUserError( 1stabcdefABzyuvghijklCDwxmnopqrEF
586 f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}',
587 code='field-serializer-signature',
588 )
590 return is_field_serializer, info_arg 1stabcdefABzyuvghijklCDwxmnopqrEF
593def inspect_annotated_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
594 """Look at a serializer function used via `Annotated` and determine whether it takes an info argument.
596 An error is raised if the function has an invalid signature.
598 Args:
599 serializer: The serializer function to check.
600 mode: The serializer mode, either 'plain' or 'wrap'.
602 Returns:
603 info_arg
604 """
605 try: 1stabcdefABzyuvghijklCDwxmnopqrEF
606 sig = signature(serializer) 1stabcdefABzyuvghijklCDwxmnopqrEF
607 except (ValueError, TypeError): 1stabcdefABuvghijklCDwxmnopqrEF
608 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
609 # In this case, we assume no info argument is present:
610 return False 1stabcdefABuvghijklCDwxmnopqrEF
611 info_arg = _serializer_info_arg(mode, count_positional_required_params(sig)) 1stabcdefABzyuvghijklCDwxmnopqrEF
612 if info_arg is None: 1stabcdefABzyuvghijklCDwxmnopqrEF
613 raise PydanticUserError( 1stabcdefABzyuvghijklCDwxmnopqrEF
614 f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}',
615 code='field-serializer-signature',
616 )
617 else:
618 return info_arg 1stabcdefABzyuvghijklCDwxmnopqrEF
621def inspect_model_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
622 """Look at a model serializer function and determine whether it takes an info argument.
624 An error is raised if the function has an invalid signature.
626 Args:
627 serializer: The serializer function to check.
628 mode: The serializer mode, either 'plain' or 'wrap'.
630 Returns:
631 `info_arg` - whether the function expects an info argument.
632 """
633 if isinstance(serializer, (staticmethod, classmethod)) or not is_instance_method_from_sig(serializer): 1stabcdefABzyuvghijklCDwxmnopqrEF
634 raise PydanticUserError( 1stabcdefABzyuvghijklCDwxmnopqrEF
635 '`@model_serializer` must be applied to instance methods', code='model-serializer-instance-method'
636 )
638 sig = signature(serializer) 1stabcdefABzyuvghijklCDwxmnopqrEF
639 info_arg = _serializer_info_arg(mode, count_positional_required_params(sig)) 1stabcdefABzyuvghijklCDwxmnopqrEF
640 if info_arg is None: 1stabcdefABzyuvghijklCDwxmnopqrEF
641 raise PydanticUserError( 1stabcdefABzyuvghijklCDwxmnopqrEF
642 f'Unrecognized model_serializer function signature for {serializer} with `mode={mode}`:{sig}',
643 code='model-serializer-signature',
644 )
645 else:
646 return info_arg 1stabcdefABzyuvghijklCDwxmnopqrEF
649def _serializer_info_arg(mode: Literal['plain', 'wrap'], n_positional: int) -> bool | None: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
650 if mode == 'plain': 1stabcdefABzyuvghijklCDwxmnopqrEF
651 if n_positional == 1: 1stabcdefABzyuvghijklCDwxmnopqrEF
652 # (input_value: Any, /) -> Any
653 return False 1stabcdefABzyuvghijklCDwxmnopqrEF
654 elif n_positional == 2: 1stabcdefABzyuvghijklCDwxmnopqrEF
655 # (model: Any, input_value: Any, /) -> Any
656 return True 1stabcdefABzyuvghijklCDwxmnopqrEF
657 else:
658 assert mode == 'wrap', f"invalid mode: {mode!r}, expected 'plain' or 'wrap'" 1stabcdefABzyuvghijklCDwxmnopqrEF
659 if n_positional == 2: 1stabcdefABzyuvghijklCDwxmnopqrEF
660 # (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any
661 return False 1stabcdefABzyuvghijklCDwxmnopqrEF
662 elif n_positional == 3: 1stabcdefABzyuvghijklCDwxmnopqrEF
663 # (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any
664 return True 1stabcdefABzyuvghijklCDwxmnopqrEF
666 return None 1stabcdefABzyuvghijklCDwxmnopqrEF
669AnyDecoratorCallable: TypeAlias = ( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
670 'Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any], Callable[..., Any]]'
671)
674def is_instance_method_from_sig(function: AnyDecoratorCallable) -> bool: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
675 """Whether the function is an instance method.
677 It will consider a function as instance method if the first parameter of
678 function is `self`.
680 Args:
681 function: The function to check.
683 Returns:
684 `True` if the function is an instance method, `False` otherwise.
685 """
686 sig = signature(unwrap_wrapped_function(function)) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
687 first = next(iter(sig.parameters.values()), None) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
688 if first and first.name == 'self': 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
689 return True 1stabcdefABzyuvghijklCDwxmnopqrEF
690 return False 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
693def ensure_classmethod_based_on_signature(function: AnyDecoratorCallable) -> Any: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
694 """Apply the `@classmethod` decorator on the function.
696 Args:
697 function: The function to apply the decorator on.
699 Return:
700 The `@classmethod` decorator applied function.
701 """
702 if not isinstance( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
703 unwrap_wrapped_function(function, unwrap_class_static_method=False), classmethod
704 ) and _is_classmethod_from_sig(function):
705 return classmethod(function) # type: ignore[arg-type] 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
706 return function 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
709def _is_classmethod_from_sig(function: AnyDecoratorCallable) -> bool: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
710 sig = signature(unwrap_wrapped_function(function)) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
711 first = next(iter(sig.parameters.values()), None) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
712 if first and first.name == 'cls': 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
713 return True 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
714 return False 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
717def unwrap_wrapped_function( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
718 func: Any,
719 *,
720 unwrap_partial: bool = True,
721 unwrap_class_static_method: bool = True,
722) -> Any:
723 """Recursively unwraps a wrapped function until the underlying function is reached.
724 This handles property, functools.partial, functools.partialmethod, staticmethod, and classmethod.
726 Args:
727 func: The function to unwrap.
728 unwrap_partial: If True (default), unwrap partial and partialmethod decorators.
729 unwrap_class_static_method: If True (default), also unwrap classmethod and staticmethod
730 decorators. If False, only unwrap partial and partialmethod decorators.
732 Returns:
733 The underlying function of the wrapped function.
734 """
735 # Define the types we want to check against as a single tuple.
736 unwrap_types = ( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
737 (property, cached_property)
738 + ((partial, partialmethod) if unwrap_partial else ())
739 + ((staticmethod, classmethod) if unwrap_class_static_method else ())
740 )
742 while isinstance(func, unwrap_types): 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
743 if unwrap_class_static_method and isinstance(func, (classmethod, staticmethod)): 1stabcdefABzyuvghijklCDwxmnopqrEF
744 func = func.__func__ 1stabcdefABzyuvghijklCDwxmnopqrEF
745 elif isinstance(func, (partial, partialmethod)): 1stabcdefABzyuvghijklCDwxmnopqrEF
746 func = func.func 1stabcdefABzyuvghijklCDwxmnopqrEF
747 elif isinstance(func, property): 1stabcdefABzyuvghijklCDwxmnopqrEF
748 func = func.fget # arbitrary choice, convenient for computed fields 1stabcdefABzyuvghijklCDwxmnopqrEF
749 else:
750 # Make coverage happy as it can only get here in the last possible case
751 assert isinstance(func, cached_property) 1stabcdefABzyuvghijklCDwxmnopqrEF
752 func = func.func # type: ignore 1stabcdefABzyuvghijklCDwxmnopqrEF
754 return func 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
757def get_function_return_type( 1stabcdefABuvghijklCDMGHIJKLwxmnopqrEF
758 func: Any,
759 explicit_return_type: Any,
760 globalns: GlobalsNamespace | None = None,
761 localns: MappingNamespace | None = None,
762) -> Any:
763 """Get the function return type.
765 It gets the return type from the type annotation if `explicit_return_type` is `None`.
766 Otherwise, it returns `explicit_return_type`.
768 Args:
769 func: The function to get its return type.
770 explicit_return_type: The explicit return type.
771 globalns: The globals namespace to use during type annotation evaluation.
772 localns: The locals namespace to use during type annotation evaluation.
774 Returns:
775 The function return type.
776 """
777 if explicit_return_type is PydanticUndefined: 1stabcdefABzyuvghijklCDwxmnopqrEF
778 # try to get it from the type annotation
779 hints = get_function_type_hints( 1stabcdefABzyuvghijklCDwxmnopqrEF
780 unwrap_wrapped_function(func),
781 include_keys={'return'},
782 globalns=globalns,
783 localns=localns,
784 )
785 return hints.get('return', PydanticUndefined) 1stabcdefABzyuvghijklCDwxmnopqrEF
786 else:
787 return explicit_return_type 1stabcdefABzyuvghijklCDwxmnopqrEF
790def count_positional_required_params(sig: Signature) -> int: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
791 """Get the number of positional (required) arguments of a signature.
793 This function should only be used to inspect signatures of validation and serialization functions.
794 The first argument (the value being serialized or validated) is counted as a required argument
795 even if a default value exists.
797 Returns:
798 The number of positional arguments of a signature.
799 """
800 parameters = list(sig.parameters.values()) 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
801 return sum( 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
802 1
803 for param in parameters
804 if can_be_positional(param)
805 # First argument is the value being validated/serialized, and can have a default value
806 # (e.g. `float`, which has signature `(x=0, /)`). We assume other parameters (the info arg
807 # for instance) should be required, and thus without any default value.
808 and (param.default is Parameter.empty or param is parameters[0])
809 )
812def ensure_property(f: Any) -> Any: 1stabcdefABzyuvghijklCDMGHIJKLwxmnopqrEF
813 """Ensure that a function is a `property` or `cached_property`, or is a valid descriptor.
815 Args:
816 f: The function to check.
818 Returns:
819 The function, or a `property` or `cached_property` instance wrapping the function.
820 """
821 if ismethoddescriptor(f) or isdatadescriptor(f): 1stabcdefABzyuvghijklCDwxmnopqrEF
822 return f 1stabcdefABzyuvghijklCDwxmnopqrEF
823 else:
824 return property(f) 1stabcdefABzyuvghijklCDwxmnopqrEF