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

252 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-05 10:27 +0000

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

2 

3from __future__ import annotations as _annotations 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

4 

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

13 

14from pydantic_core import PydanticUndefined 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

15from typing_extensions import TypeIs 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

16from typing_inspection.introspection import AnnotationSource 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

17 

18from pydantic import PydanticDeprecatedSince211 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

19from pydantic.errors import PydanticUserError 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

20 

21from ..aliases import AliasGenerator 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

22from . import _generics, _typing_extra 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

23from ._config import ConfigWrapper 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

24from ._docs_extraction import extract_docstrings_from_cls 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

25from ._import_utils import import_cached_base_model, import_cached_field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

26from ._namespace_utils import NsResolver 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

27from ._repr import Representation 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

28from ._utils import can_be_positional, get_first_not_none 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

29 

30if TYPE_CHECKING: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

31 from annotated_types import BaseMetadata 

32 

33 from ..fields import FieldInfo 

34 from ..main import BaseModel 

35 from ._dataclasses import PydanticDataclass, StandardDataclass 

36 from ._decorators import DecoratorInfos 

37 

38 

39class PydanticMetadata(Representation): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

41 

42 __slots__ = () 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

43 

44 

45def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

47 

48 Args: 

49 **metadata: The metadata to add. 

50 

51 Returns: 

52 The new `_PydanticGeneralMetadata` class. 

53 """ 

54 return _general_metadata_cls()(metadata) # type: ignore 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

55 

56 

57@cache 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

58def _general_metadata_cls() -> type[BaseMetadata]: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

60 from annotated_types import BaseMetadata 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

61 

62 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

64 

65 def __init__(self, metadata: Any): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

66 self.__dict__ = metadata 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

67 

68 return _PydanticGeneralMetadata # type: ignore 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

69 

70 

71def _check_protected_namespaces( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

72 protected_namespaces: tuple[str | Pattern[str], ...], 

73 ann_name: str, 

74 bases: tuple[type[Any], ...], 

75 cls_name: str, 

76) -> None: 

77 BaseModel = import_cached_base_model() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

78 

79 for protected_namespace in protected_namespaces: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

80 ns_violation = False 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

81 if isinstance(protected_namespace, Pattern): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

82 ns_violation = protected_namespace.match(ann_name) is not None 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

83 elif isinstance(protected_namespace, str): 83 ↛ 86line 83 didn't jump to line 86 because the condition on line 83 was always true1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

84 ns_violation = ann_name.startswith(protected_namespace) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

85 

86 if ns_violation: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

87 for b in bases: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

88 if hasattr(b, ann_name): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

90 raise ValueError( 1abcghdefij

91 f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}' 

92 f' of protected namespace {protected_namespace!r}.' 

93 ) 

94 else: 

95 valid_namespaces: list[str] = [] 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

96 for pn in protected_namespaces: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

97 if isinstance(pn, Pattern): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

98 if not pn.match(ann_name): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

99 valid_namespaces.append(f're.compile({pn.pattern!r})') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

100 else: 

101 if not ann_name.startswith(pn): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

102 valid_namespaces.append(f"'{pn}'") 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

103 

104 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}' 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

105 

106 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

107 f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n' 

108 f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.", 

109 UserWarning, 

110 stacklevel=5, 

111 ) 

112 

113 

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

115 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

116 for ann_name, field_info in fields.items(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

117 if field_info.description is None and ann_name in fields_docs: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

118 field_info.description = fields_docs[ann_name] 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

119 

120 

121def _apply_field_title_generator_to_field_info( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

122 title_generator: Callable[[str, FieldInfo], str], 

123 field_name: str, 

124 field_info: FieldInfo, 

125): 

126 if field_info.title is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

127 title = title_generator(field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

128 if not isinstance(title, str): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

129 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

130 

131 field_info.title = title 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

132 

133 

134def _apply_alias_generator_to_field_info( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

135 alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo 

136): 

137 """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate. 

138 

139 Args: 

140 alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance. 

141 field_name: The name of the field from which to generate the alias. 

142 field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied. 

143 """ 

144 # Apply an alias_generator if 

145 # 1. An alias is not specified 

146 # 2. An alias is specified, but the priority is <= 1 

147 if ( 1akIznodt

148 field_info.alias_priority is None 

149 or field_info.alias_priority <= 1 

150 or field_info.alias is None 

151 or field_info.validation_alias is None 

152 or field_info.serialization_alias is None 

153 ): 

154 alias, validation_alias, serialization_alias = None, None, None 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

155 

156 if isinstance(alias_generator, AliasGenerator): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

157 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

158 elif callable(alias_generator): 158 ↛ 166line 158 didn't jump to line 166 because the condition on line 158 was always true1akblcmgwhxyIznopqrsABCDEdteufviFjGH

159 alias = alias_generator(field_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

160 if not isinstance(alias, str): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

161 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

162 

163 # if priority is not set, we set to 1 

164 # which supports the case where the alias_generator from a child class is used 

165 # to generate an alias for a field in a parent class 

166 if field_info.alias_priority is None or field_info.alias_priority <= 1: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

167 field_info.alias_priority = 1 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

168 

169 # if the priority is 1, then we set the aliases to the generated alias 

170 if field_info.alias_priority == 1: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

171 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

172 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

173 field_info.alias = alias 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

174 

175 # if any of the aliases are not set, then we set them to the corresponding generated alias 

176 if field_info.alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

177 field_info.alias = alias 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

178 if field_info.serialization_alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

179 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

180 if field_info.validation_alias is None: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

181 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

182 

183 

184def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

185 """Update the `FieldInfo` instance from the configuration set on the model it belongs to. 

186 

187 This will apply the title and alias generators from the configuration. 

188 

189 Args: 

190 config_wrapper: The configuration from the model. 

191 field_name: The field name the `FieldInfo` instance is attached to. 

192 field_info: The `FieldInfo` instance to update. 

193 """ 

194 field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

195 if field_title_generator is not None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

196 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

197 if config_wrapper.alias_generator is not None: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

198 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

199 

200 

201_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

202 

203_deprecated_classmethod_names = { 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

204 'parse_obj', 

205 'parse_raw', 

206 'parse_file', 

207 'from_orm', 

208 'construct', 

209 'schema', 

210 'schema_json', 

211 'validate', 

212 'update_forward_refs', 

213 '_get_value', 

214} 

215 

216 

217def collect_model_fields( # noqa: C901 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

218 cls: type[BaseModel], 

219 config_wrapper: ConfigWrapper, 

220 ns_resolver: NsResolver | None, 

221 *, 

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

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

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

225 

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

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

228 is stored on the created `FieldInfo` instance. 

229 

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

231 and will make use of these stored attributes. 

232 

233 Args: 

234 cls: BaseModel or dataclass. 

235 config_wrapper: The config wrapper instance. 

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

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

238 

239 Returns: 

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

241 

242 Raises: 

243 NameError: 

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

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

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

247 """ 

248 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

249 BaseModel_ = import_cached_base_model() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

250 

251 bases = cls.__bases__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

252 parent_fields_lookup: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

253 for base in reversed(bases): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

254 if model_fields := getattr(base, '__pydantic_fields__', None): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

255 parent_fields_lookup.update(model_fields) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

256 

257 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

258 

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

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

261 annotations = cls.__dict__.get('__annotations__', {}) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

262 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

263 

264 class_vars: set[str] = set() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

265 for ann_name, (ann_type, evaluated) in type_hints.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

266 if ann_name == 'model_config': 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

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

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

270 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

271 

272 _check_protected_namespaces( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

273 protected_namespaces=config_wrapper.protected_namespaces, 

274 ann_name=ann_name, 

275 bases=bases, 

276 cls_name=cls.__name__, 

277 ) 

278 

279 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

280 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

281 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

282 

283 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

284 if assigned_value is not PydanticUndefined and ( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

285 # One of the deprecated instance methods was used as a field name (e.g. `dict()`): 

286 any(getattr(BaseModel_, depr_name, None) is assigned_value for depr_name in _deprecated_method_names) 

287 # One of the deprecated class methods was used as a field name (e.g. `schema()`): 

288 or ( 

289 hasattr(assigned_value, '__func__') 

290 and any( 

291 getattr(getattr(BaseModel_, depr_name, None), '__func__', None) is assigned_value.__func__ # pyright: ignore[reportAttributeAccessIssue] 

292 for depr_name in _deprecated_classmethod_names 

293 ) 

294 ) 

295 ): 

296 # Then `assigned_value` would be the method, even though no default was specified: 

297 assigned_value = PydanticUndefined 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

298 

299 if not is_valid_field_name(ann_name): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

300 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

301 if cls.__pydantic_root_model__ and ann_name != 'root': 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

302 raise NameError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

304 ) 

305 

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

307 # "... shadows an attribute" warnings 

308 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

309 for base in bases: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

310 dataclass_fields = { 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

312 } 

313 if hasattr(base, ann_name): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

314 if base is generic_origin: 314 ↛ 316line 314 didn't jump to line 316 because the condition on line 314 was never true1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

316 continue 

317 

318 if ann_name in dataclass_fields: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

320 # on the class instance. 

321 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

322 

323 if ann_name not in annotations: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

325 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

326 

327 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

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

330 UserWarning, 

331 ) 

332 

333 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

334 if ann_name in annotations or ann_name not in parent_fields_lookup: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

335 # field is either: 

336 # - present in the current model's annotations (and *not* from parent classes) 

337 # - not found on any base classes; this seems to be caused by fields bot getting 

338 # generated due to models not being fully defined while initializing recursive models. 

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

340 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

341 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

342 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

344 # the field info later: 

345 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

346 else: 

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

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

349 field_info = copy(parent_fields_lookup[ann_name]) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

350 

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

352 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

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

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

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

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

358 default = assigned_value.default.__get__(None, cls) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

359 assigned_value.default = default 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

360 assigned_value._attributes_set['default'] = default 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

361 

362 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

363 # Store the original annotation and assignment value that should be used to rebuild the field info later. 

364 # Note that the assignment is always stored as the annotation might contain a type var that is later 

365 # parameterized with an unknown forward reference (and we'll need it to rebuild the field info): 

366 field_info._original_assignment = assigned_value 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

367 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

368 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

369 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

370 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

371 warnings.warn( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

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

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

375 category=PydanticDeprecatedSince211, 

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

377 stacklevel=4, 

378 ) 

379 class_vars.add(ann_name) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

380 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

381 

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

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

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

385 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

386 delattr(cls, ann_name) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

387 except AttributeError: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

388 pass # indicates the attribute was on a parent class 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

389 

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

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

392 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

393 if ann_name in decorators.computed_fields: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

394 raise TypeError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

395 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. ' 

396 'This override with a computed_field is incompatible.' 

397 ) 

398 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

399 

400 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

401 # If not complete, this will be called in `rebuild_model_fields()`: 

402 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

403 

404 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

405 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

406 if field._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

407 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

408 

409 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

410 _update_fields_from_docstrings(cls, fields) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

411 return fields, class_vars 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

412 

413 

414def rebuild_model_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

415 cls: type[BaseModel], 

416 *, 

417 config_wrapper: ConfigWrapper, 

418 ns_resolver: NsResolver, 

419 typevars_map: Mapping[TypeVar, Any], 

420) -> dict[str, FieldInfo]: 

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

422 

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

424 

425 Raises: 

426 NameError: If one of the annotations failed to evaluate. 

427 

428 Note: 

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

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

431 """ 

432 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

433 

434 rebuilt_fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

435 with ns_resolver.push(cls): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

436 for f_name, field_info in cls.__pydantic_fields__.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

437 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

438 rebuilt_fields[f_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

439 else: 

440 existing_desc = field_info.description 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

441 ann = _typing_extra.eval_type( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

442 field_info._original_annotation, 

443 *ns_resolver.types_namespace, 

444 ) 

445 ann = _generics.replace_types(ann, typevars_map) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

446 

447 if (assign := field_info._original_assignment) is PydanticUndefined: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

448 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

449 else: 

450 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

451 # The description might come from the docstring if `use_attribute_docstrings` was `True`: 

452 new_field.description = new_field.description if new_field.description is not None else existing_desc 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

453 update_field_from_config(config_wrapper, f_name, new_field) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

454 rebuilt_fields[f_name] = new_field 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

455 

456 return rebuilt_fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

457 

458 

459def collect_dataclass_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

460 cls: type[StandardDataclass], 

461 *, 

462 config_wrapper: ConfigWrapper, 

463 ns_resolver: NsResolver | None = None, 

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

465) -> dict[str, FieldInfo]: 

466 """Collect the fields of a dataclass. 

467 

468 Args: 

469 cls: dataclass. 

470 config_wrapper: The config wrapper instance. 

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

472 Defaults to an empty instance. 

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

474 

475 Returns: 

476 The dataclass fields. 

477 """ 

478 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

479 

480 fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

481 ns_resolver = ns_resolver or NsResolver() 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

482 dataclass_fields = cls.__dataclass_fields__ 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

483 

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

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

486 # collected in each class: 

487 for base in reversed(cls.__mro__): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

488 if not dataclasses.is_dataclass(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

489 continue 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

490 

491 with ns_resolver.push(base): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

492 for ann_name, dataclass_field in dataclass_fields.items(): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

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

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

496 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

497 

498 globalns, localns = ns_resolver.types_namespace 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

499 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

500 

501 if _typing_extra.is_classvar_annotation(ann_type): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

502 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

503 

504 if ( 1akblcmIznopqrsdteufv

505 not dataclass_field.init 

506 and dataclass_field.default is dataclasses.MISSING 

507 and dataclass_field.default_factory is dataclasses.MISSING 

508 ): 

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

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

511 continue 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

512 

513 if isinstance(dataclass_field.default, FieldInfo_): 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

514 if dataclass_field.default.init_var: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

515 if dataclass_field.default.init is False: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

516 raise PydanticUserError( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

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

519 ) 

520 

521 # TODO: same note as above re validate_assignment 

522 continue 1blcmgwhxyzpqrsABCDEeufviFjGH

523 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

524 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS 

525 ) 

526 field_info._original_assignment = dataclass_field.default 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

527 else: 

528 field_info = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

529 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS 

530 ) 

531 field_info._original_assignment = dataclass_field 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

532 

533 if not evaluated: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

534 field_info._complete = False 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

535 field_info._original_annotation = ann_type 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

536 

537 fields[ann_name] = field_info 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

538 update_field_from_config(config_wrapper, ann_name, field_info) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

539 

540 if field_info.default is not PydanticUndefined and isinstance( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

541 getattr(cls, ann_name, field_info), FieldInfo_ 

542 ): 

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

544 setattr(cls, ann_name, field_info.default) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

545 

546 if typevars_map: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

547 for field in fields.values(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

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

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

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

551 field.apply_typevars_map(typevars_map) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

552 

553 if config_wrapper.use_attribute_docstrings: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

554 _update_fields_from_docstrings( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

555 cls, 

556 fields, 

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

558 # for stdlib dataclasses: 

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

560 ) 

561 

562 return fields 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

563 

564 

565def rebuild_dataclass_fields( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

566 cls: type[PydanticDataclass], 

567 *, 

568 config_wrapper: ConfigWrapper, 

569 ns_resolver: NsResolver, 

570 typevars_map: Mapping[TypeVar, Any], 

571) -> dict[str, FieldInfo]: 

572 """Rebuild the (already present) dataclass fields by trying to reevaluate annotations. 

573 

574 This function should be called whenever a dataclass with incomplete fields is encountered. 

575 

576 Raises: 

577 NameError: If one of the annotations failed to evaluate. 

578 

579 Note: 

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

581 schema generation, where you don't want to mutate other dataclass's fields. 

582 """ 

583 FieldInfo_ = import_cached_field_info() 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

584 

585 rebuilt_fields: dict[str, FieldInfo] = {} 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

586 with ns_resolver.push(cls): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

587 for f_name, field_info in cls.__pydantic_fields__.items(): 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

588 if field_info._complete: 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

589 rebuilt_fields[f_name] = field_info 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

590 else: 

591 existing_desc = field_info.description 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

592 ann = _typing_extra.eval_type( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

593 field_info._original_annotation, 

594 *ns_resolver.types_namespace, 

595 ) 

596 ann = _generics.replace_types(ann, typevars_map) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

597 new_field = FieldInfo_.from_annotated_attribute( 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

598 ann, 

599 field_info._original_assignment, 

600 _source=AnnotationSource.DATACLASS, 

601 ) 

602 

603 # The description might come from the docstring if `use_attribute_docstrings` was `True`: 

604 new_field.description = new_field.description if new_field.description is not None else existing_desc 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

605 update_field_from_config(config_wrapper, f_name, new_field) 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

606 rebuilt_fields[f_name] = new_field 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

607 

608 return rebuilt_fields 1akblcmgwhxyIznopqrsABCDEdteufviFjGH

609 

610 

611def is_valid_field_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

612 return not name.startswith('_') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

613 

614 

615def is_valid_privateattr_name(name: str) -> bool: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

616 return name.startswith('_') and not name.startswith('__') 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

617 

618 

619def takes_validated_data_argument( 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

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

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

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

623 try: 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

624 sig = signature(default_factory) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

625 except (ValueError, TypeError): 1akblcmgwhxynopqrsABCDEdteufviFjGH

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

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

628 return False 1akblcmgwhxynopqrsABCDEdteufviFjGH

629 

630 parameters = list(sig.parameters.values()) 1akblcmgwhxyIznopqrsABCDEJdteufviFjGH

631 

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