Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pydantic/_internal/_fields.py: 53%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

366 statements  

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 origin = cls.__pydantic_generic_metadata__['origin'] 

607 with ns_resolver.push(cls): 

608 for f_name, field_info in cls.__pydantic_fields__.items(): 

609 if field_info._complete: 

610 rebuilt_fields[f_name] = field_info 

611 else: 

612 annotation = field_info._original_annotation 

613 if origin is not None and (origin_field := origin.__pydantic_fields__.get(f_name)) is not None: 

614 # For parametrized generic models, the stored annotation can mix names from the origin class 

615 # and (forward) type arguments, which need to be evaluated in different namespaces. For instance: 

616 # 

617 # class Model[T](BaseModel): 

618 # a: dict[T, 'Later'] 

619 # 

620 # M = Model['Item'] 

621 # 

622 # The stored annotation of `M.a` is `dict['Item', 'Later']`, where `'Later'` must be evaluated 

623 # in the namespace of `Model` and `'Item'` in the namespace of `M`. Instead, 

624 # first evaluate the (unsubstituted) annotation of the origin in its own namespace: 

625 with ns_resolver.push(origin): 

626 annotation = _typing_extra.eval_type( 

627 origin_field._original_annotation, 

628 *ns_resolver.types_namespace, 

629 ) 

630 new_field = _recreate_field_info( 

631 field_info, 

632 ns_resolver=ns_resolver, 

633 typevars_map=typevars_map, 

634 lenient=False, 

635 annotation=annotation, 

636 ) 

637 update_field_from_config(config_wrapper, f_name, new_field) 

638 rebuilt_fields[f_name] = new_field 

639 

640 if cls.__pydantic_extra_info__ is not None and not cls.__pydantic_extra_info__.complete: 

641 # Not the best pattern (see comment in `_recreate_field_info()`): 

642 ann = _typing_extra.eval_type( 

643 cls.__pydantic_extra_info__.annotation, 

644 *ns_resolver.types_namespace, 

645 ) 

646 ann = _generics.replace_types(ann, typevars_map) 

647 ann = _typing_extra.eval_type( 

648 ann, 

649 *ns_resolver.types_namespace, 

650 ) 

651 rebuilt_extra_info = PydanticExtraInfo( 

652 annotation=ann, 

653 complete=True, 

654 ) 

655 else: 

656 rebuilt_extra_info = cls.__pydantic_extra_info__ 

657 

658 return rebuilt_fields, rebuilt_extra_info 

659 

660 

661def _recreate_field_info( 

662 field_info: FieldInfo, 

663 ns_resolver: NsResolver, 

664 typevars_map: Mapping[TypeVar, Any], 

665 *, 

666 lenient: bool, 

667 annotation: Any = PydanticUndefined, 

668) -> FieldInfo: 

669 FieldInfo_ = import_cached_field_info() 

670 

671 if annotation is PydanticUndefined: 

672 annotation = field_info._original_annotation 

673 

674 existing_desc = field_info.description 

675 if lenient: 

676 ann = _generics.replace_types(field_info._original_annotation, typevars_map) 

677 ann, evaluated = _typing_extra.try_eval_type( 

678 ann, 

679 *ns_resolver.types_namespace, 

680 ) 

681 else: 

682 # Not the best pattern, maybe we could ship our own `eval_type()`, 

683 # that would replace the type variables on the fly during evaluation. 

684 ann = _typing_extra.eval_type( 

685 annotation, 

686 *ns_resolver.types_namespace, 

687 ) 

688 ann = _generics.replace_types(ann, typevars_map) 

689 ann = _typing_extra.eval_type( 

690 ann, 

691 *ns_resolver.types_namespace, 

692 ) 

693 evaluated = True 

694 

695 if (assign := field_info._original_assignment) is PydanticUndefined: 

696 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 

697 else: 

698 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 

699 new_field._original_assignment = assign 

700 new_field._original_annotation = ann 

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

702 new_field.description = new_field.description if new_field.description is not None else existing_desc 

703 if not evaluated: 

704 new_field._complete = False 

705 

706 return new_field 

707 

708 

709def collect_dataclass_fields( 

710 cls: type[StandardDataclass], 

711 *, 

712 config_wrapper: ConfigWrapper, 

713 ns_resolver: NsResolver | None = None, 

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

715) -> dict[str, FieldInfo]: 

716 """Collect the fields of a dataclass. 

717 

718 Args: 

719 cls: dataclass. 

720 config_wrapper: The config wrapper instance. 

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

722 Defaults to an empty instance. 

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

724 

725 Returns: 

726 The dataclass fields. 

727 """ 

728 FieldInfo_ = import_cached_field_info() 

729 

730 fields: dict[str, FieldInfo] = {} 

731 ns_resolver = ns_resolver or NsResolver() 

732 dataclass_fields = cls.__dataclass_fields__ 

733 

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

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

736 # collected in each class: 

737 for base in reversed(cls.__mro__): 

738 if not dataclasses.is_dataclass(base): 

739 continue 

740 

741 with ns_resolver.push(base): 

742 for ann_name, dataclass_field in dataclass_fields.items(): 

743 base_anns = _typing_extra.safe_get_annotations(base) 

744 

745 if ann_name not in base_anns: 

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

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

748 continue 

749 

750 globalns, localns = ns_resolver.types_namespace 

751 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 

752 

753 if _typing_extra.is_classvar_annotation(ann_type): 

754 continue 

755 

756 if ( 

757 not dataclass_field.init 

758 and dataclass_field.default is dataclasses.MISSING 

759 and dataclass_field.default_factory is dataclasses.MISSING 

760 ): 

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

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

763 continue 

764 

765 if isinstance(dataclass_field.default, FieldInfo_): 

766 if dataclass_field.default.init_var: 

767 if dataclass_field.default.init is False: 

768 raise PydanticUserError( 

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

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

771 ) 

772 

773 # TODO: same note as above re validate_assignment 

774 continue 

775 field_info = FieldInfo_.from_annotated_attribute( 

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

777 ) 

778 field_info._original_assignment = dataclass_field.default 

779 else: 

780 field_info = FieldInfo_.from_annotated_attribute( 

781 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS 

782 ) 

783 field_info._original_assignment = dataclass_field 

784 

785 if not evaluated: 

786 field_info._complete = False 

787 field_info._original_annotation = ann_type 

788 

789 fields[ann_name] = field_info 

790 update_field_from_config(config_wrapper, ann_name, field_info) 

791 

792 if field_info.default is not PydanticUndefined and isinstance( 

793 getattr(cls, ann_name, field_info), FieldInfo_ 

794 ): 

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

796 setattr(cls, ann_name, field_info.default) 

797 

798 if typevars_map: 

799 for field in fields.values(): 

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

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

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

803 field.apply_typevars_map(typevars_map) 

804 

805 if config_wrapper.use_attribute_docstrings: 

806 _update_fields_from_docstrings( 

807 cls, 

808 fields, 

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

810 # for stdlib dataclasses: 

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

812 ) 

813 

814 return fields 

815 

816 

817def rebuild_dataclass_fields( 

818 cls: type[PydanticDataclass], 

819 *, 

820 config_wrapper: ConfigWrapper, 

821 ns_resolver: NsResolver, 

822 typevars_map: Mapping[TypeVar, Any], 

823) -> dict[str, FieldInfo]: 

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

825 

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

827 

828 Raises: 

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

830 

831 Note: 

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

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

834 """ 

835 FieldInfo_ = import_cached_field_info() 

836 

837 rebuilt_fields: dict[str, FieldInfo] = {} 

838 with ns_resolver.push(cls): 

839 for f_name, field_info in cls.__pydantic_fields__.items(): 

840 if field_info._complete: 

841 rebuilt_fields[f_name] = field_info 

842 else: 

843 existing_desc = field_info.description 

844 ann = _typing_extra.eval_type( 

845 field_info._original_annotation, 

846 *ns_resolver.types_namespace, 

847 ) 

848 ann = _generics.replace_types(ann, typevars_map) 

849 new_field = FieldInfo_.from_annotated_attribute( 

850 ann, 

851 field_info._original_assignment, 

852 _source=AnnotationSource.DATACLASS, 

853 ) 

854 

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

856 new_field.description = new_field.description if new_field.description is not None else existing_desc 

857 update_field_from_config(config_wrapper, f_name, new_field) 

858 rebuilt_fields[f_name] = new_field 

859 

860 return rebuilt_fields 

861 

862 

863def is_valid_field_name(name: str) -> bool: 

864 return not name.startswith('_') 

865 

866 

867def is_valid_privateattr_name(name: str) -> bool: 

868 return name.startswith('_') and not name.startswith('__') 

869 

870 

871def takes_validated_data_argument( 

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

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

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

875 try: 

876 sig = _typing_extra.signature_no_eval(default_factory) 

877 except (ValueError, TypeError): 

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

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

880 return False 

881 

882 parameters = list(sig.parameters.values()) 

883 

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

885 

886 

887def resolve_default_value( 

888 *, 

889 default: Any, 

890 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None, 

891 default_factory_takes_validated_data_argument: bool | None, 

892 validated_data: dict[str, Any] | None = None, 

893 call_default_factory: bool = False, 

894) -> Any: 

895 """Resolve the default value using either a static default or a default_factory.""" 

896 if default_factory is None: 

897 return smart_deepcopy(default) 

898 if call_default_factory: 

899 if default_factory_takes_validated_data_argument: 

900 fac = cast('Callable[[dict[str, Any]], Any]', default_factory) 

901 if validated_data is None: 

902 raise ValueError( 

903 "The default factory requires the 'validated_data' argument, which was not provided when calling 'get_default()'." 

904 ) 

905 return fac(validated_data) 

906 else: 

907 fac = cast('Callable[[], Any]', default_factory) 

908 return fac() 

909 

910 return PydanticUndefined