Coverage for pydantic/_internal/_fields.py: 98.15%
203 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"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
3from __future__ import annotations as _annotations 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
5import dataclasses 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
6import warnings 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
7from collections.abc import Mapping 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
8from copy import copy 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
9from functools import cache 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
10from inspect import Parameter, ismethoddescriptor, signature 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
11from re import Pattern 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
12from typing import TYPE_CHECKING, Any, Callable, TypeVar 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
14from pydantic_core import PydanticUndefined 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
15from typing_extensions import TypeIs 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
17from pydantic import PydanticDeprecatedSince211 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
18from pydantic.errors import PydanticUserError 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
20from . import _generics, _typing_extra 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
21from ._config import ConfigWrapper 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
22from ._docs_extraction import extract_docstrings_from_cls 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
23from ._import_utils import import_cached_base_model, import_cached_field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
24from ._namespace_utils import NsResolver 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
25from ._repr import Representation 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
26from ._utils import can_be_positional 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
28if TYPE_CHECKING: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
29 from annotated_types import BaseMetadata
31 from ..fields import FieldInfo
32 from ..main import BaseModel
33 from ._dataclasses import StandardDataclass
34 from ._decorators import DecoratorInfos
37class PydanticMetadata(Representation): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
38 """Base class for annotation markers like `Strict`."""
40 __slots__ = () 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
43def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
44 """Create a new `_PydanticGeneralMetadata` class with the given metadata.
46 Args:
47 **metadata: The metadata to add.
49 Returns:
50 The new `_PydanticGeneralMetadata` class.
51 """
52 return _general_metadata_cls()(metadata) # type: ignore 1esakblftguFEvwmnopxyzAhBcqdriCjD
55@cache 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
56def _general_metadata_cls() -> type[BaseMetadata]: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
57 """Do it this way to avoid importing `annotated_types` at import time."""
58 from annotated_types import BaseMetadata 1esakblftguFEvwmnopxyzAhBcqdriCjD
60 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1esakblftguFEvwmnopxyzAhBcqdriCjD
61 """Pydantic general metadata like `max_digits`."""
63 def __init__(self, metadata: Any): 1esakblftguFEvwmnopxyzAhBcqdriCjD
64 self.__dict__ = metadata 1esakblftguFEvwmnopxyzAhBcqdriCjD
66 return _PydanticGeneralMetadata # type: ignore 1esakblftguFEvwmnopxyzAhBcqdriCjD
69def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
70 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1esakblftguFEvwmnopxyzAhBcqdriCjD
71 for ann_name, field_info in fields.items(): 1esakblftguFEvwmnopxyzAhBcqdriCjD
72 if field_info.description is None and ann_name in fields_docs: 1esakblftguFEvwmnopxyzAhBcqdriCjD
73 field_info.description = fields_docs[ann_name] 1esakblftguFEvwmnopxyzAhBcqdriCjD
76def collect_model_fields( # noqa: C901 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
77 cls: type[BaseModel],
78 config_wrapper: ConfigWrapper,
79 ns_resolver: NsResolver | None,
80 *,
81 typevars_map: Mapping[TypeVar, Any] | None = None,
82) -> tuple[dict[str, FieldInfo], set[str]]:
83 """Collect the fields and class variables names of a nascent Pydantic model.
85 The fields collection process is *lenient*, meaning it won't error if string annotations
86 fail to evaluate. If this happens, the original annotation (and assigned value, if any)
87 is stored on the created `FieldInfo` instance.
89 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
90 and will make use of these stored attributes.
92 Args:
93 cls: BaseModel or dataclass.
94 config_wrapper: The config wrapper instance.
95 ns_resolver: Namespace resolver to use when getting model annotations.
96 typevars_map: A dictionary mapping type variables to their concrete types.
98 Returns:
99 A two-tuple containing model fields and class variables names.
101 Raises:
102 NameError:
103 - If there is a conflict between a field name and protected namespaces.
104 - If there is a field other than `root` in `RootModel`.
105 - If a field shadows an attribute in the parent model.
106 """
107 BaseModel = import_cached_base_model() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
108 FieldInfo_ = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
110 bases = cls.__bases__ 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
111 parent_fields_lookup: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
112 for base in reversed(bases): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
113 if model_fields := getattr(base, '__pydantic_fields__', None): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
114 parent_fields_lookup.update(model_fields) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
116 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
118 # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
119 # annotations is only used for finding fields in parent classes
120 annotations = cls.__dict__.get('__annotations__', {}) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
121 fields: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
123 class_vars: set[str] = set() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
124 for ann_name, (ann_type, evaluated) in type_hints.items(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
125 if ann_name == 'model_config': 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
126 # We never want to treat `model_config` as a field
127 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
128 # protected namespaces (where `model_config` might be allowed as a field name)
129 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
131 for protected_namespace in config_wrapper.protected_namespaces: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
132 ns_violation: bool = False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
133 if isinstance(protected_namespace, Pattern): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
134 ns_violation = protected_namespace.match(ann_name) is not None 1esakblftguFEvwmnopxyzAhBcqdriCjD
135 elif isinstance(protected_namespace, str): 135 ↛ 138line 135 didn't jump to line 138 because the condition on line 135 was always true1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
136 ns_violation = ann_name.startswith(protected_namespace) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
138 if ns_violation: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
139 for b in bases: 1esakblftguFEvwmnopxyzAhBcqdriCjD
140 if hasattr(b, ann_name): 1esakblftguFEvwmnopxyzAhBcqdriCjD
141 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})): 141 ↛ 139line 141 didn't jump to line 139 because the condition on line 141 was always true1eabfghcdij
142 raise NameError( 1eabfghcdij
143 f'Field "{ann_name}" conflicts with member {getattr(b, ann_name)}'
144 f' of protected namespace "{protected_namespace}".'
145 )
146 else:
147 valid_namespaces = () 1esakblftguFEvwmnopxyzAhBcqdriCjD
148 for pn in config_wrapper.protected_namespaces: 1esakblftguFEvwmnopxyzAhBcqdriCjD
149 if isinstance(pn, Pattern): 1esakblftguFEvwmnopxyzAhBcqdriCjD
150 if not pn.match(ann_name): 150 ↛ 151line 150 didn't jump to line 151 because the condition on line 150 was never true1esakblftguFEvwmnopxyzAhBcqdriCjD
151 valid_namespaces += (f're.compile({pn.pattern})',)
152 else:
153 if not ann_name.startswith(pn): 1esakblftguFEvwmnopxyzAhBcqdriCjD
154 valid_namespaces += (pn,) 1esakblftguFEvwmnopxyzAhBcqdriCjD
156 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD
157 f'Field "{ann_name}" in {cls.__name__} has conflict with protected namespace "{protected_namespace}".'
158 '\n\nYou may be able to resolve this warning by setting'
159 f" `model_config['protected_namespaces'] = {valid_namespaces}`.",
160 UserWarning,
161 )
162 if _typing_extra.is_classvar_annotation(ann_type): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
163 class_vars.add(ann_name) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
164 continue 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
166 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
168 if _is_finalvar_with_default_val(ann_type, assigned_value): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
169 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD
170 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
171 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
172 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
173 category=PydanticDeprecatedSince211,
174 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
175 stacklevel=4,
176 )
177 class_vars.add(ann_name) 1esakblftguFEvwmnopxyzAhBcqdriCjD
178 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
179 if not is_valid_field_name(ann_name): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
180 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
181 if cls.__pydantic_root_model__ and ann_name != 'root': 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
182 raise NameError( 1esakblftguFEvwmnopxyzAhBcqdriCjD
183 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`"
184 )
186 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
187 # "... shadows an attribute" warnings
188 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
189 for base in bases: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
190 dataclass_fields = { 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
191 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
192 }
193 if hasattr(base, ann_name): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
194 if base is generic_origin: 194 ↛ 196line 194 didn't jump to line 196 because the condition on line 194 was never true1esakblftguFEvwmnopxyzAhBcqdriCjD
195 # Don't warn when "shadowing" of attributes in parametrized generics
196 continue
198 if ann_name in dataclass_fields: 1esakblftguFEvwmnopxyzAhBcqdriCjD
199 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
200 # on the class instance.
201 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
203 if ann_name not in annotations: 1esakblftguFEvwmnopxyzAhBcqdriCjD
204 # Don't warn when a field exists in a parent class but has not been defined in the current class
205 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
207 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD
208 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
209 f'"{base.__qualname__}"',
210 UserWarning,
211 )
213 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
214 if ann_name in annotations: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
215 # field is present in the current model's annotations (and *not* from parent classes)
216 field_info = FieldInfo_.from_annotation(ann_type) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
217 elif ann_name in parent_fields_lookup: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
218 # The field was present on one of the (possibly multiple) base classes
219 # copy the field to make sure typevar substitutions don't cause issues with the base classes
220 field_info = copy(parent_fields_lookup[ann_name]) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
221 else:
222 # The field was not found on any base classes; this seems to be caused by fields not getting
223 # generated thanks to models not being fully defined while initializing recursive models.
224 # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this.
225 field_info = FieldInfo_.from_annotation(ann_type) 1esakblftguFEvwmnopxyzAhBcqdriCjD
227 if not evaluated: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
228 field_info._complete = False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
229 # Store the original annotation that should be used to rebuild
230 # the field info later:
231 field_info._original_annotation = ann_type 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
232 else: # An assigned value is present (either the default value, or a `Field()` function)
233 _warn_on_nested_alias_in_annotation(ann_type, ann_name) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
234 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
235 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
236 # for descriptors, so we do the same if the `= field(default=...)` form is used.
237 # Note that we only do this for method descriptors for now, we might want to
238 # extend this to any descriptor in the future (by simply checking for
239 # `hasattr(assigned_value.default, '__get__')`).
240 assigned_value.default = assigned_value.default.__get__(None, cls) 1esakblftguFEvwmnopxyzAhBcqdriCjD
242 # The `from_annotated_attribute()` call below mutates the assigned `Field()`, so make a copy:
243 original_assignment = ( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
244 copy(assigned_value) if not evaluated and isinstance(assigned_value, FieldInfo_) else assigned_value
245 )
247 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
248 if not evaluated: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
249 field_info._complete = False 1esakblftguFEvwmnopxyzAhBcqdriCjD
250 # Store the original annotation and assignment value that should be used to rebuild
251 # the field info later:
252 field_info._original_annotation = ann_type 1esakblftguFEvwmnopxyzAhBcqdriCjD
253 field_info._original_assignment = original_assignment 1esakblftguFEvwmnopxyzAhBcqdriCjD
255 # attributes which are fields are removed from the class namespace:
256 # 1. To match the behaviour of annotation-only fields
257 # 2. To avoid false positives in the NameError check above
258 try: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
259 delattr(cls, ann_name) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
260 except AttributeError: 1esakblftguFEvwmnopxyzAhBcqdriCjD
261 pass # indicates the attribute was on a parent class 1esakblftguFEvwmnopxyzAhBcqdriCjD
263 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
264 # to make sure the decorators have already been built for this exact class
265 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
266 if ann_name in decorators.computed_fields: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
267 raise ValueError("you can't override a field with a computed field") 1esakblftguFEvwmnopxyzAhBcqdriCjD
268 fields[ann_name] = field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
270 if typevars_map: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
271 for field in fields.values(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
272 if field._complete: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
273 field.apply_typevars_map(typevars_map) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
275 if config_wrapper.use_attribute_docstrings: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
276 _update_fields_from_docstrings(cls, fields) 1esakblftguFEvwmnopxyzAhBcqdriCjD
277 return fields, class_vars 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
280def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> None: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
281 FieldInfo = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
283 args = getattr(ann_type, '__args__', None) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
284 if args: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
285 for anno_arg in args: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
286 if _typing_extra.is_annotated(anno_arg): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
287 for anno_type_arg in _typing_extra.get_args(anno_arg): 1esakblftguFEvwmnopxyzAhBcqdriCjD
288 if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: 1esakblftguFEvwmnopxyzAhBcqdriCjD
289 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD
290 f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.',
291 UserWarning,
292 )
293 return 1esakblftguFEvwmnopxyzAhBcqdriCjD
296def _is_finalvar_with_default_val(ann_type: type[Any], assigned_value: Any) -> bool: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
297 if assigned_value is PydanticUndefined: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
298 return False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
300 FieldInfo = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
302 if isinstance(assigned_value, FieldInfo) and assigned_value.is_required(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
303 return False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
304 elif not _typing_extra.is_finalvar(ann_type): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
305 return False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
306 else:
307 return True 1esakblftguFEvwmnopxyzAhBcqdriCjD
310def rebuild_model_fields( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
311 cls: type[BaseModel],
312 *,
313 ns_resolver: NsResolver,
314 typevars_map: Mapping[TypeVar, Any],
315) -> dict[str, FieldInfo]:
316 """Rebuild the (already present) model fields by trying to reevaluate annotations.
318 This function should be called whenever a model with incomplete fields is encountered.
320 Note:
321 This function *doesn't* mutate the model fields in place, as it can be called during
322 schema generation, where you don't want to mutate other model's fields.
323 """
324 FieldInfo_ = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
326 rebuilt_fields: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
327 with ns_resolver.push(cls): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
328 for f_name, field_info in cls.__pydantic_fields__.items(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
329 if field_info._complete: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
330 rebuilt_fields[f_name] = field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
331 else:
332 ann = _typing_extra.eval_type( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
333 field_info._original_annotation,
334 *ns_resolver.types_namespace,
335 )
336 ann = _generics.replace_types(ann, typevars_map) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
338 if (assign := field_info._original_assignment) is PydanticUndefined: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
339 rebuilt_fields[f_name] = FieldInfo_.from_annotation(ann) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
340 else:
341 rebuilt_fields[f_name] = FieldInfo_.from_annotated_attribute(ann, assign) 1esakblftguFEvwmnopxyzAGhBcqdriCjD
343 return rebuilt_fields 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
346def collect_dataclass_fields( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
347 cls: type[StandardDataclass],
348 *,
349 ns_resolver: NsResolver | None = None,
350 typevars_map: dict[Any, Any] | None = None,
351 config_wrapper: ConfigWrapper | None = None,
352) -> dict[str, FieldInfo]:
353 """Collect the fields of a dataclass.
355 Args:
356 cls: dataclass.
357 ns_resolver: Namespace resolver to use when getting dataclass annotations.
358 Defaults to an empty instance.
359 typevars_map: A dictionary mapping type variables to their concrete types.
360 config_wrapper: The config wrapper instance.
362 Returns:
363 The dataclass fields.
364 """
365 FieldInfo_ = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
367 fields: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
368 ns_resolver = ns_resolver or NsResolver() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
369 dataclass_fields = cls.__dataclass_fields__ 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
371 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
372 # although we do it manually as stdlib dataclasses already have annotations
373 # collected in each class:
374 for base in reversed(cls.__mro__): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
375 if not dataclasses.is_dataclass(base): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
376 continue 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
378 with ns_resolver.push(base): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
379 for ann_name, dataclass_field in dataclass_fields.items(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
380 if ann_name not in base.__dict__.get('__annotations__', {}): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
381 # `__dataclass_fields__`contains every field, even the ones from base classes.
382 # Only collect the ones defined on `base`.
383 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
385 globalns, localns = ns_resolver.types_namespace 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
386 ann_type, _ = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
388 if _typing_extra.is_classvar_annotation(ann_type): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
389 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
391 if ( 1esakblFEvwmnopGHIhBcqdr
392 not dataclass_field.init
393 and dataclass_field.default is dataclasses.MISSING
394 and dataclass_field.default_factory is dataclasses.MISSING
395 ):
396 # TODO: We should probably do something with this so that validate_assignment behaves properly
397 # Issue: https://github.com/pydantic/pydantic/issues/5470
398 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD
400 if isinstance(dataclass_field.default, FieldInfo_): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
401 if dataclass_field.default.init_var: 1esakblftguFEvwmnopxyzAhBcqdriCjD
402 if dataclass_field.default.init is False: 1esakblftguFEvwmnopxyzAhBcqdriCjD
403 raise PydanticUserError( 1esakblftguFEvwmnopxyzAhBcqdriCjD
404 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
405 code='clashing-init-and-init-var',
406 )
408 # TODO: same note as above re validate_assignment
409 continue 1akblftguEmnopxyzAcqdriCjD
410 field_info = FieldInfo_.from_annotated_attribute(ann_type, dataclass_field.default) 1esakblftguFEvwmnopxyzAhBcqdriCjD
411 else:
412 field_info = FieldInfo_.from_annotated_attribute(ann_type, dataclass_field) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
414 fields[ann_name] = field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
416 if field_info.default is not PydanticUndefined and isinstance( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
417 getattr(cls, ann_name, field_info), FieldInfo_
418 ):
419 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
420 setattr(cls, ann_name, field_info.default) 1esakblftguFEvwmnopxyzAhBcqdriCjD
422 if typevars_map: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
423 for field in fields.values(): 1esakblftguFEvwmnopxyzAhBcqdriCjD
424 # We don't pass any ns, as `field.annotation`
425 # was already evaluated. TODO: is this method relevant?
426 # Can't we juste use `_generics.replace_types`?
427 field.apply_typevars_map(typevars_map) 1esakblftguFEvwmnopxyzAhBcqdriCjD
429 if config_wrapper is not None and config_wrapper.use_attribute_docstrings: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
430 _update_fields_from_docstrings( 1esakblftguFEvwmnopxyzAhBcqdriCjD
431 cls,
432 fields,
433 # We can't rely on the (more reliable) frame inspection method
434 # for stdlib dataclasses:
435 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
436 )
438 return fields 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
441def is_valid_field_name(name: str) -> bool: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
442 return not name.startswith('_') 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
445def is_valid_privateattr_name(name: str) -> bool: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
446 return name.startswith('_') and not name.startswith('__') 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
449def takes_validated_data_argument( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
450 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
451) -> TypeIs[Callable[[dict[str, Any]], Any]]:
452 """Whether the provided default factory callable has a validated data parameter."""
453 try: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
454 sig = signature(default_factory) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
455 except (ValueError, TypeError): 1esakblftguvwmnopxyzAhBcqdriCjD
456 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
457 # In this case, we assume no data argument is present:
458 return False 1esakblftguvwmnopxyzAhBcqdriCjD
460 parameters = list(sig.parameters.values()) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD
462 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD