Coverage for pydantic/_internal/_fields.py: 98.15%

203 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-13 19:35 +0000

1"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`.""" 

2 

3from __future__ import annotations as _annotations 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

4 

5import dataclasses 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

6import warnings 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

7from collections.abc import Mapping 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

8from copy import copy 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

9from functools import cache 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

10from inspect import Parameter, ismethoddescriptor, signature 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

11from re import Pattern 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

12from typing import TYPE_CHECKING, Any, Callable, TypeVar 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

13 

14from pydantic_core import PydanticUndefined 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

15from typing_extensions import TypeIs 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

16 

17from pydantic import PydanticDeprecatedSince211 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

18from pydantic.errors import PydanticUserError 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

19 

20from . import _generics, _typing_extra 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

21from ._config import ConfigWrapper 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

22from ._docs_extraction import extract_docstrings_from_cls 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

23from ._import_utils import import_cached_base_model, import_cached_field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

24from ._namespace_utils import NsResolver 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

25from ._repr import Representation 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

26from ._utils import can_be_positional 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

27 

28if TYPE_CHECKING: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

29 from annotated_types import BaseMetadata 

30 

31 from ..fields import FieldInfo 

32 from ..main import BaseModel 

33 from ._dataclasses import StandardDataclass 

34 from ._decorators import DecoratorInfos 

35 

36 

37class PydanticMetadata(Representation): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

38 """Base class for annotation markers like `Strict`.""" 

39 

40 __slots__ = () 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

41 

42 

43def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

44 """Create a new `_PydanticGeneralMetadata` class with the given metadata. 

45 

46 Args: 

47 **metadata: The metadata to add. 

48 

49 Returns: 

50 The new `_PydanticGeneralMetadata` class. 

51 """ 

52 return _general_metadata_cls()(metadata) # type: ignore 1esakblftguFEvwmnopxyzAhBcqdriCjD

53 

54 

55@cache 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

56def _general_metadata_cls() -> type[BaseMetadata]: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

57 """Do it this way to avoid importing `annotated_types` at import time.""" 

58 from annotated_types import BaseMetadata 1esakblftguFEvwmnopxyzAhBcqdriCjD

59 

60 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1esakblftguFEvwmnopxyzAhBcqdriCjD

61 """Pydantic general metadata like `max_digits`.""" 

62 

63 def __init__(self, metadata: Any): 1esakblftguFEvwmnopxyzAhBcqdriCjD

64 self.__dict__ = metadata 1esakblftguFEvwmnopxyzAhBcqdriCjD

65 

66 return _PydanticGeneralMetadata # type: ignore 1esakblftguFEvwmnopxyzAhBcqdriCjD

67 

68 

69def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

70 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1esakblftguFEvwmnopxyzAhBcqdriCjD

71 for ann_name, field_info in fields.items(): 1esakblftguFEvwmnopxyzAhBcqdriCjD

72 if field_info.description is None and ann_name in fields_docs: 1esakblftguFEvwmnopxyzAhBcqdriCjD

73 field_info.description = fields_docs[ann_name] 1esakblftguFEvwmnopxyzAhBcqdriCjD

74 

75 

76def collect_model_fields( # noqa: C901 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

77 cls: type[BaseModel], 

78 config_wrapper: ConfigWrapper, 

79 ns_resolver: NsResolver | None, 

80 *, 

81 typevars_map: Mapping[TypeVar, Any] | None = None, 

82) -> tuple[dict[str, FieldInfo], set[str]]: 

83 """Collect the fields and class variables names of a nascent Pydantic model. 

84 

85 The fields collection process is *lenient*, meaning it won't error if string annotations 

86 fail to evaluate. If this happens, the original annotation (and assigned value, if any) 

87 is stored on the created `FieldInfo` instance. 

88 

89 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model), 

90 and will make use of these stored attributes. 

91 

92 Args: 

93 cls: BaseModel or dataclass. 

94 config_wrapper: The config wrapper instance. 

95 ns_resolver: Namespace resolver to use when getting model annotations. 

96 typevars_map: A dictionary mapping type variables to their concrete types. 

97 

98 Returns: 

99 A two-tuple containing model fields and class variables names. 

100 

101 Raises: 

102 NameError: 

103 - If there is a conflict between a field name and protected namespaces. 

104 - If there is a field other than `root` in `RootModel`. 

105 - If a field shadows an attribute in the parent model. 

106 """ 

107 BaseModel = import_cached_base_model() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

108 FieldInfo_ = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

109 

110 bases = cls.__bases__ 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

111 parent_fields_lookup: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

112 for base in reversed(bases): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

113 if model_fields := getattr(base, '__pydantic_fields__', None): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

114 parent_fields_lookup.update(model_fields) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

115 

116 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

117 

118 # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older 

119 # annotations is only used for finding fields in parent classes 

120 annotations = cls.__dict__.get('__annotations__', {}) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

121 fields: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

122 

123 class_vars: set[str] = set() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

124 for ann_name, (ann_type, evaluated) in type_hints.items(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

125 if ann_name == 'model_config': 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

126 # We never want to treat `model_config` as a field 

127 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no 

128 # protected namespaces (where `model_config` might be allowed as a field name) 

129 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

130 

131 for protected_namespace in config_wrapper.protected_namespaces: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

132 ns_violation: bool = False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

133 if isinstance(protected_namespace, Pattern): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

134 ns_violation = protected_namespace.match(ann_name) is not None 1esakblftguFEvwmnopxyzAhBcqdriCjD

135 elif isinstance(protected_namespace, str): 135 ↛ 138line 135 didn't jump to line 138 because the condition on line 135 was always true1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

136 ns_violation = ann_name.startswith(protected_namespace) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

137 

138 if ns_violation: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

139 for b in bases: 1esakblftguFEvwmnopxyzAhBcqdriCjD

140 if hasattr(b, ann_name): 1esakblftguFEvwmnopxyzAhBcqdriCjD

141 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})): 141 ↛ 139line 141 didn't jump to line 139 because the condition on line 141 was always true1eabfghcdij

142 raise NameError( 1eabfghcdij

143 f'Field "{ann_name}" conflicts with member {getattr(b, ann_name)}' 

144 f' of protected namespace "{protected_namespace}".' 

145 ) 

146 else: 

147 valid_namespaces = () 1esakblftguFEvwmnopxyzAhBcqdriCjD

148 for pn in config_wrapper.protected_namespaces: 1esakblftguFEvwmnopxyzAhBcqdriCjD

149 if isinstance(pn, Pattern): 1esakblftguFEvwmnopxyzAhBcqdriCjD

150 if not pn.match(ann_name): 150 ↛ 151line 150 didn't jump to line 151 because the condition on line 150 was never true1esakblftguFEvwmnopxyzAhBcqdriCjD

151 valid_namespaces += (f're.compile({pn.pattern})',) 

152 else: 

153 if not ann_name.startswith(pn): 1esakblftguFEvwmnopxyzAhBcqdriCjD

154 valid_namespaces += (pn,) 1esakblftguFEvwmnopxyzAhBcqdriCjD

155 

156 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD

157 f'Field "{ann_name}" in {cls.__name__} has conflict with protected namespace "{protected_namespace}".' 

158 '\n\nYou may be able to resolve this warning by setting' 

159 f" `model_config['protected_namespaces'] = {valid_namespaces}`.", 

160 UserWarning, 

161 ) 

162 if _typing_extra.is_classvar_annotation(ann_type): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

163 class_vars.add(ann_name) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

164 continue 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

165 

166 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

167 

168 if _is_finalvar_with_default_val(ann_type, assigned_value): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

169 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD

170 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a ' 

171 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you ' 

172 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`', 

173 category=PydanticDeprecatedSince211, 

174 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case: 

175 stacklevel=4, 

176 ) 

177 class_vars.add(ann_name) 1esakblftguFEvwmnopxyzAhBcqdriCjD

178 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

179 if not is_valid_field_name(ann_name): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

180 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

181 if cls.__pydantic_root_model__ and ann_name != 'root': 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

182 raise NameError( 1esakblftguFEvwmnopxyzAhBcqdriCjD

183 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`" 

184 ) 

185 

186 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get 

187 # "... shadows an attribute" warnings 

188 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

189 for base in bases: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

190 dataclass_fields = { 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

191 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ()) 

192 } 

193 if hasattr(base, ann_name): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

194 if base is generic_origin: 194 ↛ 196line 194 didn't jump to line 196 because the condition on line 194 was never true1esakblftguFEvwmnopxyzAhBcqdriCjD

195 # Don't warn when "shadowing" of attributes in parametrized generics 

196 continue 

197 

198 if ann_name in dataclass_fields: 1esakblftguFEvwmnopxyzAhBcqdriCjD

199 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set 

200 # on the class instance. 

201 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

202 

203 if ann_name not in annotations: 1esakblftguFEvwmnopxyzAhBcqdriCjD

204 # Don't warn when a field exists in a parent class but has not been defined in the current class 

205 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

206 

207 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD

208 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent ' 

209 f'"{base.__qualname__}"', 

210 UserWarning, 

211 ) 

212 

213 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

214 if ann_name in annotations: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

215 # field is present in the current model's annotations (and *not* from parent classes) 

216 field_info = FieldInfo_.from_annotation(ann_type) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

217 elif ann_name in parent_fields_lookup: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

218 # The field was present on one of the (possibly multiple) base classes 

219 # copy the field to make sure typevar substitutions don't cause issues with the base classes 

220 field_info = copy(parent_fields_lookup[ann_name]) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

221 else: 

222 # The field was not found on any base classes; this seems to be caused by fields not getting 

223 # generated thanks to models not being fully defined while initializing recursive models. 

224 # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this. 

225 field_info = FieldInfo_.from_annotation(ann_type) 1esakblftguFEvwmnopxyzAhBcqdriCjD

226 

227 if not evaluated: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

228 field_info._complete = False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

229 # Store the original annotation that should be used to rebuild 

230 # the field info later: 

231 field_info._original_annotation = ann_type 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

232 else: # An assigned value is present (either the default value, or a `Field()` function) 

233 _warn_on_nested_alias_in_annotation(ann_type, ann_name) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

234 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

235 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__` 

236 # for descriptors, so we do the same if the `= field(default=...)` form is used. 

237 # Note that we only do this for method descriptors for now, we might want to 

238 # extend this to any descriptor in the future (by simply checking for 

239 # `hasattr(assigned_value.default, '__get__')`). 

240 assigned_value.default = assigned_value.default.__get__(None, cls) 1esakblftguFEvwmnopxyzAhBcqdriCjD

241 

242 # The `from_annotated_attribute()` call below mutates the assigned `Field()`, so make a copy: 

243 original_assignment = ( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

244 copy(assigned_value) if not evaluated and isinstance(assigned_value, FieldInfo_) else assigned_value 

245 ) 

246 

247 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

248 if not evaluated: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

249 field_info._complete = False 1esakblftguFEvwmnopxyzAhBcqdriCjD

250 # Store the original annotation and assignment value that should be used to rebuild 

251 # the field info later: 

252 field_info._original_annotation = ann_type 1esakblftguFEvwmnopxyzAhBcqdriCjD

253 field_info._original_assignment = original_assignment 1esakblftguFEvwmnopxyzAhBcqdriCjD

254 

255 # attributes which are fields are removed from the class namespace: 

256 # 1. To match the behaviour of annotation-only fields 

257 # 2. To avoid false positives in the NameError check above 

258 try: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

259 delattr(cls, ann_name) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

260 except AttributeError: 1esakblftguFEvwmnopxyzAhBcqdriCjD

261 pass # indicates the attribute was on a parent class 1esakblftguFEvwmnopxyzAhBcqdriCjD

262 

263 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__ 

264 # to make sure the decorators have already been built for this exact class 

265 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

266 if ann_name in decorators.computed_fields: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

267 raise ValueError("you can't override a field with a computed field") 1esakblftguFEvwmnopxyzAhBcqdriCjD

268 fields[ann_name] = field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

269 

270 if typevars_map: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

271 for field in fields.values(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

272 if field._complete: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

273 field.apply_typevars_map(typevars_map) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

274 

275 if config_wrapper.use_attribute_docstrings: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

276 _update_fields_from_docstrings(cls, fields) 1esakblftguFEvwmnopxyzAhBcqdriCjD

277 return fields, class_vars 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

278 

279 

280def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> None: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

281 FieldInfo = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

282 

283 args = getattr(ann_type, '__args__', None) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

284 if args: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

285 for anno_arg in args: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

286 if _typing_extra.is_annotated(anno_arg): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

287 for anno_type_arg in _typing_extra.get_args(anno_arg): 1esakblftguFEvwmnopxyzAhBcqdriCjD

288 if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: 1esakblftguFEvwmnopxyzAhBcqdriCjD

289 warnings.warn( 1esakblftguFEvwmnopxyzAhBcqdriCjD

290 f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.', 

291 UserWarning, 

292 ) 

293 return 1esakblftguFEvwmnopxyzAhBcqdriCjD

294 

295 

296def _is_finalvar_with_default_val(ann_type: type[Any], assigned_value: Any) -> bool: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

297 if assigned_value is PydanticUndefined: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

298 return False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

299 

300 FieldInfo = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

301 

302 if isinstance(assigned_value, FieldInfo) and assigned_value.is_required(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

303 return False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

304 elif not _typing_extra.is_finalvar(ann_type): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

305 return False 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

306 else: 

307 return True 1esakblftguFEvwmnopxyzAhBcqdriCjD

308 

309 

310def rebuild_model_fields( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

311 cls: type[BaseModel], 

312 *, 

313 ns_resolver: NsResolver, 

314 typevars_map: Mapping[TypeVar, Any], 

315) -> dict[str, FieldInfo]: 

316 """Rebuild the (already present) model fields by trying to reevaluate annotations. 

317 

318 This function should be called whenever a model with incomplete fields is encountered. 

319 

320 Note: 

321 This function *doesn't* mutate the model fields in place, as it can be called during 

322 schema generation, where you don't want to mutate other model's fields. 

323 """ 

324 FieldInfo_ = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

325 

326 rebuilt_fields: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

327 with ns_resolver.push(cls): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

328 for f_name, field_info in cls.__pydantic_fields__.items(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

329 if field_info._complete: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

330 rebuilt_fields[f_name] = field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

331 else: 

332 ann = _typing_extra.eval_type( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

333 field_info._original_annotation, 

334 *ns_resolver.types_namespace, 

335 ) 

336 ann = _generics.replace_types(ann, typevars_map) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

337 

338 if (assign := field_info._original_assignment) is PydanticUndefined: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

339 rebuilt_fields[f_name] = FieldInfo_.from_annotation(ann) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

340 else: 

341 rebuilt_fields[f_name] = FieldInfo_.from_annotated_attribute(ann, assign) 1esakblftguFEvwmnopxyzAGhBcqdriCjD

342 

343 return rebuilt_fields 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

344 

345 

346def collect_dataclass_fields( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

347 cls: type[StandardDataclass], 

348 *, 

349 ns_resolver: NsResolver | None = None, 

350 typevars_map: dict[Any, Any] | None = None, 

351 config_wrapper: ConfigWrapper | None = None, 

352) -> dict[str, FieldInfo]: 

353 """Collect the fields of a dataclass. 

354 

355 Args: 

356 cls: dataclass. 

357 ns_resolver: Namespace resolver to use when getting dataclass annotations. 

358 Defaults to an empty instance. 

359 typevars_map: A dictionary mapping type variables to their concrete types. 

360 config_wrapper: The config wrapper instance. 

361 

362 Returns: 

363 The dataclass fields. 

364 """ 

365 FieldInfo_ = import_cached_field_info() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

366 

367 fields: dict[str, FieldInfo] = {} 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

368 ns_resolver = ns_resolver or NsResolver() 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

369 dataclass_fields = cls.__dataclass_fields__ 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

370 

371 # The logic here is similar to `_typing_extra.get_cls_type_hints`, 

372 # although we do it manually as stdlib dataclasses already have annotations 

373 # collected in each class: 

374 for base in reversed(cls.__mro__): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

375 if not dataclasses.is_dataclass(base): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

376 continue 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

377 

378 with ns_resolver.push(base): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

379 for ann_name, dataclass_field in dataclass_fields.items(): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

380 if ann_name not in base.__dict__.get('__annotations__', {}): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

381 # `__dataclass_fields__`contains every field, even the ones from base classes. 

382 # Only collect the ones defined on `base`. 

383 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

384 

385 globalns, localns = ns_resolver.types_namespace 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

386 ann_type, _ = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

387 

388 if _typing_extra.is_classvar_annotation(ann_type): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

389 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

390 

391 if ( 1esakblFEvwmnopGHIhBcqdr

392 not dataclass_field.init 

393 and dataclass_field.default is dataclasses.MISSING 

394 and dataclass_field.default_factory is dataclasses.MISSING 

395 ): 

396 # TODO: We should probably do something with this so that validate_assignment behaves properly 

397 # Issue: https://github.com/pydantic/pydantic/issues/5470 

398 continue 1esakblftguFEvwmnopxyzAhBcqdriCjD

399 

400 if isinstance(dataclass_field.default, FieldInfo_): 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

401 if dataclass_field.default.init_var: 1esakblftguFEvwmnopxyzAhBcqdriCjD

402 if dataclass_field.default.init is False: 1esakblftguFEvwmnopxyzAhBcqdriCjD

403 raise PydanticUserError( 1esakblftguFEvwmnopxyzAhBcqdriCjD

404 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.', 

405 code='clashing-init-and-init-var', 

406 ) 

407 

408 # TODO: same note as above re validate_assignment 

409 continue 1akblftguEmnopxyzAcqdriCjD

410 field_info = FieldInfo_.from_annotated_attribute(ann_type, dataclass_field.default) 1esakblftguFEvwmnopxyzAhBcqdriCjD

411 else: 

412 field_info = FieldInfo_.from_annotated_attribute(ann_type, dataclass_field) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

413 

414 fields[ann_name] = field_info 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

415 

416 if field_info.default is not PydanticUndefined and isinstance( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

417 getattr(cls, ann_name, field_info), FieldInfo_ 

418 ): 

419 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo 

420 setattr(cls, ann_name, field_info.default) 1esakblftguFEvwmnopxyzAhBcqdriCjD

421 

422 if typevars_map: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

423 for field in fields.values(): 1esakblftguFEvwmnopxyzAhBcqdriCjD

424 # We don't pass any ns, as `field.annotation` 

425 # was already evaluated. TODO: is this method relevant? 

426 # Can't we juste use `_generics.replace_types`? 

427 field.apply_typevars_map(typevars_map) 1esakblftguFEvwmnopxyzAhBcqdriCjD

428 

429 if config_wrapper is not None and config_wrapper.use_attribute_docstrings: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

430 _update_fields_from_docstrings( 1esakblftguFEvwmnopxyzAhBcqdriCjD

431 cls, 

432 fields, 

433 # We can't rely on the (more reliable) frame inspection method 

434 # for stdlib dataclasses: 

435 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'), 

436 ) 

437 

438 return fields 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

439 

440 

441def is_valid_field_name(name: str) -> bool: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

442 return not name.startswith('_') 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

443 

444 

445def is_valid_privateattr_name(name: str) -> bool: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

446 return name.startswith('_') and not name.startswith('__') 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

447 

448 

449def takes_validated_data_argument( 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

450 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any], 

451) -> TypeIs[Callable[[dict[str, Any]], Any]]: 

452 """Whether the provided default factory callable has a validated data parameter.""" 

453 try: 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

454 sig = signature(default_factory) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

455 except (ValueError, TypeError): 1esakblftguvwmnopxyzAhBcqdriCjD

456 # `inspect.signature` might not be able to infer a signature, e.g. with C objects. 

457 # In this case, we assume no data argument is present: 

458 return False 1esakblftguvwmnopxyzAhBcqdriCjD

459 

460 parameters = list(sig.parameters.values()) 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD

461 

462 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1esakblftguFEvwmnopxyzAGHIJKLMhBcqdriCjD