1"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
2
3from __future__ import annotations as _annotations
4
5import dataclasses
6import warnings
7from collections.abc import Callable, Mapping
8from functools import cache
9from inspect import Parameter, ismethoddescriptor
10from re import Pattern
11from typing import TYPE_CHECKING, Any, TypeVar, cast
12
13from pydantic_core import PydanticUndefined
14from typing_extensions import TypeForm, TypeIs, get_args, get_origin # noqa: UP035 (for `get_args` and `get_origin`)
15from typing_inspection import typing_objects
16from typing_inspection.introspection import AnnotationSource
17
18from pydantic import PydanticDeprecatedSince211
19from pydantic.errors import PydanticUserError
20
21from ..aliases import AliasGenerator
22from . import _generics, _typing_extra
23from ._config import ConfigWrapper
24from ._docs_extraction import extract_docstrings_from_cls
25from ._import_utils import import_cached_base_model, import_cached_field_info
26from ._namespace_utils import NsResolver
27from ._repr import Representation
28from ._utils import can_be_positional, get_first_not_none, smart_deepcopy
29
30if TYPE_CHECKING:
31 from annotated_types import BaseMetadata
32
33 from ..fields import FieldInfo, ModelPrivateAttr
34 from ..main import BaseModel
35 from ._dataclasses import PydanticDataclass, StandardDataclass
36 from ._decorators import DecoratorInfos
37
38
39class PydanticMetadata(Representation):
40 """Base class for annotation markers like `Strict`."""
41
42 __slots__ = ()
43
44
45@dataclasses.dataclass(slots=True, kw_only=True)
46class PydanticExtraInfo:
47 annotation: TypeForm[Any]
48 complete: bool
49
50
51@dataclasses.dataclass(slots=True, kw_only=True)
52class ModelNamespaceInfo:
53 """Information gathered by inspecting the namespace of a model class before it is created.
54
55 Decisions requiring the (possibly unevaluatable at class creation time) annotations (such as
56 whether a sunder-named attribute is a private attribute or a class variable) are deferred to
57 `collect_model_fields()`, where evaluated annotations are available.
58 """
59
60 private_attributes: dict[str, ModelPrivateAttr]
61 """Private attributes explicitly assigned a `ModelPrivateAttr` instance (removed from the namespace)."""
62
63 ignored_names: set[str]
64 """Names of the namespace attributes with a value of an ignored type."""
65
66 ignored_types: tuple[type[Any], ...]
67 """The ignored types (the `ignored_types` config value, together with the implicitly ignored types)."""
68
69 private_candidates: list[str]
70 """Sunder-named attributes assigned in the namespace, to be converted to private attributes
71 unless annotated as class variables."""
72
73 field_candidates: list[str]
74 """Namespace attributes with a valid field name, required to have a type annotation."""
75
76 base_field_names: set[str]
77 """The field names of the model's bases."""
78
79 model_config_assigned: bool
80 """Whether `model_config` was assigned a value in the namespace."""
81
82
83def pydantic_general_metadata(**metadata: Any) -> BaseMetadata:
84 """Create a new `_PydanticGeneralMetadata` class with the given metadata.
85
86 Args:
87 **metadata: The metadata to add.
88
89 Returns:
90 The new `_PydanticGeneralMetadata` class.
91 """
92 return _general_metadata_cls()(metadata) # type: ignore
93
94
95@cache
96def _general_metadata_cls() -> type[BaseMetadata]:
97 """Do it this way to avoid importing `annotated_types` at import time."""
98 from annotated_types import BaseMetadata
99
100 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata):
101 """Pydantic general metadata like `max_digits`."""
102
103 def __init__(self, metadata: Any):
104 self.__dict__ = metadata
105
106 return _PydanticGeneralMetadata # type: ignore
107
108
109def _check_protected_namespaces(
110 protected_namespaces: tuple[str | Pattern[str], ...],
111 ann_name: str,
112 bases: tuple[type[Any], ...],
113 cls_name: str,
114) -> None:
115 BaseModel = import_cached_base_model()
116
117 for protected_namespace in protected_namespaces:
118 ns_violation = False
119 if isinstance(protected_namespace, Pattern):
120 ns_violation = protected_namespace.match(ann_name) is not None
121 elif isinstance(protected_namespace, str):
122 ns_violation = ann_name.startswith(protected_namespace)
123
124 if ns_violation:
125 for b in bases:
126 if hasattr(b, ann_name):
127 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})):
128 raise ValueError(
129 f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}'
130 f' of protected namespace {protected_namespace!r}.'
131 )
132 else:
133 valid_namespaces: list[str] = []
134 for pn in protected_namespaces:
135 if isinstance(pn, Pattern):
136 if not pn.match(ann_name):
137 valid_namespaces.append(f're.compile({pn.pattern!r})')
138 else:
139 if not ann_name.startswith(pn):
140 valid_namespaces.append(f"'{pn}'")
141
142 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}'
143
144 warnings.warn(
145 f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n'
146 f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.",
147 UserWarning,
148 stacklevel=5,
149 )
150
151
152def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None:
153 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect)
154 for ann_name, field_info in fields.items():
155 if field_info.description is None and ann_name in fields_docs:
156 field_info.description = fields_docs[ann_name]
157
158
159def _apply_field_title_generator_to_field_info(
160 title_generator: Callable[[str, FieldInfo], str],
161 field_name: str,
162 field_info: FieldInfo,
163):
164 if field_info.title is None:
165 title = title_generator(field_name, field_info)
166 if not isinstance(title, str):
167 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}')
168
169 field_info.title = title
170
171
172def _apply_alias_generator_to_field_info(
173 alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo
174):
175 """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate.
176
177 Args:
178 alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
179 field_name: The name of the field from which to generate the alias.
180 field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied.
181 """
182 # Apply an alias_generator if
183 # 1. An alias is not specified
184 # 2. An alias is specified, but the priority is <= 1
185 if (
186 field_info.alias_priority is None
187 or field_info.alias_priority <= 1
188 or field_info.alias is None
189 or field_info.validation_alias is None
190 or field_info.serialization_alias is None
191 ):
192 alias, validation_alias, serialization_alias = None, None, None
193
194 if isinstance(alias_generator, AliasGenerator):
195 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name)
196 elif callable(alias_generator):
197 alias = alias_generator(field_name)
198 if not isinstance(alias, str):
199 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}')
200
201 # if priority is not set, we set to 1
202 # which supports the case where the alias_generator from a child class is used
203 # to generate an alias for a field in a parent class
204 if field_info.alias_priority is None or field_info.alias_priority <= 1:
205 field_info.alias_priority = 1
206
207 # if the priority is 1, then we set the aliases to the generated alias
208 if field_info.alias_priority == 1:
209 field_info.serialization_alias = get_first_not_none(serialization_alias, alias)
210 field_info.validation_alias = get_first_not_none(validation_alias, alias)
211 field_info.alias = alias
212
213 # if any of the aliases are not set, then we set them to the corresponding generated alias
214 if field_info.alias is None:
215 field_info.alias = alias
216 if field_info.serialization_alias is None:
217 field_info.serialization_alias = get_first_not_none(serialization_alias, alias)
218 if field_info.validation_alias is None:
219 field_info.validation_alias = get_first_not_none(validation_alias, alias)
220
221
222def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None:
223 """Update the `FieldInfo` instance from the configuration set on the model it belongs to.
224
225 This will apply the title and alias generators from the configuration.
226
227 Args:
228 config_wrapper: The configuration from the model.
229 field_name: The field name the `FieldInfo` instance is attached to.
230 field_info: The `FieldInfo` instance to update.
231 """
232 field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator
233 if field_title_generator is not None:
234 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info)
235 if config_wrapper.alias_generator is not None:
236 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info)
237
238
239_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'}
240
241_deprecated_classmethod_names = {
242 'parse_obj',
243 'parse_raw',
244 'parse_file',
245 'from_orm',
246 'construct',
247 'schema',
248 'schema_json',
249 'validate',
250 'update_forward_refs',
251 '_get_value',
252}
253
254
255@cache
256def _deprecated_base_model_method_ids() -> tuple[frozenset[int], frozenset[int]]:
257 """IDs of the deprecated `BaseModel` methods (and the underlying functions of the deprecated classmethods).
258
259 These are used to check if an assigned field value is one of the deprecated methods by identity.
260 The `BaseModel` class (and thus the methods) stays alive for the lifetime of the process, so the
261 IDs are guaranteed to be stable.
262 """
263 BaseModel_ = import_cached_base_model()
264 return (
265 frozenset(
266 id(method) for name in _deprecated_method_names if (method := getattr(BaseModel_, name, None)) is not None
267 ),
268 frozenset(
269 id(func)
270 for name in _deprecated_classmethod_names
271 if (func := getattr(getattr(BaseModel_, name, None), '__func__', None)) is not None
272 ),
273 )
274
275
276def collect_model_fields( # noqa: C901
277 cls: type[BaseModel],
278 config_wrapper: ConfigWrapper,
279 ns_resolver: NsResolver,
280 *,
281 typevars_map: Mapping[TypeVar, Any] | None = None,
282 namespace_info: ModelNamespaceInfo | None = None,
283) -> tuple[dict[str, FieldInfo], PydanticExtraInfo | None, set[str], dict[str, ModelPrivateAttr]]:
284 """Collect the fields, class variables names and private attributes of a nascent Pydantic model.
285
286 The fields collection process is *lenient*, meaning it won't error if string annotations
287 fail to evaluate. If this happens, the original annotation (and assigned value, if any)
288 is stored on the created `FieldInfo` instance.
289
290 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
291 and will make use of these stored attributes.
292
293 Args:
294 cls: BaseModel or dataclass.
295 config_wrapper: The config wrapper instance.
296 ns_resolver: Namespace resolver to use when getting model annotations.
297 typevars_map: A dictionary mapping type variables to their concrete types.
298 namespace_info: The result of inspecting the class namespace before the class was created.
299
300 Returns:
301 A four-tuple containing the model fields, the `PydanticExtraInfo` instance if the `__pydantic_extra__` annotation is set,
302 the class variables names, and the private attributes discovered from the namespace and annotations.
303
304 Raises:
305 NameError: If there is a conflict between a field name and protected namespaces.
306 PydanticUserError: If there is a field other than `root` in `RootModel`, or if one of
307 the namespace checks performed with `namespace_info` fails.
308 """
309 FieldInfo_ = import_cached_field_info()
310
311 from ..fields import ModelPrivateAttr, PrivateAttr
312
313 bases = cls.__bases__
314 parent_fields_lookup: dict[str, FieldInfo] = {}
315 for base in reversed(bases):
316 if model_fields := getattr(base, '__pydantic_fields__', None):
317 parent_fields_lookup.update(model_fields)
318
319 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver)
320
321 # `cls_annotations` is only used to determine if an annotation comes from a parent class
322 cls_annotations = _typing_extra.safe_get_annotations(cls)
323
324 private_attributes: dict[str, ModelPrivateAttr] = {}
325
326 if namespace_info is not None:
327 if '__root__' in cls_annotations:
328 # This check also exists in `inspect_namespace()`, but for assignments only:
329 raise PydanticUserError(
330 "To define root models, use `pydantic.RootModel` rather than a field called '__root__'", code=None
331 )
332
333 if 'model_config' in cls_annotations and not namespace_info.model_config_assigned:
334 raise PydanticUserError(
335 '`model_config` cannot be used as a model field name. Use `model_config` for model configuration.',
336 code='model-config-invalid-field-name',
337 )
338
339 for var_name in namespace_info.private_candidates:
340 # A sunder-named attribute with an assigned value is a private attribute,
341 # unless annotated as a class variable:
342 hint = type_hints.get(var_name)
343 if hint is None or not _typing_extra.is_classvar_annotation(hint[0]):
344 # Unlike for the annotation-only private attributes below, `__set_name__()` must not be
345 # called on the wrapping `PrivateAttr()` here: the assigned value stayed in the namespace
346 # during class creation, so `type.__new__()` already invoked the protocol on it natively:
347 private_attributes[var_name] = cast('ModelPrivateAttr', PrivateAttr(default=cls.__dict__[var_name]))
348 delattr(cls, var_name)
349
350 for var_name in namespace_info.field_candidates:
351 if var_name in cls_annotations:
352 continue
353 if var_name in namespace_info.base_field_names:
354 raise PydanticUserError(
355 f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. '
356 f'All field definitions, including overrides, require a type annotation.',
357 code='model-field-overridden',
358 )
359 value = cls.__dict__[var_name]
360 if isinstance(value, FieldInfo_):
361 raise PydanticUserError(
362 f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation'
363 )
364 else:
365 raise PydanticUserError(
366 f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a '
367 f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this '
368 f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.",
369 code='model-field-missing-annotation',
370 )
371
372 fields: dict[str, FieldInfo] = {}
373
374 deprecated_method_ids, deprecated_classmethod_func_ids = _deprecated_base_model_method_ids()
375
376 class_vars: set[str] = set()
377 for ann_name, (ann_type, evaluated) in type_hints.items():
378 if ann_name == 'model_config':
379 # We never want to treat `model_config` as a field
380 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
381 # protected namespaces (where `model_config` might be allowed as a field name)
382 continue
383
384 _check_protected_namespaces(
385 protected_namespaces=config_wrapper.protected_namespaces,
386 ann_name=ann_name,
387 bases=bases,
388 cls_name=cls.__name__,
389 )
390
391 if _typing_extra.is_classvar_annotation(ann_type):
392 class_vars.add(ann_name)
393 continue
394
395 if not is_valid_field_name(ann_name):
396 if (
397 namespace_info is not None
398 and is_valid_privateattr_name(ann_name)
399 and ann_name in cls_annotations
400 and ann_name not in private_attributes
401 and ann_name not in namespace_info.private_attributes
402 and ann_name not in namespace_info.ignored_names
403 and ann_type not in namespace_info.ignored_types
404 and getattr(ann_type, '__module__', None) != 'functools'
405 ):
406 # A sunder-named annotation (with no assigned value, as this case is handled above)
407 # defines a private attribute:
408 private_attr: ModelPrivateAttr | None = None
409 if typing_objects.is_annotated(get_origin(ann_type)):
410 _, *metadata = get_args(ann_type)
411 private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None)
412 if private_attr is None:
413 private_attr = cast('ModelPrivateAttr', PrivateAttr())
414 private_attr.__set_name__(cls, ann_name)
415 private_attributes[ann_name] = private_attr
416 continue
417
418 assigned_value = getattr(cls, ann_name, PydanticUndefined)
419 if assigned_value is not PydanticUndefined and (
420 # One of the deprecated instance methods was used as a field name (e.g. `dict()`):
421 id(assigned_value) in deprecated_method_ids
422 # One of the deprecated class methods was used as a field name (e.g. `schema()`):
423 or (
424 (assigned_func := getattr(assigned_value, '__func__', None)) is not None
425 and id(assigned_func) in deprecated_classmethod_func_ids
426 )
427 ):
428 # Then `assigned_value` would be the method, even though no default was specified:
429 assigned_value = PydanticUndefined
430
431 if cls.__pydantic_root_model__ and ann_name != 'root':
432 raise PydanticUserError(
433 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`",
434 code=None,
435 )
436
437 for base in bases:
438 if hasattr(base, ann_name):
439 if ann_name not in cls_annotations:
440 # Don't warn when a field exists in a parent class but has not been defined in the current class
441 continue
442
443 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
444 # "... shadows an attribute" warnings
445 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
446 if base is generic_origin:
447 # Don't warn when "shadowing" of attributes in parametrized generics
448 continue
449
450 dataclass_fields = {
451 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
452 }
453 if ann_name in dataclass_fields:
454 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
455 # on the class instance.
456 continue
457
458 warnings.warn(
459 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
460 f'"{base.__qualname__}"',
461 UserWarning,
462 stacklevel=4,
463 )
464
465 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation
466 if ann_name in cls_annotations or ann_name not in parent_fields_lookup:
467 # field is either:
468 # - present in the current model's annotations (and *not* from parent classes)
469 # - not found on any base classes; this seems to be caused by fields not getting
470 # generated due to models not being fully defined while initializing recursive models.
471 # Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this.
472 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS)
473 field_info._original_annotation = ann_type
474 if not evaluated:
475 field_info._complete = False
476 # Store the original annotation that should be used to rebuild
477 # the field info later:
478 else:
479 # The field was present on one of the (possibly multiple) base classes, we make a copy directly from it.
480 parent_field_info = parent_fields_lookup[ann_name]._copy()
481
482 # The only case where substituting the type variables is relevant (i.e. when `typevars_map` is not empty)
483 # is when a generic class is parameterized (e.g. `MyGenericModel[int, str]`), which creates a new class object
484 # (unlike the stdlib genercis that create a generic alias). In this case, we are guaranteed to only have to copy
485 # from the origin/parent model (e.g. `MyGenericModel`).
486 if typevars_map:
487 field_info = _recreate_field_info(
488 parent_field_info, ns_resolver=ns_resolver, typevars_map=typevars_map, lenient=True
489 )
490 else:
491 field_info = parent_field_info
492
493 else: # An assigned value is present (either the default value, or a `Field()` function)
494 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default):
495 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
496 # for descriptors, so we do the same if the `= field(default=...)` form is used.
497 # Note that we only do this for method descriptors for now, we might want to
498 # extend this to any descriptor in the future (by simply checking for
499 # `hasattr(assigned_value.default, '__get__')`).
500 default = assigned_value.default.__get__(None, cls)
501 assigned_value.default = default
502 assigned_value._attributes_set['default'] = default
503
504 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS)
505
506 # Store the original annotation and assignment value that could be used to rebuild the field info later.
507 field_info._original_assignment = assigned_value
508 field_info._original_annotation = ann_type
509 if not evaluated:
510 field_info._complete = False
511 elif 'final' in field_info._qualifiers and not field_info.is_required():
512 warnings.warn(
513 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
514 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
515 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
516 category=PydanticDeprecatedSince211,
517 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
518 stacklevel=4,
519 )
520 class_vars.add(ann_name)
521 continue
522
523 # attributes which are fields are removed from the class namespace:
524 # 1. To match the behaviour of annotation-only fields
525 # 2. To avoid false positives in the NameError check above
526 try:
527 delattr(cls, ann_name)
528 except AttributeError:
529 pass # indicates the attribute was on a parent class
530
531 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
532 # to make sure the decorators have already been built for this exact class
533 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__']
534 if ann_name in decorators.computed_fields:
535 raise TypeError(
536 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
537 'This override with a computed_field is incompatible.'
538 )
539 fields[ann_name] = field_info
540
541 if field_info._complete:
542 # If not complete, this will be called in `rebuild_model_fields()`:
543 update_field_from_config(config_wrapper, ann_name, field_info)
544
545 if config_wrapper.use_attribute_docstrings:
546 _update_fields_from_docstrings(cls, fields)
547
548 pydantic_extra_info: PydanticExtraInfo | None = None
549 if '__pydantic_extra__' in type_hints:
550 parent_extra_info: PydanticExtraInfo | None = None
551 if '__pydantic_extra__' not in cls_annotations:
552 # The annotation is only present on parent classes. As with fields, we make use of the parent's
553 # extra info, where type variables are already substituted:
554 for base in bases:
555 parent_extra_info = getattr(base, '__pydantic_extra_info__', None)
556 if parent_extra_info is not None:
557 break
558
559 if parent_extra_info is not None:
560 # As with fields, the only case where substituting the type variables is relevant (i.e. when
561 # `typevars_map` is not empty) is when a generic class is parameterized:
562 if typevars_map and parent_extra_info.complete:
563 # If not complete, `rebuild_model_fields()` takes care of it:
564 pydantic_extra_info = PydanticExtraInfo(
565 annotation=_generics.replace_types(parent_extra_info.annotation, typevars_map),
566 complete=True,
567 )
568 else:
569 pydantic_extra_info = parent_extra_info
570 else:
571 ann, complete = type_hints['__pydantic_extra__']
572 if complete:
573 # If not complete, `rebuild_model_fields()` takes care of it:
574 ann = _generics.replace_types(ann, typevars_map)
575 pydantic_extra_info = PydanticExtraInfo(
576 annotation=ann,
577 complete=complete,
578 )
579
580 return fields, pydantic_extra_info, class_vars, private_attributes
581
582
583def rebuild_model_fields(
584 cls: type[BaseModel],
585 *,
586 config_wrapper: ConfigWrapper,
587 ns_resolver: NsResolver,
588 typevars_map: Mapping[TypeVar, Any],
589) -> tuple[dict[str, FieldInfo], PydanticExtraInfo | None]:
590 """Rebuild the (already present) model fields by trying to reevaluate annotations.
591
592 This function should be called whenever a model with incomplete fields is encountered.
593
594 Returns:
595 A two-tuple, the first element being the rebuilt fields, the second element being
596 the rebuild `PydanticExtraInfo` instance, if available.
597
598 Raises:
599 NameError: If one of the annotations failed to evaluate.
600
601 Note:
602 This function *doesn't* mutate the model fields in place, as it can be called during
603 schema generation, where you don't want to mutate other model's fields.
604 """
605 rebuilt_fields: dict[str, FieldInfo] = {}
606 with ns_resolver.push(cls):
607 for f_name, field_info in cls.__pydantic_fields__.items():
608 if field_info._complete:
609 rebuilt_fields[f_name] = field_info
610 else:
611 new_field = _recreate_field_info(
612 field_info, ns_resolver=ns_resolver, typevars_map=typevars_map, lenient=False
613 )
614 update_field_from_config(config_wrapper, f_name, new_field)
615 rebuilt_fields[f_name] = new_field
616
617 if cls.__pydantic_extra_info__ is not None and not cls.__pydantic_extra_info__.complete:
618 # Not the best pattern (see comment in `_recreate_field_info()`):
619 ann = _typing_extra.eval_type(
620 cls.__pydantic_extra_info__.annotation,
621 *ns_resolver.types_namespace,
622 )
623 ann = _generics.replace_types(ann, typevars_map)
624 ann = _typing_extra.eval_type(
625 ann,
626 *ns_resolver.types_namespace,
627 )
628 rebuilt_extra_info = PydanticExtraInfo(
629 annotation=ann,
630 complete=True,
631 )
632 else:
633 rebuilt_extra_info = cls.__pydantic_extra_info__
634
635 return rebuilt_fields, rebuilt_extra_info
636
637
638def _recreate_field_info(
639 field_info: FieldInfo,
640 ns_resolver: NsResolver,
641 typevars_map: Mapping[TypeVar, Any],
642 *,
643 lenient: bool,
644) -> FieldInfo:
645 FieldInfo_ = import_cached_field_info()
646
647 existing_desc = field_info.description
648 if lenient:
649 ann = _generics.replace_types(field_info._original_annotation, typevars_map)
650 ann, evaluated = _typing_extra.try_eval_type(
651 ann,
652 *ns_resolver.types_namespace,
653 )
654 else:
655 # Not the best pattern, maybe we could ship our own `eval_type()`,
656 # that would replace the type variables on the fly during evaluation.
657 ann = _typing_extra.eval_type(
658 field_info._original_annotation,
659 *ns_resolver.types_namespace,
660 )
661 ann = _generics.replace_types(ann, typevars_map)
662 ann = _typing_extra.eval_type(
663 ann,
664 *ns_resolver.types_namespace,
665 )
666 evaluated = True
667
668 if (assign := field_info._original_assignment) is PydanticUndefined:
669 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS)
670 else:
671 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS)
672 new_field._original_assignment = assign
673 new_field._original_annotation = ann
674 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
675 new_field.description = new_field.description if new_field.description is not None else existing_desc
676 if not evaluated:
677 new_field._complete = False
678
679 return new_field
680
681
682def collect_dataclass_fields(
683 cls: type[StandardDataclass],
684 *,
685 config_wrapper: ConfigWrapper,
686 ns_resolver: NsResolver | None = None,
687 typevars_map: dict[Any, Any] | None = None,
688) -> dict[str, FieldInfo]:
689 """Collect the fields of a dataclass.
690
691 Args:
692 cls: dataclass.
693 config_wrapper: The config wrapper instance.
694 ns_resolver: Namespace resolver to use when getting dataclass annotations.
695 Defaults to an empty instance.
696 typevars_map: A dictionary mapping type variables to their concrete types.
697
698 Returns:
699 The dataclass fields.
700 """
701 FieldInfo_ = import_cached_field_info()
702
703 fields: dict[str, FieldInfo] = {}
704 ns_resolver = ns_resolver or NsResolver()
705 dataclass_fields = cls.__dataclass_fields__
706
707 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
708 # although we do it manually as stdlib dataclasses already have annotations
709 # collected in each class:
710 for base in reversed(cls.__mro__):
711 if not dataclasses.is_dataclass(base):
712 continue
713
714 with ns_resolver.push(base):
715 for ann_name, dataclass_field in dataclass_fields.items():
716 base_anns = _typing_extra.safe_get_annotations(base)
717
718 if ann_name not in base_anns:
719 # `__dataclass_fields__`contains every field, even the ones from base classes.
720 # Only collect the ones defined on `base`.
721 continue
722
723 globalns, localns = ns_resolver.types_namespace
724 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns)
725
726 if _typing_extra.is_classvar_annotation(ann_type):
727 continue
728
729 if (
730 not dataclass_field.init
731 and dataclass_field.default is dataclasses.MISSING
732 and dataclass_field.default_factory is dataclasses.MISSING
733 ):
734 # TODO: We should probably do something with this so that validate_assignment behaves properly
735 # Issue: https://github.com/pydantic/pydantic/issues/5470
736 continue
737
738 if isinstance(dataclass_field.default, FieldInfo_):
739 if dataclass_field.default.init_var:
740 if dataclass_field.default.init is False:
741 raise PydanticUserError(
742 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
743 code='clashing-init-and-init-var',
744 )
745
746 # TODO: same note as above re validate_assignment
747 continue
748 field_info = FieldInfo_.from_annotated_attribute(
749 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
750 )
751 field_info._original_assignment = dataclass_field.default
752 else:
753 field_info = FieldInfo_.from_annotated_attribute(
754 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
755 )
756 field_info._original_assignment = dataclass_field
757
758 if not evaluated:
759 field_info._complete = False
760 field_info._original_annotation = ann_type
761
762 fields[ann_name] = field_info
763 update_field_from_config(config_wrapper, ann_name, field_info)
764
765 if field_info.default is not PydanticUndefined and isinstance(
766 getattr(cls, ann_name, field_info), FieldInfo_
767 ):
768 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
769 setattr(cls, ann_name, field_info.default)
770
771 if typevars_map:
772 for field in fields.values():
773 # We don't pass any ns, as `field.annotation`
774 # was already evaluated. TODO: is this method relevant?
775 # Can't we juste use `_generics.replace_types`?
776 field.apply_typevars_map(typevars_map)
777
778 if config_wrapper.use_attribute_docstrings:
779 _update_fields_from_docstrings(
780 cls,
781 fields,
782 # We can't rely on the (more reliable) frame inspection method
783 # for stdlib dataclasses:
784 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
785 )
786
787 return fields
788
789
790def rebuild_dataclass_fields(
791 cls: type[PydanticDataclass],
792 *,
793 config_wrapper: ConfigWrapper,
794 ns_resolver: NsResolver,
795 typevars_map: Mapping[TypeVar, Any],
796) -> dict[str, FieldInfo]:
797 """Rebuild the (already present) dataclass fields by trying to reevaluate annotations.
798
799 This function should be called whenever a dataclass with incomplete fields is encountered.
800
801 Raises:
802 NameError: If one of the annotations failed to evaluate.
803
804 Note:
805 This function *doesn't* mutate the dataclass fields in place, as it can be called during
806 schema generation, where you don't want to mutate other dataclass's fields.
807 """
808 FieldInfo_ = import_cached_field_info()
809
810 rebuilt_fields: dict[str, FieldInfo] = {}
811 with ns_resolver.push(cls):
812 for f_name, field_info in cls.__pydantic_fields__.items():
813 if field_info._complete:
814 rebuilt_fields[f_name] = field_info
815 else:
816 existing_desc = field_info.description
817 ann = _typing_extra.eval_type(
818 field_info._original_annotation,
819 *ns_resolver.types_namespace,
820 )
821 ann = _generics.replace_types(ann, typevars_map)
822 new_field = FieldInfo_.from_annotated_attribute(
823 ann,
824 field_info._original_assignment,
825 _source=AnnotationSource.DATACLASS,
826 )
827
828 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
829 new_field.description = new_field.description if new_field.description is not None else existing_desc
830 update_field_from_config(config_wrapper, f_name, new_field)
831 rebuilt_fields[f_name] = new_field
832
833 return rebuilt_fields
834
835
836def is_valid_field_name(name: str) -> bool:
837 return not name.startswith('_')
838
839
840def is_valid_privateattr_name(name: str) -> bool:
841 return name.startswith('_') and not name.startswith('__')
842
843
844def takes_validated_data_argument(
845 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
846) -> TypeIs[Callable[[dict[str, Any]], Any]]:
847 """Whether the provided default factory callable has a validated data parameter."""
848 try:
849 sig = _typing_extra.signature_no_eval(default_factory)
850 except (ValueError, TypeError):
851 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
852 # In this case, we assume no data argument is present:
853 return False
854
855 parameters = list(sig.parameters.values())
856
857 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty
858
859
860def resolve_default_value(
861 *,
862 default: Any,
863 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None,
864 default_factory_takes_validated_data_argument: bool | None,
865 validated_data: dict[str, Any] | None = None,
866 call_default_factory: bool = False,
867) -> Any:
868 """Resolve the default value using either a static default or a default_factory."""
869 if default_factory is None:
870 return smart_deepcopy(default)
871 if call_default_factory:
872 if default_factory_takes_validated_data_argument:
873 fac = cast('Callable[[dict[str, Any]], Any]', default_factory)
874 if validated_data is None:
875 raise ValueError(
876 "The default factory requires the 'validated_data' argument, which was not provided when calling 'get_default()'."
877 )
878 return fac(validated_data)
879 else:
880 fac = cast('Callable[[], Any]', default_factory)
881 return fac()
882
883 return PydanticUndefined