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