Coverage for pydantic/_internal/_fields.py: 98.68%
238 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-02 16:20 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-02 16:20 +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 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
338 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
339 # Store the original annotation and assignment value that should be used to rebuild
340 # the field info later:
341 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
342 field_info._original_assignment = original_assignment 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
343 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
344 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
345 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
346 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
347 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
348 category=PydanticDeprecatedSince211,
349 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
350 stacklevel=4,
351 )
352 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
353 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
355 # attributes which are fields are removed from the class namespace:
356 # 1. To match the behaviour of annotation-only fields
357 # 2. To avoid false positives in the NameError check above
358 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
359 delattr(cls, ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
360 except AttributeError: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
361 pass # indicates the attribute was on a parent class 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
363 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
364 # to make sure the decorators have already been built for this exact class
365 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
366 if ann_name in decorators.computed_fields: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
367 raise TypeError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
368 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
369 'This override with a computed_field is incompatible.'
370 )
371 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
373 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
374 # If not complete, this will be called in `rebuild_model_fields()`:
375 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
377 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
378 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
379 if field._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
380 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
382 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
383 _update_fields_from_docstrings(cls, fields) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
384 return fields, class_vars 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
387def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
388 FieldInfo = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
390 args = getattr(ann_type, '__args__', None) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
391 if args: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
392 for anno_arg in args: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
393 if typing_objects.is_annotated(get_origin(anno_arg)): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
394 for anno_type_arg in _typing_extra.get_args(anno_arg): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
395 if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
396 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
397 f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.',
398 UserWarning,
399 )
400 return 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
403def rebuild_model_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
404 cls: type[BaseModel],
405 *,
406 config_wrapper: ConfigWrapper,
407 ns_resolver: NsResolver,
408 typevars_map: Mapping[TypeVar, Any],
409) -> dict[str, FieldInfo]:
410 """Rebuild the (already present) model fields by trying to reevaluate annotations.
412 This function should be called whenever a model with incomplete fields is encountered.
414 Note:
415 This function *doesn't* mutate the model fields in place, as it can be called during
416 schema generation, where you don't want to mutate other model's fields.
417 """
418 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
420 rebuilt_fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
421 with ns_resolver.push(cls): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
422 for f_name, field_info in cls.__pydantic_fields__.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
423 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
424 rebuilt_fields[f_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
425 else:
426 existing_desc = field_info.description 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
427 ann = _typing_extra.eval_type( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
428 field_info._original_annotation,
429 *ns_resolver.types_namespace,
430 )
431 ann = _generics.replace_types(ann, typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
433 if (assign := field_info._original_assignment) is PydanticUndefined: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
434 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
435 else:
436 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
437 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
438 new_field.description = new_field.description if new_field.description is not None else existing_desc 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
439 update_field_from_config(config_wrapper, f_name, new_field) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
440 rebuilt_fields[f_name] = new_field 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
442 return rebuilt_fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
445def collect_dataclass_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
446 cls: type[StandardDataclass],
447 *,
448 config_wrapper: ConfigWrapper,
449 ns_resolver: NsResolver | None = None,
450 typevars_map: dict[Any, Any] | None = None,
451) -> dict[str, FieldInfo]:
452 """Collect the fields of a dataclass.
454 Args:
455 cls: dataclass.
456 config_wrapper: The config wrapper instance.
457 ns_resolver: Namespace resolver to use when getting dataclass annotations.
458 Defaults to an empty instance.
459 typevars_map: A dictionary mapping type variables to their concrete types.
461 Returns:
462 The dataclass fields.
463 """
464 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
466 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
467 ns_resolver = ns_resolver or NsResolver() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
468 dataclass_fields = cls.__dataclass_fields__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
470 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
471 # although we do it manually as stdlib dataclasses already have annotations
472 # collected in each class:
473 for base in reversed(cls.__mro__): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
474 if not dataclasses.is_dataclass(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
475 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
477 with ns_resolver.push(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
478 for ann_name, dataclass_field in dataclass_fields.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
479 if ann_name not in base.__dict__.get('__annotations__', {}): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
480 # `__dataclass_fields__`contains every field, even the ones from base classes.
481 # Only collect the ones defined on `base`.
482 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
484 globalns, localns = ns_resolver.types_namespace 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
485 ann_type, _ = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
487 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
488 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
490 if ( 1akblcmIznopqrsdteufv
491 not dataclass_field.init
492 and dataclass_field.default is dataclasses.MISSING
493 and dataclass_field.default_factory is dataclasses.MISSING
494 ):
495 # TODO: We should probably do something with this so that validate_assignment behaves properly
496 # Issue: https://github.com/pydantic/pydantic/issues/5470
497 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
499 if isinstance(dataclass_field.default, FieldInfo_): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
500 if dataclass_field.default.init_var: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
501 if dataclass_field.default.init is False: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
502 raise PydanticUserError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
503 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
504 code='clashing-init-and-init-var',
505 )
507 # TODO: same note as above re validate_assignment
508 continue 1blcmgwhxyzpqrsABCDEeufviFjGH
509 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
510 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
511 )
512 else:
513 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
514 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
515 )
517 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
518 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
520 if field_info.default is not PydanticUndefined and isinstance( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
521 getattr(cls, ann_name, field_info), FieldInfo_
522 ):
523 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
524 setattr(cls, ann_name, field_info.default) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
526 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
527 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
528 # We don't pass any ns, as `field.annotation`
529 # was already evaluated. TODO: is this method relevant?
530 # Can't we juste use `_generics.replace_types`?
531 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
533 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
534 _update_fields_from_docstrings( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH
535 cls,
536 fields,
537 # We can't rely on the (more reliable) frame inspection method
538 # for stdlib dataclasses:
539 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
540 )
542 return fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
545def is_valid_field_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
546 return not name.startswith('_') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
549def is_valid_privateattr_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
550 return name.startswith('_') and not name.startswith('__') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
553def takes_validated_data_argument( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
554 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
555) -> TypeIs[Callable[[dict[str, Any]], Any]]:
556 """Whether the provided default factory callable has a validated data parameter."""
557 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
558 sig = signature(default_factory) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
559 except (ValueError, TypeError): 1akblcmgwhxynopqrsABCDEdteufviFjGH
560 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
561 # In this case, we assume no data argument is present:
562 return False 1akblcmgwhxynopqrsABCDEdteufviFjGH
564 parameters = list(sig.parameters.values()) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH
566 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH