Coverage for pydantic/_internal/_fields.py: 98.72%
252 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-05 10:27 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-05 10:27 +0000
1"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
3from __future__ import annotations as _annotations 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
5import dataclasses 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
6import warnings 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
7from collections.abc import Mapping 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
8from copy import copy 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
9from functools import cache 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
10from inspect import Parameter, ismethoddescriptor, signature 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
11from re import Pattern 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
12from typing import TYPE_CHECKING, Any, Callable, TypeVar 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
14from pydantic_core import PydanticUndefined 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
15from typing_extensions import TypeIs 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
16from typing_inspection.introspection import AnnotationSource 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
18from pydantic import PydanticDeprecatedSince211 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
19from pydantic.errors import PydanticUserError 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
21from ..aliases import AliasGenerator 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
22from . import _generics, _typing_extra 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
23from ._config import ConfigWrapper 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
24from ._docs_extraction import extract_docstrings_from_cls 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
25from ._import_utils import import_cached_base_model, import_cached_field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
26from ._namespace_utils import NsResolver 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
27from ._repr import Representation 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
28from ._utils import can_be_positional, get_first_not_none 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
30if TYPE_CHECKING: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
31 from annotated_types import BaseMetadata
33 from ..fields import FieldInfo
34 from ..main import BaseModel
35 from ._dataclasses import PydanticDataclass, StandardDataclass
36 from ._decorators import DecoratorInfos
39class PydanticMetadata(Representation): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
40 """Base class for annotation markers like `Strict`."""
42 __slots__ = () 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
45def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
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 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
57@cache 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
58def _general_metadata_cls() -> type[BaseMetadata]: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
59 """Do it this way to avoid importing `annotated_types` at import time."""
60 from annotated_types import BaseMetadata 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
62 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
63 """Pydantic general metadata like `max_digits`."""
65 def __init__(self, metadata: Any): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
66 self.__dict__ = metadata 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
68 return _PydanticGeneralMetadata # type: ignore 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
71def _check_protected_namespaces( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
72 protected_namespaces: tuple[str | Pattern[str], ...],
73 ann_name: str,
74 bases: tuple[type[Any], ...],
75 cls_name: str,
76) -> None:
77 BaseModel = import_cached_base_model() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
79 for protected_namespace in protected_namespaces: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
80 ns_violation = False 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
81 if isinstance(protected_namespace, Pattern): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
82 ns_violation = protected_namespace.match(ann_name) is not None 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
83 elif isinstance(protected_namespace, str): 83 ↛ 86line 83 didn't jump to line 86 because the condition on line 83 was always true1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
84 ns_violation = ann_name.startswith(protected_namespace) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
86 if ns_violation: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
87 for b in bases: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
88 if hasattr(b, ann_name): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
89 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})): 89 ↛ 87line 89 didn't jump to line 87 because the condition on line 89 was always true1abcghdefij
90 raise ValueError( 1abcghdefij
91 f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}'
92 f' of protected namespace {protected_namespace!r}.'
93 )
94 else:
95 valid_namespaces: list[str] = [] 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
96 for pn in protected_namespaces: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
97 if isinstance(pn, Pattern): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
98 if not pn.match(ann_name): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
99 valid_namespaces.append(f're.compile({pn.pattern!r})') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
100 else:
101 if not ann_name.startswith(pn): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
102 valid_namespaces.append(f"'{pn}'") 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
104 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}' 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
106 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
107 f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n'
108 f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.",
109 UserWarning,
110 stacklevel=5,
111 )
114def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
115 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
116 for ann_name, field_info in fields.items(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
117 if field_info.description is None and ann_name in fields_docs: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
118 field_info.description = fields_docs[ann_name] 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
121def _apply_field_title_generator_to_field_info( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
122 title_generator: Callable[[str, FieldInfo], str],
123 field_name: str,
124 field_info: FieldInfo,
125):
126 if field_info.title is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
127 title = title_generator(field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
128 if not isinstance(title, str): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
129 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
131 field_info.title = title 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
134def _apply_alias_generator_to_field_info( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
135 alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo
136):
137 """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate.
139 Args:
140 alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
141 field_name: The name of the field from which to generate the alias.
142 field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied.
143 """
144 # Apply an alias_generator if
145 # 1. An alias is not specified
146 # 2. An alias is specified, but the priority is <= 1
147 if ( 1akIznodt
148 field_info.alias_priority is None
149 or field_info.alias_priority <= 1
150 or field_info.alias is None
151 or field_info.validation_alias is None
152 or field_info.serialization_alias is None
153 ):
154 alias, validation_alias, serialization_alias = None, None, None 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
156 if isinstance(alias_generator, AliasGenerator): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
157 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
158 elif callable(alias_generator): 158 ↛ 166line 158 didn't jump to line 166 because the condition on line 158 was always true1akblcmgwhxyIznopqrsABCDEdteufviFjGH
159 alias = alias_generator(field_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
160 if not isinstance(alias, str): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
161 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
163 # if priority is not set, we set to 1
164 # which supports the case where the alias_generator from a child class is used
165 # to generate an alias for a field in a parent class
166 if field_info.alias_priority is None or field_info.alias_priority <= 1: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
167 field_info.alias_priority = 1 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
169 # if the priority is 1, then we set the aliases to the generated alias
170 if field_info.alias_priority == 1: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
171 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
172 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
173 field_info.alias = alias 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
175 # if any of the aliases are not set, then we set them to the corresponding generated alias
176 if field_info.alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
177 field_info.alias = alias 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
178 if field_info.serialization_alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
179 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
180 if field_info.validation_alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
181 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
184def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
185 """Update the `FieldInfo` instance from the configuration set on the model it belongs to.
187 This will apply the title and alias generators from the configuration.
189 Args:
190 config_wrapper: The configuration from the model.
191 field_name: The field name the `FieldInfo` instance is attached to.
192 field_info: The `FieldInfo` instance to update.
193 """
194 field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
195 if field_title_generator is not None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
196 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
197 if config_wrapper.alias_generator is not None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
198 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
201_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
203_deprecated_classmethod_names = { 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
204 'parse_obj',
205 'parse_raw',
206 'parse_file',
207 'from_orm',
208 'construct',
209 'schema',
210 'schema_json',
211 'validate',
212 'update_forward_refs',
213 '_get_value',
214}
217def collect_model_fields( # noqa: C901 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
218 cls: type[BaseModel],
219 config_wrapper: ConfigWrapper,
220 ns_resolver: NsResolver | None,
221 *,
222 typevars_map: Mapping[TypeVar, Any] | None = None,
223) -> tuple[dict[str, FieldInfo], set[str]]:
224 """Collect the fields and class variables names of a nascent Pydantic model.
226 The fields collection process is *lenient*, meaning it won't error if string annotations
227 fail to evaluate. If this happens, the original annotation (and assigned value, if any)
228 is stored on the created `FieldInfo` instance.
230 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
231 and will make use of these stored attributes.
233 Args:
234 cls: BaseModel or dataclass.
235 config_wrapper: The config wrapper instance.
236 ns_resolver: Namespace resolver to use when getting model annotations.
237 typevars_map: A dictionary mapping type variables to their concrete types.
239 Returns:
240 A two-tuple containing model fields and class variables names.
242 Raises:
243 NameError:
244 - If there is a conflict between a field name and protected namespaces.
245 - If there is a field other than `root` in `RootModel`.
246 - If a field shadows an attribute in the parent model.
247 """
248 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
249 BaseModel_ = import_cached_base_model() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
251 bases = cls.__bases__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
252 parent_fields_lookup: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
253 for base in reversed(bases): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
254 if model_fields := getattr(base, '__pydantic_fields__', None): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
255 parent_fields_lookup.update(model_fields) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
257 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
259 # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
260 # annotations is only used for finding fields in parent classes
261 annotations = cls.__dict__.get('__annotations__', {}) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
262 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
264 class_vars: set[str] = set() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
265 for ann_name, (ann_type, evaluated) in type_hints.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
266 if ann_name == 'model_config': 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
267 # We never want to treat `model_config` as a field
268 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
269 # protected namespaces (where `model_config` might be allowed as a field name)
270 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
272 _check_protected_namespaces( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
273 protected_namespaces=config_wrapper.protected_namespaces,
274 ann_name=ann_name,
275 bases=bases,
276 cls_name=cls.__name__,
277 )
279 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
280 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
281 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
283 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
284 if assigned_value is not PydanticUndefined and ( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
285 # One of the deprecated instance methods was used as a field name (e.g. `dict()`):
286 any(getattr(BaseModel_, depr_name, None) is assigned_value for depr_name in _deprecated_method_names)
287 # One of the deprecated class methods was used as a field name (e.g. `schema()`):
288 or (
289 hasattr(assigned_value, '__func__')
290 and any(
291 getattr(getattr(BaseModel_, depr_name, None), '__func__', None) is assigned_value.__func__ # pyright: ignore[reportAttributeAccessIssue]
292 for depr_name in _deprecated_classmethod_names
293 )
294 )
295 ):
296 # Then `assigned_value` would be the method, even though no default was specified:
297 assigned_value = PydanticUndefined 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
299 if not is_valid_field_name(ann_name): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
300 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
301 if cls.__pydantic_root_model__ and ann_name != 'root': 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
302 raise NameError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
303 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`"
304 )
306 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
307 # "... shadows an attribute" warnings
308 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
309 for base in bases: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
310 dataclass_fields = { 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
311 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
312 }
313 if hasattr(base, ann_name): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
314 if base is generic_origin: 314 ↛ 316line 314 didn't jump to line 316 because the condition on line 314 was never true1akblcmgwhxyIznopqrsABCDEdteufviFjGH
315 # Don't warn when "shadowing" of attributes in parametrized generics
316 continue
318 if ann_name in dataclass_fields: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
319 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
320 # on the class instance.
321 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
323 if ann_name not in annotations: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
324 # Don't warn when a field exists in a parent class but has not been defined in the current class
325 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
327 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
328 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
329 f'"{base.__qualname__}"',
330 UserWarning,
331 )
333 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
334 if ann_name in annotations or ann_name not in parent_fields_lookup: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
335 # field is either:
336 # - present in the current model's annotations (and *not* from parent classes)
337 # - not found on any base classes; this seems to be caused by fields bot getting
338 # generated due to models not being fully defined while initializing recursive models.
339 # Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this.
340 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
341 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
342 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
343 # Store the original annotation that should be used to rebuild
344 # the field info later:
345 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
346 else:
347 # The field was present on one of the (possibly multiple) base classes
348 # copy the field to make sure typevar substitutions don't cause issues with the base classes
349 field_info = copy(parent_fields_lookup[ann_name]) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
351 else: # An assigned value is present (either the default value, or a `Field()` function)
352 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
353 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
354 # for descriptors, so we do the same if the `= field(default=...)` form is used.
355 # Note that we only do this for method descriptors for now, we might want to
356 # extend this to any descriptor in the future (by simply checking for
357 # `hasattr(assigned_value.default, '__get__')`).
358 default = assigned_value.default.__get__(None, cls) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
359 assigned_value.default = default 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
360 assigned_value._attributes_set['default'] = default 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
362 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
363 # Store the original annotation and assignment value that should be used to rebuild the field info later.
364 # Note that the assignment is always stored as the annotation might contain a type var that is later
365 # parameterized with an unknown forward reference (and we'll need it to rebuild the field info):
366 field_info._original_assignment = assigned_value 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
367 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
368 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
369 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
370 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
371 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
372 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
373 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
374 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
375 category=PydanticDeprecatedSince211,
376 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
377 stacklevel=4,
378 )
379 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
380 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
382 # attributes which are fields are removed from the class namespace:
383 # 1. To match the behaviour of annotation-only fields
384 # 2. To avoid false positives in the NameError check above
385 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
386 delattr(cls, ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
387 except AttributeError: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
388 pass # indicates the attribute was on a parent class 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
390 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
391 # to make sure the decorators have already been built for this exact class
392 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
393 if ann_name in decorators.computed_fields: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
394 raise TypeError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
395 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
396 'This override with a computed_field is incompatible.'
397 )
398 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
400 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
401 # If not complete, this will be called in `rebuild_model_fields()`:
402 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
404 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
405 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
406 if field._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
407 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
409 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
410 _update_fields_from_docstrings(cls, fields) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
411 return fields, class_vars 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
414def rebuild_model_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
415 cls: type[BaseModel],
416 *,
417 config_wrapper: ConfigWrapper,
418 ns_resolver: NsResolver,
419 typevars_map: Mapping[TypeVar, Any],
420) -> dict[str, FieldInfo]:
421 """Rebuild the (already present) model fields by trying to reevaluate annotations.
423 This function should be called whenever a model with incomplete fields is encountered.
425 Raises:
426 NameError: If one of the annotations failed to evaluate.
428 Note:
429 This function *doesn't* mutate the model fields in place, as it can be called during
430 schema generation, where you don't want to mutate other model's fields.
431 """
432 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
434 rebuilt_fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
435 with ns_resolver.push(cls): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
436 for f_name, field_info in cls.__pydantic_fields__.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
437 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
438 rebuilt_fields[f_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
439 else:
440 existing_desc = field_info.description 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
441 ann = _typing_extra.eval_type( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
442 field_info._original_annotation,
443 *ns_resolver.types_namespace,
444 )
445 ann = _generics.replace_types(ann, typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
447 if (assign := field_info._original_assignment) is PydanticUndefined: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
448 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
449 else:
450 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
451 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
452 new_field.description = new_field.description if new_field.description is not None else existing_desc 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
453 update_field_from_config(config_wrapper, f_name, new_field) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
454 rebuilt_fields[f_name] = new_field 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
456 return rebuilt_fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
459def collect_dataclass_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
460 cls: type[StandardDataclass],
461 *,
462 config_wrapper: ConfigWrapper,
463 ns_resolver: NsResolver | None = None,
464 typevars_map: dict[Any, Any] | None = None,
465) -> dict[str, FieldInfo]:
466 """Collect the fields of a dataclass.
468 Args:
469 cls: dataclass.
470 config_wrapper: The config wrapper instance.
471 ns_resolver: Namespace resolver to use when getting dataclass annotations.
472 Defaults to an empty instance.
473 typevars_map: A dictionary mapping type variables to their concrete types.
475 Returns:
476 The dataclass fields.
477 """
478 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
480 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
481 ns_resolver = ns_resolver or NsResolver() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
482 dataclass_fields = cls.__dataclass_fields__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
484 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
485 # although we do it manually as stdlib dataclasses already have annotations
486 # collected in each class:
487 for base in reversed(cls.__mro__): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
488 if not dataclasses.is_dataclass(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
489 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
491 with ns_resolver.push(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
492 for ann_name, dataclass_field in dataclass_fields.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
493 if ann_name not in base.__dict__.get('__annotations__', {}): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
494 # `__dataclass_fields__`contains every field, even the ones from base classes.
495 # Only collect the ones defined on `base`.
496 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
498 globalns, localns = ns_resolver.types_namespace 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
499 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
501 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
502 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
504 if ( 1akblcmIznopqrsdteufv
505 not dataclass_field.init
506 and dataclass_field.default is dataclasses.MISSING
507 and dataclass_field.default_factory is dataclasses.MISSING
508 ):
509 # TODO: We should probably do something with this so that validate_assignment behaves properly
510 # Issue: https://github.com/pydantic/pydantic/issues/5470
511 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
513 if isinstance(dataclass_field.default, FieldInfo_): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
514 if dataclass_field.default.init_var: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
515 if dataclass_field.default.init is False: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
516 raise PydanticUserError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
517 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
518 code='clashing-init-and-init-var',
519 )
521 # TODO: same note as above re validate_assignment
522 continue 1blcmgwhxyzpqrsABCDEeufviFjGH
523 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
524 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
525 )
526 field_info._original_assignment = dataclass_field.default 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
527 else:
528 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
529 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
530 )
531 field_info._original_assignment = dataclass_field 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
533 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
534 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
535 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
537 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
538 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
540 if field_info.default is not PydanticUndefined and isinstance( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
541 getattr(cls, ann_name, field_info), FieldInfo_
542 ):
543 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
544 setattr(cls, ann_name, field_info.default) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
546 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
547 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
548 # We don't pass any ns, as `field.annotation`
549 # was already evaluated. TODO: is this method relevant?
550 # Can't we juste use `_generics.replace_types`?
551 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
553 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
554 _update_fields_from_docstrings( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
555 cls,
556 fields,
557 # We can't rely on the (more reliable) frame inspection method
558 # for stdlib dataclasses:
559 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
560 )
562 return fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
565def rebuild_dataclass_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
566 cls: type[PydanticDataclass],
567 *,
568 config_wrapper: ConfigWrapper,
569 ns_resolver: NsResolver,
570 typevars_map: Mapping[TypeVar, Any],
571) -> dict[str, FieldInfo]:
572 """Rebuild the (already present) dataclass fields by trying to reevaluate annotations.
574 This function should be called whenever a dataclass with incomplete fields is encountered.
576 Raises:
577 NameError: If one of the annotations failed to evaluate.
579 Note:
580 This function *doesn't* mutate the dataclass fields in place, as it can be called during
581 schema generation, where you don't want to mutate other dataclass's fields.
582 """
583 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
585 rebuilt_fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
586 with ns_resolver.push(cls): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
587 for f_name, field_info in cls.__pydantic_fields__.items(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
588 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
589 rebuilt_fields[f_name] = field_info 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
590 else:
591 existing_desc = field_info.description 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
592 ann = _typing_extra.eval_type( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
593 field_info._original_annotation,
594 *ns_resolver.types_namespace,
595 )
596 ann = _generics.replace_types(ann, typevars_map) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
597 new_field = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
598 ann,
599 field_info._original_assignment,
600 _source=AnnotationSource.DATACLASS,
601 )
603 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
604 new_field.description = new_field.description if new_field.description is not None else existing_desc 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
605 update_field_from_config(config_wrapper, f_name, new_field) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
606 rebuilt_fields[f_name] = new_field 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
608 return rebuilt_fields 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
611def is_valid_field_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
612 return not name.startswith('_') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
615def is_valid_privateattr_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
616 return name.startswith('_') and not name.startswith('__') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
619def takes_validated_data_argument( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
620 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
621) -> TypeIs[Callable[[dict[str, Any]], Any]]:
622 """Whether the provided default factory callable has a validated data parameter."""
623 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
624 sig = signature(default_factory) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
625 except (ValueError, TypeError): 1akblcmgwhxynopqrsABCDEdteufviFjGH
626 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
627 # In this case, we assume no data argument is present:
628 return False 1akblcmgwhxynopqrsABCDEdteufviFjGH
630 parameters = list(sig.parameters.values()) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
632 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH