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

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

343 statements  

1"""Private logic for creating models.""" 

2 

3from __future__ import annotations as _annotations 

4 

5import operator 

6import sys 

7import typing 

8import warnings 

9import weakref 

10from abc import ABCMeta 

11from collections.abc import Callable, MutableMapping 

12from functools import cache, partial, wraps 

13from types import FunctionType 

14from typing import TYPE_CHECKING, Any, Generic, Literal, NoReturn, TypeVar, cast 

15 

16from pydantic_core import PydanticUndefined, SchemaSerializer 

17from typing_extensions import ( 

18 TypeAliasType, 

19 dataclass_transform, 

20 deprecated, 

21) 

22 

23from ..errors import PydanticUndefinedAnnotation, PydanticUserError 

24from ..plugin._schema_validator import create_schema_validator 

25from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20 

26from ._config import ConfigWrapper 

27from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function 

28from ._fields import ( 

29 ModelNamespaceInfo, 

30 collect_model_fields, 

31 is_valid_field_name, 

32 is_valid_privateattr_name, 

33 rebuild_model_fields, 

34) 

35from ._generate_schema import GenerateSchema, InvalidSchemaError 

36from ._generics import PydanticGenericMetadata, get_model_typevars_map 

37from ._import_utils import import_cached_base_model, import_cached_field_info 

38from ._mock_val_ser import set_model_mocks 

39from ._namespace_utils import NsResolver 

40from ._signature import generate_pydantic_signature 

41from ._typing_extra import parent_frame_namespace 

42from ._utils import LazyClassAttribute, SafeGetItemProxy 

43 

44if TYPE_CHECKING: 

45 from ..fields import Field as PydanticModelField 

46 from ..fields import FieldInfo, ModelPrivateAttr 

47 from ..fields import PrivateAttr as PydanticModelPrivateAttr 

48 from ..main import BaseModel 

49 from ._fields import PydanticExtraInfo 

50else: 

51 PydanticModelField = object() 

52 PydanticModelPrivateAttr = object() 

53 

54object_setattr = object.__setattr__ 

55 

56 

57class _ModelNamespaceDict(dict): 

58 """A dictionary subclass that intercepts attribute setting on model classes and 

59 warns about overriding of decorators. 

60 """ 

61 

62 def __setitem__(self, k: str, v: object) -> None: 

63 existing: Any = self.get(k, None) 

64 if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy): 

65 warnings.warn( 

66 f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator', 

67 stacklevel=2, 

68 ) 

69 

70 return super().__setitem__(k, v) 

71 

72 

73def NoInitField( 

74 *, 

75 init: Literal[False] = False, 

76) -> Any: 

77 """Only for typing purposes. Used as default value of `__pydantic_fields_set__`, 

78 `__pydantic_extra__`, `__pydantic_private__`, so they could be ignored when 

79 synthesizing the `__init__` signature. 

80 """ 

81 

82 

83# For ModelMetaclass.register(): 

84_T = TypeVar('_T') 

85 

86 

87@dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr, NoInitField)) 

88class ModelMetaclass(ABCMeta): 

89 def __new__( 

90 mcs, 

91 cls_name: str, 

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

93 namespace: dict[str, Any], 

94 __pydantic_generic_metadata__: PydanticGenericMetadata | None = None, 

95 __pydantic_reset_parent_namespace__: bool = True, 

96 _create_model_module: str | None = None, 

97 **kwargs: Any, 

98 ) -> type: 

99 """Metaclass for creating Pydantic models. 

100 

101 Args: 

102 cls_name: The name of the class to be created. 

103 bases: The base classes of the class to be created. 

104 namespace: The attribute dictionary of the class to be created. 

105 __pydantic_generic_metadata__: Metadata for generic models. 

106 __pydantic_reset_parent_namespace__: Reset parent namespace. 

107 _create_model_module: The module of the class to be created, if created by `create_model`. 

108 **kwargs: Catch-all for any other keyword arguments. 

109 

110 Returns: 

111 The new class created by the metaclass. 

112 """ 

113 # Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact 

114 # that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__` 

115 # call we're in the middle of is for the `BaseModel` class. 

116 if bases: 

117 base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases) 

118 

119 config_wrapper = ConfigWrapper.for_model(bases, namespace, kwargs) 

120 namespace_info = inspect_namespace( 

121 namespace, 

122 config_wrapper.ignored_types, 

123 base_class_fields=base_field_names, 

124 base_class_vars=class_vars, 

125 ) 

126 private_attributes = namespace_info.private_attributes 

127 # Must be done after `inspect_namespace()` is called, as it checks for the 

128 # user-assigned `model_config`: 

129 namespace['model_config'] = config_wrapper.config_dict 

130 namespace['__class_vars__'] = class_vars 

131 namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes} 

132 

133 cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs)) 

134 BaseModel_ = import_cached_base_model() 

135 

136 mro = cls.__mro__ 

137 if Generic in mro and mro.index(Generic) < mro.index(BaseModel_): 

138 warnings.warn( 

139 GenericBeforeBaseModelWarning( 

140 'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) ' 

141 'for pydantic generics to work properly.' 

142 ), 

143 stacklevel=2, 

144 ) 

145 

146 cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False) 

147 

148 cls.__pydantic_setattr_handlers__ = {} 

149 

150 cls.__pydantic_decorators__ = DecoratorInfos.build(cls, replace_wrapped_methods=True) 

151 cls.__pydantic_decorators__.update_from_config(config_wrapper) 

152 

153 # Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class 

154 if __pydantic_generic_metadata__: 

155 cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__ 

156 else: 

157 parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ()) 

158 parameters = getattr(cls, '__parameters__', None) or parent_parameters 

159 if parameters and parent_parameters and not all(x in parameters for x in parent_parameters): 

160 from ..root_model import RootModelRootType 

161 

162 missing_parameters = tuple(x for x in parameters if x not in parent_parameters) 

163 if RootModelRootType in parent_parameters and RootModelRootType not in parameters: 

164 # This is a special case where the user has subclassed RootModel, but has not parameterized 

165 # RootModel with the generic type identifiers being used. Ex: 

166 # class MyModel(RootModel, Generic[T]): 

167 # root: T 

168 # Should instead just be: 

169 # class MyModel(RootModel[T]): 

170 # root: T 

171 parameters_str = ', '.join([x.__name__ for x in missing_parameters]) 

172 error_message = ( 

173 f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) ' 

174 f'{parameters_str} in its parameters. ' 

175 f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.' 

176 ) 

177 else: 

178 combined_parameters = parent_parameters + missing_parameters 

179 parameters_str = ', '.join([str(x) for x in combined_parameters]) 

180 generic_type_label = f'typing.Generic[{parameters_str}]' 

181 error_message = ( 

182 f'All parameters must be present on typing.Generic;' 

183 f' you should inherit from {generic_type_label}.' 

184 ) 

185 if Generic not in bases: # pragma: no cover 

186 # We raise an error here not because it is desirable, but because some cases are mishandled. 

187 # It would be nice to remove this error and still have things behave as expected, it's just 

188 # challenging because we are using a custom `__class_getitem__` to parametrize generic models, 

189 # and not returning a typing._GenericAlias from it. 

190 bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label]) 

191 error_message += ( 

192 f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)' 

193 ) 

194 raise TypeError(error_message) 

195 

196 cls.__pydantic_generic_metadata__ = { 

197 'origin': None, 

198 'args': (), 

199 'parameters': parameters, 

200 } 

201 

202 cls.__pydantic_complete__ = False # Ensure this specific class gets completed 

203 

204 # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487 

205 # for attributes not in `new_namespace` (e.g. private attributes) 

206 for name, obj in private_attributes.items(): 

207 obj.__set_name__(cls, name) 

208 

209 if __pydantic_reset_parent_namespace__: 

210 cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace()) 

211 parent_namespace: dict[str, Any] | None = getattr(cls, '__pydantic_parent_namespace__', None) 

212 if isinstance(parent_namespace, dict): 

213 parent_namespace = unpack_lenient_weakvaluedict(parent_namespace) 

214 

215 ns_resolver = NsResolver(parent_namespace=parent_namespace) 

216 

217 set_model_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver, namespace_info=namespace_info) 

218 

219 # This can only be done after fields collection, as private attributes may be 

220 # discovered from the (evaluated) class annotations: 

221 if cls.__private_attributes__: 

222 original_model_post_init = get_model_post_init(namespace, bases) 

223 if original_model_post_init is not None: 

224 # if there are private attributes and a model_post_init function, we handle both 

225 

226 @wraps(original_model_post_init) 

227 def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None: 

228 """We need to both initialize private attributes and call the user-defined model_post_init 

229 method. 

230 """ 

231 init_private_attributes(self, context) 

232 original_model_post_init(self, context) 

233 

234 cls.model_post_init = wrapped_model_post_init # pyright: ignore[reportAttributeAccessIssue] 

235 else: 

236 cls.model_post_init = init_private_attributes # pyright: ignore[reportAttributeAccessIssue] 

237 

238 cls.__pydantic_post_init__ = ( 

239 None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init' 

240 ) 

241 

242 # This is also set in `complete_model_class()`, after schema gen because they are recreated. 

243 # We set them here as well for backwards compatibility: 

244 cls.__pydantic_computed_fields__ = { 

245 k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items() 

246 } 

247 

248 if config_wrapper.defer_build: 

249 set_model_mocks(cls) 

250 else: 

251 # Any operation that requires accessing the field infos instances should be put inside 

252 # `complete_model_class()`: 

253 complete_model_class( 

254 cls, 

255 config_wrapper, 

256 ns_resolver, 

257 raise_errors=False, 

258 create_model_module=_create_model_module, 

259 ) 

260 

261 if config_wrapper.frozen and '__hash__' not in namespace: 

262 set_default_hash_func(cls, bases) 

263 

264 # using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__ 

265 # I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is 

266 # only hit for _proper_ subclasses of BaseModel 

267 super(cls, cls).__pydantic_init_subclass__(**kwargs) # type: ignore[misc] 

268 return cls 

269 else: 

270 # These are instance variables, but have been assigned to `NoInitField` to trick the type checker. 

271 for instance_slot in '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__': 

272 namespace.pop( 

273 instance_slot, 

274 None, # In case the metaclass is used with a class other than `BaseModel`. 

275 ) 

276 namespace.get('__annotations__', {}).clear() 

277 return super().__new__(mcs, cls_name, bases, namespace, **kwargs) 

278 

279 if not TYPE_CHECKING: # pragma: no branch 

280 # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access 

281 

282 def __getattr__(self, item: str) -> Any: 

283 """This is necessary to keep attribute access working for class attribute access.""" 

284 private_attributes = self.__dict__.get('__private_attributes__') 

285 if private_attributes and item in private_attributes: 

286 return private_attributes[item] 

287 raise AttributeError(item) 

288 

289 @classmethod 

290 def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]: 

291 return _ModelNamespaceDict() 

292 

293 # Due to performance and memory issues, in the ABCMeta.__subclasscheck__ implementation, we don't support 

294 # registered virtual subclasses. See https://github.com/python/cpython/issues/92810#issuecomment-2762454345. 

295 # This may change once CPython is fixed (possibly in 3.15), in which case we should conditionally 

296 # define `register()`. 

297 def register(self, subclass: type[_T]) -> type[_T]: 

298 warnings.warn( 

299 f"For performance reasons, virtual subclasses registered using '{self.__qualname__}.register()' " 

300 "are not supported in 'isinstance()' and 'issubclass()' checks.", 

301 stacklevel=2, 

302 ) 

303 return super().register(subclass) 

304 

305 __instancecheck__ = type.__instancecheck__ # pyright: ignore[reportAssignmentType] 

306 __subclasscheck__ = type.__subclasscheck__ # pyright: ignore[reportAssignmentType] 

307 

308 @staticmethod 

309 def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]: 

310 BaseModel = import_cached_base_model() 

311 

312 field_names: set[str] = set() 

313 class_vars: set[str] = set() 

314 private_attributes: dict[str, ModelPrivateAttr] = {} 

315 for base in bases: 

316 if issubclass(base, BaseModel) and base is not BaseModel: 

317 # model_fields might not be defined yet in the case of generics, so we use getattr here: 

318 field_names.update(getattr(base, '__pydantic_fields__', {}).keys()) 

319 class_vars.update(base.__class_vars__) 

320 private_attributes.update(base.__private_attributes__) 

321 return field_names, class_vars, private_attributes 

322 

323 @property 

324 @deprecated( 

325 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None 

326 ) 

327 def __fields__(self) -> dict[str, FieldInfo]: 

328 warnings.warn( 

329 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', 

330 PydanticDeprecatedSince20, 

331 stacklevel=2, 

332 ) 

333 return getattr(self, '__pydantic_fields__', {}) 

334 

335 @property 

336 def __pydantic_fields_complete__(self) -> bool: 

337 """Whether the fields were successfully collected (i.e. type hints were successfully resolved). 

338 

339 This is a private attribute, not meant to be used outside Pydantic. 

340 """ 

341 if '__pydantic_fields__' not in self.__dict__: 

342 return False 

343 

344 field_infos = cast('dict[str, FieldInfo]', self.__pydantic_fields__) # pyright: ignore[reportAttributeAccessIssue] 

345 

346 pydantic_extra_info = cast('PydanticExtraInfo | None', self.__pydantic_extra_info__) # pyright: ignore[reportAttributeAccessIssue] 

347 if pydantic_extra_info is not None: 

348 extra_complete = pydantic_extra_info.complete 

349 else: 

350 extra_complete = True 

351 

352 return all(field_info._complete for field_info in field_infos.values()) and extra_complete 

353 

354 def __dir__(self) -> list[str]: 

355 attributes = list(super().__dir__()) 

356 if '__fields__' in attributes: 

357 attributes.remove('__fields__') 

358 return attributes 

359 

360 

361def init_private_attributes(self: BaseModel, context: Any, /) -> None: 

362 """This function is meant to behave like a BaseModel method to initialize private attributes. 

363 

364 It takes context as an argument since that's what pydantic-core passes when calling it. 

365 

366 Args: 

367 self: The BaseModel instance. 

368 context: The context. 

369 """ 

370 if getattr(self, '__pydantic_private__', None) is None: 

371 pydantic_private = {} 

372 for name, private_attr in self.__private_attributes__.items(): 

373 # Avoid needlessly creating a new dict for the validated data: 

374 if private_attr.default_factory_takes_validated_data: 

375 default = private_attr.get_default( 

376 call_default_factory=True, validated_data={**self.__dict__, **pydantic_private} 

377 ) 

378 else: 

379 default = private_attr.get_default(call_default_factory=True) 

380 if default is not PydanticUndefined: 

381 pydantic_private[name] = default 

382 object_setattr(self, '__pydantic_private__', pydantic_private) 

383 

384 

385def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None: 

386 """Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined.""" 

387 if 'model_post_init' in namespace: 

388 return namespace['model_post_init'] 

389 

390 BaseModel = import_cached_base_model() 

391 

392 model_post_init = get_attribute_from_bases(bases, 'model_post_init') 

393 if model_post_init is not BaseModel.model_post_init: 

394 return model_post_init 

395 

396 

397def inspect_namespace( 

398 namespace: dict[str, Any], 

399 ignored_types: tuple[type[Any], ...], 

400 *, 

401 base_class_vars: set[str], 

402 base_class_fields: set[str], 

403) -> ModelNamespaceInfo: 

404 """Inspect the namespace prior to class creation. 

405 

406 Most of the collected information is then used by `collect_model_fields()` to perform checks. 

407 

408 Args: 

409 namespace: The attribute dictionary of the class to be created. 

410 ignored_types: Tuple of ignore types. 

411 base_class_vars: The set of base class variables. 

412 base_class_fields: The set of base class fields. 

413 

414 Returns: 

415 A `ModelNamespaceInfo` instance. 

416 

417 Raises: 

418 PydanticUserError: If there is a `__root__` field in model, or if a private attribute name is invalid. 

419 """ 

420 from ..fields import ModelPrivateAttr 

421 

422 FieldInfo = import_cached_field_info() 

423 

424 all_ignored_types = ignored_types + default_ignored_types() 

425 

426 private_attributes: dict[str, ModelPrivateAttr] = {} 

427 ignored_names: set[str] = set() 

428 private_candidates: list[str] = [] 

429 field_candidates: list[str] = [] 

430 

431 if '__root__' in namespace: 

432 # This only checks for assignment, `collect_model_fields()` will raise if only an annotated `__root__` is defined: 

433 raise PydanticUserError( 

434 "To define root models, use `pydantic.RootModel` rather than a field called '__root__'", code=None 

435 ) 

436 

437 for var_name, value in namespace.items(): 

438 if var_name == 'model_config' or var_name == '__pydantic_extra__': 

439 continue 

440 elif ( 

441 isinstance(value, type) 

442 and value.__module__ == namespace['__module__'] 

443 and '__qualname__' in namespace 

444 and value.__qualname__.startswith(f'{namespace["__qualname__"]}.') 

445 ): 

446 # `value` is a nested type defined in this namespace; don't error 

447 continue 

448 elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools': 

449 ignored_names.add(var_name) 

450 continue 

451 elif isinstance(value, ModelPrivateAttr): 

452 if var_name.startswith('__'): 

453 raise PydanticUserError( 

454 'Private attributes must not use dunder names;' 

455 f' use a single underscore prefix instead of {var_name!r}.', 

456 code=None, 

457 ) 

458 elif is_valid_field_name(var_name): 

459 raise PydanticUserError( 

460 'Private attributes must not use valid field names;' 

461 f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.', 

462 code=None, 

463 ) 

464 private_attributes[var_name] = value 

465 elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name): 

466 suggested_name = var_name.lstrip('_') or 'my_field' # don't suggest '' for all-underscore name 

467 raise PydanticUserError( 

468 f'Fields must not use names with leading underscores;' 

469 f' e.g., use {suggested_name!r} instead of {var_name!r}.', 

470 code=None, 

471 ) 

472 elif var_name.startswith('__'): 

473 continue 

474 elif is_valid_privateattr_name(var_name): 

475 # A private attribute, unless annotated as a class variable (only known 

476 # once annotations are evaluated during fields collection): 

477 private_candidates.append(var_name) 

478 elif var_name in base_class_vars: 

479 continue 

480 else: 

481 # A field default or an invalid non-annotated attribute (only known 

482 # once annotations are evaluated during fields collection): 

483 field_candidates.append(var_name) 

484 

485 for var_name in private_attributes: 

486 del namespace[var_name] 

487 

488 return ModelNamespaceInfo( 

489 private_attributes=private_attributes, 

490 ignored_names=ignored_names, 

491 ignored_types=all_ignored_types, 

492 private_candidates=private_candidates, 

493 field_candidates=field_candidates, 

494 base_field_names=base_class_fields, 

495 model_config_assigned=namespace.get('model_config') is not None, 

496 ) 

497 

498 

499def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None: 

500 base_hash_func = get_attribute_from_bases(bases, '__hash__') 

501 new_hash_func = make_hash_func(cls) 

502 if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__: 

503 # If `__hash__` is some default, we generate a hash function. 

504 # It will be `None` if not overridden from BaseModel. 

505 # It may be `object.__hash__` if there is another 

506 # parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`). 

507 # It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model. 

508 # In the last case we still need a new hash function to account for new `model_fields`. 

509 cls.__hash__ = new_hash_func 

510 

511 

512def make_hash_func(cls: type[BaseModel]) -> Any: 

513 getter = operator.itemgetter(*cls.__pydantic_fields__.keys()) if cls.__pydantic_fields__ else lambda _: 0 

514 

515 def hash_func(self: Any) -> int: 

516 try: 

517 return hash(getter(self.__dict__)) 

518 except KeyError: 

519 # In rare cases (such as when using the deprecated copy method), the __dict__ may not contain 

520 # all model fields, which is how we can get here. 

521 # getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys, 

522 # and wrapping it in a `try` doesn't slow things down much in the common case. 

523 return hash(getter(SafeGetItemProxy(self.__dict__))) 

524 

525 return hash_func 

526 

527 

528def set_model_fields( 

529 cls: type[BaseModel], 

530 config_wrapper: ConfigWrapper, 

531 ns_resolver: NsResolver, 

532 namespace_info: ModelNamespaceInfo | None = None, 

533) -> None: 

534 """Collect and set `cls.__pydantic_fields__`, `cls.__class_vars__` and `cls.__private_attributes__`. 

535 

536 Args: 

537 cls: BaseModel or dataclass. 

538 config_wrapper: The config wrapper instance. 

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

540 namespace_info: The result of inspecting the class namespace before the class was created. 

541 """ 

542 typevars_map = get_model_typevars_map(cls) 

543 fields, pydantic_extra_info, class_vars, private_attributes = collect_model_fields( 

544 cls, config_wrapper, ns_resolver, typevars_map=typevars_map, namespace_info=namespace_info 

545 ) 

546 

547 cls.__pydantic_fields__ = fields 

548 cls.__pydantic_extra_info__ = pydantic_extra_info 

549 cls.__class_vars__.update(class_vars) 

550 cls.__private_attributes__.update(private_attributes) 

551 

552 for k in class_vars: 

553 # Class vars should not be private attributes (this can happen when a class variable 

554 # of a base class shadows a private attribute, e.g. when annotated as a `ClassVar` 

555 # in a subclass): 

556 value = cls.__private_attributes__.pop(k, None) 

557 if value is not None and value.default is not PydanticUndefined: 

558 setattr(cls, k, value.default) 

559 

560 

561def complete_model_class( 

562 cls: type[BaseModel], 

563 config_wrapper: ConfigWrapper, 

564 ns_resolver: NsResolver, 

565 *, 

566 raise_errors: bool = True, 

567 call_on_complete_hook: bool = True, 

568 create_model_module: str | None = None, 

569 is_force_rebuild: bool = False, 

570) -> bool: 

571 """Finish building a model class. 

572 

573 This logic must be called after class has been created since validation functions must be bound 

574 and `get_type_hints` requires a class object. 

575 

576 Args: 

577 cls: BaseModel or dataclass. 

578 config_wrapper: The config wrapper instance. 

579 ns_resolver: The namespace resolver instance to use during schema building. 

580 raise_errors: Whether to raise errors. 

581 call_on_complete_hook: Whether to call the `__pydantic_on_complete__` hook. 

582 create_model_module: The module of the class to be created, if created by `create_model`. 

583 is_force_rebuild: Whether the model is being force-rebuilt (if True, pre-built serializers and 

584 validators are not used, to avoid stale references). 

585 

586 Returns: 

587 `True` if the model is successfully completed, else `False`. 

588 

589 Raises: 

590 PydanticUndefinedAnnotation: If `PydanticUndefinedAnnotation` occurs during core schema generation 

591 and `raise_errors` is True. 

592 """ 

593 typevars_map = get_model_typevars_map(cls) 

594 

595 if not cls.__pydantic_fields_complete__: 

596 # Note: when coming from `ModelMetaclass.__new__()`, this results in fields being built twice. 

597 # We do so a second time here so that we can get the ``NameError`` for the specific undefined annotation. 

598 # Alternatively, we could let `GenerateSchema()` raise the error, but there are cases where incomplete 

599 # fields are inherited in `collect_model_fields()` and can actually have their annotation resolved in the 

600 # generate schema process. As we want to avoid having `__pydantic_fields_complete__` set to `False` 

601 # when `__pydantic_complete__` is `True`, we rebuild here: 

602 try: 

603 cls.__pydantic_fields__, cls.__pydantic_extra_info__ = rebuild_model_fields( 

604 cls, 

605 config_wrapper=config_wrapper, 

606 ns_resolver=ns_resolver, 

607 typevars_map=typevars_map, 

608 ) 

609 except NameError as e: 

610 exc = PydanticUndefinedAnnotation.from_name_error(e) 

611 set_model_mocks(cls, f'`{exc.name}`' if exc.name is not None else None) 

612 if raise_errors: 

613 raise exc from e 

614 

615 if not raise_errors and not cls.__pydantic_fields_complete__: 

616 # No need to continue with schema gen, it is guaranteed to fail 

617 return False 

618 

619 assert cls.__pydantic_fields_complete__ 

620 

621 gen_schema = GenerateSchema( 

622 config_wrapper, 

623 ns_resolver, 

624 typevars_map, 

625 ) 

626 

627 try: 

628 schema = gen_schema.generate_schema(cls) 

629 except PydanticUndefinedAnnotation as e: 

630 if raise_errors: 

631 raise 

632 set_model_mocks(cls, f'`{e.name}`' if e.name is not None else None) 

633 return False 

634 

635 core_config = config_wrapper.core_config(title=cls.__name__) 

636 

637 try: 

638 schema = gen_schema.clean_schema(schema) 

639 except InvalidSchemaError: 

640 set_model_mocks(cls) 

641 return False 

642 

643 # This needs to happen *after* model schema generation, as the return types 

644 # of the properties are evaluated and the `ComputedFieldInfo` are recreated: 

645 cls.__pydantic_computed_fields__ = {k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()} 

646 

647 set_deprecated_descriptors(cls) 

648 

649 cls.__pydantic_core_schema__ = schema 

650 

651 cls.__pydantic_validator__ = create_schema_validator( 

652 schema, 

653 cls, 

654 create_model_module or cls.__module__, 

655 cls.__qualname__, 

656 'create_model' if create_model_module else 'BaseModel', 

657 core_config, 

658 config_wrapper.plugin_settings, 

659 _use_prebuilt=not is_force_rebuild, 

660 ) 

661 cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config, _use_prebuilt=not is_force_rebuild) 

662 

663 # set __signature__ attr only for model class, but not for its instances 

664 # (because instances can define `__call__`, and `inspect.signature` shouldn't 

665 # use the `__signature__` attribute and instead generate from `__call__`). 

666 cls.__signature__ = LazyClassAttribute( 

667 '__signature__', 

668 partial( 

669 generate_pydantic_signature, 

670 init=cls.__init__, 

671 fields=cls.__pydantic_fields__, 

672 validate_by_alias=config_wrapper.validate_by_alias, 

673 extra=config_wrapper.extra, 

674 ), 

675 ) 

676 

677 cls.__pydantic_complete__ = True 

678 

679 if call_on_complete_hook: 

680 cls.__pydantic_on_complete__() 

681 

682 return True 

683 

684 

685def set_deprecated_descriptors(cls: type[BaseModel]) -> None: 

686 """Set data descriptors on the class for deprecated fields.""" 

687 for field, field_info in cls.__pydantic_fields__.items(): 

688 if (msg := field_info.deprecation_message) is not None: 

689 desc = _DeprecatedFieldDescriptor(msg) 

690 desc.__set_name__(cls, field) 

691 setattr(cls, field, desc) 

692 

693 for field, computed_field_info in cls.__pydantic_computed_fields__.items(): 

694 if ( 

695 (msg := computed_field_info.deprecation_message) is not None 

696 # If the property was already decorated with `@deprecated`, we avoid emitting the warning message 

697 # from `@computed_field` and give priority to the other. Ideally, the deprecation message from 

698 # `@computed_field` should take priority, but we can't safely unwrap (using `inspect.unwrap()`) 

699 # to get the inner property decorated by `@deprecated`, as we might skip other user-defined decorators 

700 # while doing so: 

701 and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__') 

702 ): 

703 desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property) 

704 desc.__set_name__(cls, field) 

705 setattr(cls, field, desc) 

706 

707 

708class _DeprecatedFieldDescriptor: 

709 """Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field. 

710 

711 Attributes: 

712 msg: The deprecation message to be emitted. 

713 wrapped_property: The property instance if the deprecated field is a computed field, or `None`. 

714 field_name: The name of the field being deprecated. 

715 """ 

716 

717 field_name: str 

718 

719 def __init__(self, msg: str, wrapped_property: property | None = None) -> None: 

720 self.msg = msg 

721 self.wrapped_property = wrapped_property 

722 

723 def __set_name__(self, cls: type[BaseModel], name: str) -> None: 

724 self.field_name = name 

725 

726 def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any: 

727 if obj is None: 

728 if self.wrapped_property is not None: 

729 return self.wrapped_property.__get__(None, obj_type) 

730 raise AttributeError(self.field_name) 

731 

732 warnings.warn(self.msg, DeprecationWarning, stacklevel=2) 

733 

734 if self.wrapped_property is not None: 

735 return self.wrapped_property.__get__(obj, obj_type) 

736 return obj.__dict__[self.field_name] 

737 

738 # Defined to make it a data descriptor and take precedence over the instance's dictionary. 

739 # Note that it will not be called when setting a value on a model instance 

740 # as `BaseModel.__setattr__` is defined and takes priority. 

741 def __set__(self, obj: Any, value: Any) -> NoReturn: 

742 raise AttributeError(self.field_name) 

743 

744 

745class _PydanticWeakRef: 

746 """Wrapper for `weakref.ref` that enables `pickle` serialization. 

747 

748 Cloudpickle fails to serialize weakref.ref objects due to an arcane error related to 

749 to abstract base classes (`abc.ABC`). This class works around the issue by wrapping 

750 `weakref.ref` instead of subclassing it. 

751 

752 See https://github.com/pydantic/pydantic/issues/6763 for context. 

753 

754 Semantics: 

755 - If not pickled, behaves the same as a `weakref.ref`. 

756 - If pickled along with the referenced object, the same `weakref.ref` behavior 

757 will be maintained between them after unpickling. 

758 - If pickled without the referenced object, after unpickling the underlying 

759 reference will be cleared (`__call__` will always return `None`). 

760 """ 

761 

762 def __init__(self, obj: Any): 

763 if obj is None: 

764 # The object will be `None` upon deserialization if the serialized weakref 

765 # had lost its underlying object. 

766 self._wr = None 

767 else: 

768 self._wr = weakref.ref(obj) 

769 

770 def __call__(self) -> Any: 

771 if self._wr is None: 

772 return None 

773 else: 

774 return self._wr() 

775 

776 def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]: 

777 return _PydanticWeakRef, (self(),) 

778 

779 

780def build_lenient_weakvaluedict(d: MutableMapping[str, Any] | None) -> dict[str, Any] | None: 

781 """Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs. 

782 

783 We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values 

784 in a WeakValueDictionary. 

785 

786 The `unpack_lenient_weakvaluedict` function can be used to reverse this operation. 

787 """ 

788 if d is None: 

789 return None 

790 result = {} 

791 for k, v in d.items(): 

792 try: 

793 proxy = _PydanticWeakRef(v) 

794 except TypeError: 

795 proxy = v 

796 result[k] = proxy 

797 return result 

798 

799 

800def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None: 

801 """Inverts the transform performed by `build_lenient_weakvaluedict`.""" 

802 if d is None: 

803 return None 

804 

805 result = {} 

806 for k, v in d.items(): 

807 if isinstance(v, _PydanticWeakRef): 

808 v = v() 

809 if v is not None: 

810 result[k] = v 

811 else: 

812 result[k] = v 

813 return result 

814 

815 

816@cache 

817def default_ignored_types() -> tuple[type[Any], ...]: 

818 from ..fields import ComputedFieldInfo 

819 

820 ignored_types = [ 

821 FunctionType, 

822 property, 

823 classmethod, 

824 staticmethod, 

825 PydanticDescriptorProxy, 

826 ComputedFieldInfo, 

827 TypeAliasType, # from `typing_extensions` 

828 ] 

829 

830 if sys.version_info >= (3, 12): 

831 ignored_types.append(typing.TypeAliasType) 

832 

833 return tuple(ignored_types)