_fields.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. """Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
  2. from __future__ import annotations as _annotations
  3. import dataclasses
  4. import sys
  5. import warnings
  6. from copy import copy
  7. from functools import lru_cache
  8. from typing import TYPE_CHECKING, Any
  9. from pydantic_core import PydanticUndefined
  10. from . import _typing_extra
  11. from ._config import ConfigWrapper
  12. from ._repr import Representation
  13. from ._typing_extra import get_cls_type_hints_lenient, get_type_hints, is_classvar, is_finalvar
  14. if TYPE_CHECKING:
  15. from annotated_types import BaseMetadata
  16. from ..fields import FieldInfo
  17. from ..main import BaseModel
  18. from ._dataclasses import StandardDataclass
  19. from ._decorators import DecoratorInfos
  20. def get_type_hints_infer_globalns(
  21. obj: Any,
  22. localns: dict[str, Any] | None = None,
  23. include_extras: bool = False,
  24. ) -> dict[str, Any]:
  25. """Gets type hints for an object by inferring the global namespace.
  26. It uses the `typing.get_type_hints`, The only thing that we do here is fetching
  27. global namespace from `obj.__module__` if it is not `None`.
  28. Args:
  29. obj: The object to get its type hints.
  30. localns: The local namespaces.
  31. include_extras: Whether to recursively include annotation metadata.
  32. Returns:
  33. The object type hints.
  34. """
  35. module_name = getattr(obj, '__module__', None)
  36. globalns: dict[str, Any] | None = None
  37. if module_name:
  38. try:
  39. globalns = sys.modules[module_name].__dict__
  40. except KeyError:
  41. # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363
  42. pass
  43. return get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras)
  44. class PydanticMetadata(Representation):
  45. """Base class for annotation markers like `Strict`."""
  46. __slots__ = ()
  47. def pydantic_general_metadata(**metadata: Any) -> BaseMetadata:
  48. """Create a new `_PydanticGeneralMetadata` class with the given metadata.
  49. Args:
  50. **metadata: The metadata to add.
  51. Returns:
  52. The new `_PydanticGeneralMetadata` class.
  53. """
  54. return _general_metadata_cls()(metadata) # type: ignore
  55. @lru_cache(maxsize=None)
  56. def _general_metadata_cls() -> type[BaseMetadata]:
  57. """Do it this way to avoid importing `annotated_types` at import time."""
  58. from annotated_types import BaseMetadata
  59. class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata):
  60. """Pydantic general metadata like `max_digits`."""
  61. def __init__(self, metadata: Any):
  62. self.__dict__ = metadata
  63. return _PydanticGeneralMetadata # type: ignore
  64. def collect_model_fields( # noqa: C901
  65. cls: type[BaseModel],
  66. bases: tuple[type[Any], ...],
  67. config_wrapper: ConfigWrapper,
  68. types_namespace: dict[str, Any] | None,
  69. *,
  70. typevars_map: dict[Any, Any] | None = None,
  71. ) -> tuple[dict[str, FieldInfo], set[str]]:
  72. """Collect the fields of a nascent pydantic model.
  73. Also collect the names of any ClassVars present in the type hints.
  74. The returned value is a tuple of two items: the fields dict, and the set of ClassVar names.
  75. Args:
  76. cls: BaseModel or dataclass.
  77. bases: Parents of the class, generally `cls.__bases__`.
  78. config_wrapper: The config wrapper instance.
  79. types_namespace: Optional extra namespace to look for types in.
  80. typevars_map: A dictionary mapping type variables to their concrete types.
  81. Returns:
  82. A tuple contains fields and class variables.
  83. Raises:
  84. NameError:
  85. - If there is a conflict between a field name and protected namespaces.
  86. - If there is a field other than `root` in `RootModel`.
  87. - If a field shadows an attribute in the parent model.
  88. """
  89. from ..fields import FieldInfo
  90. type_hints = get_cls_type_hints_lenient(cls, types_namespace)
  91. # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
  92. # annotations is only used for finding fields in parent classes
  93. annotations = cls.__dict__.get('__annotations__', {})
  94. fields: dict[str, FieldInfo] = {}
  95. class_vars: set[str] = set()
  96. for ann_name, ann_type in type_hints.items():
  97. if ann_name == 'model_config':
  98. # We never want to treat `model_config` as a field
  99. # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
  100. # protected namespaces (where `model_config` might be allowed as a field name)
  101. continue
  102. for protected_namespace in config_wrapper.protected_namespaces:
  103. if ann_name.startswith(protected_namespace):
  104. for b in bases:
  105. if hasattr(b, ann_name):
  106. from ..main import BaseModel
  107. if not (issubclass(b, BaseModel) and ann_name in b.model_fields):
  108. raise NameError(
  109. f'Field "{ann_name}" conflicts with member {getattr(b, ann_name)}'
  110. f' of protected namespace "{protected_namespace}".'
  111. )
  112. else:
  113. valid_namespaces = tuple(
  114. x for x in config_wrapper.protected_namespaces if not ann_name.startswith(x)
  115. )
  116. warnings.warn(
  117. f'Field "{ann_name}" has conflict with protected namespace "{protected_namespace}".'
  118. '\n\nYou may be able to resolve this warning by setting'
  119. f" `model_config['protected_namespaces'] = {valid_namespaces}`.",
  120. UserWarning,
  121. )
  122. if is_classvar(ann_type):
  123. class_vars.add(ann_name)
  124. continue
  125. if _is_finalvar_with_default_val(ann_type, getattr(cls, ann_name, PydanticUndefined)):
  126. class_vars.add(ann_name)
  127. continue
  128. if not is_valid_field_name(ann_name):
  129. continue
  130. if cls.__pydantic_root_model__ and ann_name != 'root':
  131. raise NameError(
  132. f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`"
  133. )
  134. # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
  135. # "... shadows an attribute" errors
  136. generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
  137. for base in bases:
  138. dataclass_fields = {
  139. field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
  140. }
  141. if hasattr(base, ann_name):
  142. if base is generic_origin:
  143. # Don't error when "shadowing" of attributes in parametrized generics
  144. continue
  145. if ann_name in dataclass_fields:
  146. # Don't error when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
  147. # on the class instance.
  148. continue
  149. warnings.warn(
  150. f'Field name "{ann_name}" shadows an attribute in parent "{base.__qualname__}"; ',
  151. UserWarning,
  152. )
  153. try:
  154. default = getattr(cls, ann_name, PydanticUndefined)
  155. if default is PydanticUndefined:
  156. raise AttributeError
  157. except AttributeError:
  158. if ann_name in annotations:
  159. field_info = FieldInfo.from_annotation(ann_type)
  160. else:
  161. # if field has no default value and is not in __annotations__ this means that it is
  162. # defined in a base class and we can take it from there
  163. model_fields_lookup: dict[str, FieldInfo] = {}
  164. for x in cls.__bases__[::-1]:
  165. model_fields_lookup.update(getattr(x, 'model_fields', {}))
  166. if ann_name in model_fields_lookup:
  167. # The field was present on one of the (possibly multiple) base classes
  168. # copy the field to make sure typevar substitutions don't cause issues with the base classes
  169. field_info = copy(model_fields_lookup[ann_name])
  170. else:
  171. # The field was not found on any base classes; this seems to be caused by fields not getting
  172. # generated thanks to models not being fully defined while initializing recursive models.
  173. # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this.
  174. field_info = FieldInfo.from_annotation(ann_type)
  175. else:
  176. field_info = FieldInfo.from_annotated_attribute(ann_type, default)
  177. # attributes which are fields are removed from the class namespace:
  178. # 1. To match the behaviour of annotation-only fields
  179. # 2. To avoid false positives in the NameError check above
  180. try:
  181. delattr(cls, ann_name)
  182. except AttributeError:
  183. pass # indicates the attribute was on a parent class
  184. # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
  185. # to make sure the decorators have already been built for this exact class
  186. decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__']
  187. if ann_name in decorators.computed_fields:
  188. raise ValueError("you can't override a field with a computed field")
  189. fields[ann_name] = field_info
  190. if typevars_map:
  191. for field in fields.values():
  192. field.apply_typevars_map(typevars_map, types_namespace)
  193. return fields, class_vars
  194. def _is_finalvar_with_default_val(type_: type[Any], val: Any) -> bool:
  195. from ..fields import FieldInfo
  196. if not is_finalvar(type_):
  197. return False
  198. elif val is PydanticUndefined:
  199. return False
  200. elif isinstance(val, FieldInfo) and (val.default is PydanticUndefined and val.default_factory is None):
  201. return False
  202. else:
  203. return True
  204. def collect_dataclass_fields(
  205. cls: type[StandardDataclass], types_namespace: dict[str, Any] | None, *, typevars_map: dict[Any, Any] | None = None
  206. ) -> dict[str, FieldInfo]:
  207. """Collect the fields of a dataclass.
  208. Args:
  209. cls: dataclass.
  210. types_namespace: Optional extra namespace to look for types in.
  211. typevars_map: A dictionary mapping type variables to their concrete types.
  212. Returns:
  213. The dataclass fields.
  214. """
  215. from ..fields import FieldInfo
  216. fields: dict[str, FieldInfo] = {}
  217. dataclass_fields: dict[str, dataclasses.Field] = cls.__dataclass_fields__
  218. cls_localns = dict(vars(cls)) # this matches get_cls_type_hints_lenient, but all tests pass with `= None` instead
  219. for ann_name, dataclass_field in dataclass_fields.items():
  220. ann_type = _typing_extra.eval_type_lenient(dataclass_field.type, types_namespace, cls_localns)
  221. if is_classvar(ann_type):
  222. continue
  223. if (
  224. not dataclass_field.init
  225. and dataclass_field.default == dataclasses.MISSING
  226. and dataclass_field.default_factory == dataclasses.MISSING
  227. ):
  228. # TODO: We should probably do something with this so that validate_assignment behaves properly
  229. # Issue: https://github.com/pydantic/pydantic/issues/5470
  230. continue
  231. if isinstance(dataclass_field.default, FieldInfo):
  232. if dataclass_field.default.init_var:
  233. # TODO: same note as above
  234. continue
  235. field_info = FieldInfo.from_annotated_attribute(ann_type, dataclass_field.default)
  236. else:
  237. field_info = FieldInfo.from_annotated_attribute(ann_type, dataclass_field)
  238. fields[ann_name] = field_info
  239. if field_info.default is not PydanticUndefined and isinstance(getattr(cls, ann_name, field_info), FieldInfo):
  240. # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
  241. setattr(cls, ann_name, field_info.default)
  242. if typevars_map:
  243. for field in fields.values():
  244. field.apply_typevars_map(typevars_map, types_namespace)
  245. return fields
  246. def is_valid_field_name(name: str) -> bool:
  247. return not name.startswith('_')
  248. def is_valid_privateattr_name(name: str) -> bool:
  249. return name.startswith('_') and not name.startswith('__')