Coverage for pydantic/_internal/_fields.py: 98.68%
238 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-04 10:05 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-04 10:05 +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, get_origin 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
16from typing_inspection import typing_objects 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
17from typing_inspection.introspection import AnnotationSource 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
19from pydantic import PydanticDeprecatedSince211 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
20from pydantic.errors import PydanticUserError 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
22from ..aliases import AliasGenerator 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
23from . import _generics, _typing_extra 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
24from ._config import ConfigWrapper 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
25from ._docs_extraction import extract_docstrings_from_cls 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
26from ._import_utils import import_cached_base_model, import_cached_field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
27from ._namespace_utils import NsResolver 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
28from ._repr import Representation 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
29from ._utils import can_be_positional, get_first_not_none 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
31if TYPE_CHECKING: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
32 from annotated_types import BaseMetadata
34 from ..fields import FieldInfo
35 from ..main import BaseModel
36 from ._dataclasses import StandardDataclass
37 from ._decorators import DecoratorInfos
40class PydanticMetadata(Representation): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
41 """Base class for annotation markers like `Strict`."""
43 __slots__ = () 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
46def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
47 """Create a new `_PydanticGeneralMetadata` class with the given metadata.
49 Args:
50 **metadata: The metadata to add.
52 Returns:
53 The new `_PydanticGeneralMetadata` class.
54 """
55 return _general_metadata_cls()(metadata) # type: ignore 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
58@cache 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
59def _general_metadata_cls() -> type[BaseMetadata]: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
60 """Do it this way to avoid importing `annotated_types` at import time."""
61 from annotated_types import BaseMetadata 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
63 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
64 """Pydantic general metadata like `max_digits`."""
66 def __init__(self, metadata: Any): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
67 self.__dict__ = metadata 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
69 return _PydanticGeneralMetadata # type: ignore 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
72def _check_protected_namespaces( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
73 protected_namespaces: tuple[str | Pattern[str], ...],
74 ann_name: str,
75 bases: tuple[type[Any], ...],
76 cls_name: str,
77) -> None:
78 BaseModel = import_cached_base_model() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
80 for protected_namespace in protected_namespaces: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
81 ns_violation = False 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
82 if isinstance(protected_namespace, Pattern): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
83 ns_violation = protected_namespace.match(ann_name) is not None 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
84 elif isinstance(protected_namespace, str): 84 ↛ 87line 84 didn't jump to line 87 because the condition on line 84 was always true1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
85 ns_violation = ann_name.startswith(protected_namespace) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
87 if ns_violation: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
88 for b in bases: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
89 if hasattr(b, ann_name): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
90 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})): 90 ↛ 88line 90 didn't jump to line 88 because the condition on line 90 was always true1abcghdefij
91 raise ValueError( 1abcghdefij
92 f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}'
93 f' of protected namespace {protected_namespace!r}.'
94 )
95 else:
96 valid_namespaces: list[str] = [] 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
97 for pn in protected_namespaces: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
98 if isinstance(pn, Pattern): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
99 if not pn.match(ann_name): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
100 valid_namespaces.append(f're.compile({pn.pattern!r})') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
101 else:
102 if not ann_name.startswith(pn): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
103 valid_namespaces.append(f"'{pn}'") 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
105 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}' 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
107 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
108 f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n'
109 f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.",
110 UserWarning,
111 stacklevel=5,
112 )
115def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
116 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
117 for ann_name, field_info in fields.items(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
118 if field_info.description is None and ann_name in fields_docs: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
119 field_info.description = fields_docs[ann_name] 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
122def _apply_field_title_generator_to_field_info( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
123 title_generator: Callable[[str, FieldInfo], str],
124 field_name: str,
125 field_info: FieldInfo,
126):
127 if field_info.title is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
128 title = title_generator(field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
129 if not isinstance(title, str): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
130 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
132 field_info.title = title 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
135def _apply_alias_generator_to_field_info( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
136 alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo
137):
138 """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate.
140 Args:
141 alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
142 field_name: The name of the field from which to generate the alias.
143 field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied.
144 """
145 # Apply an alias_generator if
146 # 1. An alias is not specified
147 # 2. An alias is specified, but the priority is <= 1
148 if ( 1akIznodt
149 field_info.alias_priority is None
150 or field_info.alias_priority <= 1
151 or field_info.alias is None
152 or field_info.validation_alias is None
153 or field_info.serialization_alias is None
154 ):
155 alias, validation_alias, serialization_alias = None, None, None 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
157 if isinstance(alias_generator, AliasGenerator): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
158 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
159 elif callable(alias_generator): 159 ↛ 167line 159 didn't jump to line 167 because the condition on line 159 was always true1akblcmgwhxyIznopqrsABCDEdteufviFjGH
160 alias = alias_generator(field_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
161 if not isinstance(alias, str): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
162 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
164 # if priority is not set, we set to 1
165 # which supports the case where the alias_generator from a child class is used
166 # to generate an alias for a field in a parent class
167 if field_info.alias_priority is None or field_info.alias_priority <= 1: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
168 field_info.alias_priority = 1 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
170 # if the priority is 1, then we set the aliases to the generated alias
171 if field_info.alias_priority == 1: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
172 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
173 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
174 field_info.alias = alias 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
176 # if any of the aliases are not set, then we set them to the corresponding generated alias
177 if field_info.alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
178 field_info.alias = alias 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
179 if field_info.serialization_alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
180 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
181 if field_info.validation_alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
182 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
185def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
186 """Update the `FieldInfo` instance from the configuration set on the model it belongs to.
188 This will apply the title and alias generators from the configuration.
190 Args:
191 config_wrapper: The configuration from the model.
192 field_name: The field name the `FieldInfo` instance is attached to.
193 field_info: The `FieldInfo` instance to update.
194 """
195 field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
196 if field_title_generator is not None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
197 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
198 if config_wrapper.alias_generator is not None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
199 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
202def collect_model_fields( # noqa: C901 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
203 cls: type[BaseModel],
204 config_wrapper: ConfigWrapper,
205 ns_resolver: NsResolver | None,
206 *,
207 typevars_map: Mapping[TypeVar, Any] | None = None,
208) -> tuple[dict[str, FieldInfo], set[str]]:
209 """Collect the fields and class variables names of a nascent Pydantic model.
211 The fields collection process is *lenient*, meaning it won't error if string annotations
212 fail to evaluate. If this happens, the original annotation (and assigned value, if any)
213 is stored on the created `FieldInfo` instance.
215 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
216 and will make use of these stored attributes.
218 Args:
219 cls: BaseModel or dataclass.
220 config_wrapper: The config wrapper instance.
221 ns_resolver: Namespace resolver to use when getting model annotations.
222 typevars_map: A dictionary mapping type variables to their concrete types.
224 Returns:
225 A two-tuple containing model fields and class variables names.
227 Raises:
228 NameError:
229 - If there is a conflict between a field name and protected namespaces.
230 - If there is a field other than `root` in `RootModel`.
231 - If a field shadows an attribute in the parent model.
232 """
233 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
235 bases = cls.__bases__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
236 parent_fields_lookup: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
237 for base in reversed(bases): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
238 if model_fields := getattr(base, '__pydantic_fields__', None): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
239 parent_fields_lookup.update(model_fields) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
241 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
243 # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
244 # annotations is only used for finding fields in parent classes
245 annotations = cls.__dict__.get('__annotations__', {}) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
246 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
248 class_vars: set[str] = set() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
249 for ann_name, (ann_type, evaluated) in type_hints.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
250 if ann_name == 'model_config': 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
251 # We never want to treat `model_config` as a field
252 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
253 # protected namespaces (where `model_config` might be allowed as a field name)
254 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
256 _check_protected_namespaces( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
257 protected_namespaces=config_wrapper.protected_namespaces,
258 ann_name=ann_name,
259 bases=bases,
260 cls_name=cls.__name__,
261 )
263 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
264 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
265 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
267 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
269 if not is_valid_field_name(ann_name): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
270 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
271 if cls.__pydantic_root_model__ and ann_name != 'root': 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
272 raise NameError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
273 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`"
274 )
276 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
277 # "... shadows an attribute" warnings
278 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
279 for base in bases: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
280 dataclass_fields = { 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
281 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
282 }
283 if hasattr(base, ann_name): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
284 if base is generic_origin: 284 ↛ 286line 284 didn't jump to line 286 because the condition on line 284 was never true1akblcmgwhxyIznopqrsABCDEdteufviFjGH
285 # Don't warn when "shadowing" of attributes in parametrized generics
286 continue
288 if ann_name in dataclass_fields: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
289 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
290 # on the class instance.
291 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
293 if ann_name not in annotations: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
294 # Don't warn when a field exists in a parent class but has not been defined in the current class
295 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
297 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
298 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
299 f'"{base.__qualname__}"',
300 UserWarning,
301 )
303 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
304 if ann_name in annotations or ann_name not in parent_fields_lookup: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
305 # field is either:
306 # - present in the current model's annotations (and *not* from parent classes)
307 # - not found on any base classes; this seems to be caused by fields bot getting
308 # generated due to models not being fully defined while initializing recursive models.
309 # Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this.
310 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
311 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
312 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
313 # Store the original annotation that should be used to rebuild
314 # the field info later:
315 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
316 else:
317 # The field was present on one of the (possibly multiple) base classes
318 # copy the field to make sure typevar substitutions don't cause issues with the base classes
319 field_info = copy(parent_fields_lookup[ann_name]) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
321 else: # An assigned value is present (either the default value, or a `Field()` function)
322 _warn_on_nested_alias_in_annotation(ann_type, ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
323 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
324 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
325 # for descriptors, so we do the same if the `= field(default=...)` form is used.
326 # Note that we only do this for method descriptors for now, we might want to
327 # extend this to any descriptor in the future (by simply checking for
328 # `hasattr(assigned_value.default, '__get__')`).
329 assigned_value.default = assigned_value.default.__get__(None, cls) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
331 # The `from_annotated_attribute()` call below mutates the assigned `Field()`, so make a copy:
332 original_assignment = ( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
333 copy(assigned_value) if not evaluated and isinstance(assigned_value, FieldInfo_) else assigned_value
334 )
336 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
337 # Store the original annotation and assignment value that should be used to rebuild the field info later.
338 # Note that the assignment is always stored as the annotation might contain a type var that is later
339 # parameterized with an unknown forward reference (and we'll need it to rebuild the field info):
340 field_info._original_assignment = original_assignment 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
341 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
342 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
343 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
344 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
345 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
346 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
347 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
348 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
349 category=PydanticDeprecatedSince211,
350 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
351 stacklevel=4,
352 )
353 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
354 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
356 # attributes which are fields are removed from the class namespace:
357 # 1. To match the behaviour of annotation-only fields
358 # 2. To avoid false positives in the NameError check above
359 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
360 delattr(cls, ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
361 except AttributeError: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
362 pass # indicates the attribute was on a parent class 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
364 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
365 # to make sure the decorators have already been built for this exact class
366 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
367 if ann_name in decorators.computed_fields: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
368 raise TypeError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
369 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
370 'This override with a computed_field is incompatible.'
371 )
372 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
374 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
375 # If not complete, this will be called in `rebuild_model_fields()`:
376 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
378 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
379 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
380 if field._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
381 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
383 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
384 _update_fields_from_docstrings(cls, fields) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
385 return fields, class_vars 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
388def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
389 FieldInfo = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
391 args = getattr(ann_type, '__args__', None) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
392 if args: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
393 for anno_arg in args: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
394 if typing_objects.is_annotated(get_origin(anno_arg)): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
395 for anno_type_arg in _typing_extra.get_args(anno_arg): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
396 if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
397 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
398 f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.',
399 UserWarning,
400 )
401 return 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
404def rebuild_model_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
405 cls: type[BaseModel],
406 *,
407 config_wrapper: ConfigWrapper,
408 ns_resolver: NsResolver,
409 typevars_map: Mapping[TypeVar, Any],
410) -> dict[str, FieldInfo]:
411 """Rebuild the (already present) model fields by trying to reevaluate annotations.
413 This function should be called whenever a model with incomplete fields is encountered.
415 Raises:
416 NameError: If one of the annotations failed to evaluate.
418 Note:
419 This function *doesn't* mutate the model fields in place, as it can be called during
420 schema generation, where you don't want to mutate other model's fields.
421 """
422 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
424 rebuilt_fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
425 with ns_resolver.push(cls): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
426 for f_name, field_info in cls.__pydantic_fields__.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
427 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
428 rebuilt_fields[f_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
429 else:
430 existing_desc = field_info.description 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
431 ann = _typing_extra.eval_type( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
432 field_info._original_annotation,
433 *ns_resolver.types_namespace,
434 )
435 ann = _generics.replace_types(ann, typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
437 if (assign := field_info._original_assignment) is PydanticUndefined: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
438 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
439 else:
440 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
441 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
442 new_field.description = new_field.description if new_field.description is not None else existing_desc 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
443 update_field_from_config(config_wrapper, f_name, new_field) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
444 rebuilt_fields[f_name] = new_field 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
446 return rebuilt_fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
449def collect_dataclass_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
450 cls: type[StandardDataclass],
451 *,
452 config_wrapper: ConfigWrapper,
453 ns_resolver: NsResolver | None = None,
454 typevars_map: dict[Any, Any] | None = None,
455) -> dict[str, FieldInfo]:
456 """Collect the fields of a dataclass.
458 Args:
459 cls: dataclass.
460 config_wrapper: The config wrapper instance.
461 ns_resolver: Namespace resolver to use when getting dataclass annotations.
462 Defaults to an empty instance.
463 typevars_map: A dictionary mapping type variables to their concrete types.
465 Returns:
466 The dataclass fields.
467 """
468 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
470 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
471 ns_resolver = ns_resolver or NsResolver() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
472 dataclass_fields = cls.__dataclass_fields__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
474 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
475 # although we do it manually as stdlib dataclasses already have annotations
476 # collected in each class:
477 for base in reversed(cls.__mro__): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
478 if not dataclasses.is_dataclass(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
479 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
481 with ns_resolver.push(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
482 for ann_name, dataclass_field in dataclass_fields.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
483 if ann_name not in base.__dict__.get('__annotations__', {}): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
484 # `__dataclass_fields__`contains every field, even the ones from base classes.
485 # Only collect the ones defined on `base`.
486 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
488 globalns, localns = ns_resolver.types_namespace 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
489 ann_type, _ = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
491 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
492 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
494 if ( 1akblcmIznopqrsdteufv
495 not dataclass_field.init
496 and dataclass_field.default is dataclasses.MISSING
497 and dataclass_field.default_factory is dataclasses.MISSING
498 ):
499 # TODO: We should probably do something with this so that validate_assignment behaves properly
500 # Issue: https://github.com/pydantic/pydantic/issues/5470
501 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
503 if isinstance(dataclass_field.default, FieldInfo_): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
504 if dataclass_field.default.init_var: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
505 if dataclass_field.default.init is False: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
506 raise PydanticUserError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
507 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
508 code='clashing-init-and-init-var',
509 )
511 # TODO: same note as above re validate_assignment
512 continue 1blcmgwhxyzpqrsABCDEeufviFjGH
513 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
514 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
515 )
516 else:
517 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
518 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
519 )
521 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
522 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
524 if field_info.default is not PydanticUndefined and isinstance( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
525 getattr(cls, ann_name, field_info), FieldInfo_
526 ):
527 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
528 setattr(cls, ann_name, field_info.default) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
530 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
531 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
532 # We don't pass any ns, as `field.annotation`
533 # was already evaluated. TODO: is this method relevant?
534 # Can't we juste use `_generics.replace_types`?
535 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
537 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
538 _update_fields_from_docstrings( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
539 cls,
540 fields,
541 # We can't rely on the (more reliable) frame inspection method
542 # for stdlib dataclasses:
543 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
544 )
546 return fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
549def is_valid_field_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
550 return not name.startswith('_') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
553def is_valid_privateattr_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
554 return name.startswith('_') and not name.startswith('__') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
557def takes_validated_data_argument( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
558 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
559) -> TypeIs[Callable[[dict[str, Any]], Any]]:
560 """Whether the provided default factory callable has a validated data parameter."""
561 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
562 sig = signature(default_factory) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
563 except (ValueError, TypeError): 1akblcmgwhxynopqrsABCDEdteufviFjGH
564 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
565 # In this case, we assume no data argument is present:
566 return False 1akblcmgwhxynopqrsABCDEdteufviFjGH
568 parameters = list(sig.parameters.values()) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
570 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH