copy_internals.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. from __future__ import annotations as _annotations
  2. import typing
  3. from copy import deepcopy
  4. from enum import Enum
  5. from typing import Any
  6. import typing_extensions
  7. from .._internal import (
  8. _model_construction,
  9. _typing_extra,
  10. _utils,
  11. )
  12. if typing.TYPE_CHECKING:
  13. from .. import BaseModel
  14. from .._internal._utils import AbstractSetIntStr, MappingIntStrAny
  15. AnyClassMethod = classmethod[Any, Any, Any]
  16. TupleGenerator = typing.Generator[tuple[str, Any], None, None]
  17. Model = typing.TypeVar('Model', bound='BaseModel')
  18. # should be `set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None`, but mypy can't cope
  19. IncEx: typing_extensions.TypeAlias = 'set[int] | set[str] | dict[int, Any] | dict[str, Any] | None'
  20. _object_setattr = _model_construction.object_setattr
  21. def _iter(
  22. self: BaseModel,
  23. to_dict: bool = False,
  24. by_alias: bool = False,
  25. include: AbstractSetIntStr | MappingIntStrAny | None = None,
  26. exclude: AbstractSetIntStr | MappingIntStrAny | None = None,
  27. exclude_unset: bool = False,
  28. exclude_defaults: bool = False,
  29. exclude_none: bool = False,
  30. ) -> TupleGenerator:
  31. # Merge field set excludes with explicit exclude parameter with explicit overriding field set options.
  32. # The extra "is not None" guards are not logically necessary but optimizes performance for the simple case.
  33. if exclude is not None:
  34. exclude = _utils.ValueItems.merge(
  35. {k: v.exclude for k, v in self.__pydantic_fields__.items() if v.exclude is not None}, exclude
  36. )
  37. if include is not None:
  38. include = _utils.ValueItems.merge(dict.fromkeys(self.__pydantic_fields__, True), include, intersect=True)
  39. allowed_keys = _calculate_keys(self, include=include, exclude=exclude, exclude_unset=exclude_unset) # type: ignore
  40. if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none):
  41. # huge boost for plain _iter()
  42. yield from self.__dict__.items()
  43. if self.__pydantic_extra__:
  44. yield from self.__pydantic_extra__.items()
  45. return
  46. value_exclude = _utils.ValueItems(self, exclude) if exclude is not None else None
  47. value_include = _utils.ValueItems(self, include) if include is not None else None
  48. if self.__pydantic_extra__ is None:
  49. items = self.__dict__.items()
  50. else:
  51. items = list(self.__dict__.items()) + list(self.__pydantic_extra__.items())
  52. for field_key, v in items:
  53. if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None):
  54. continue
  55. if exclude_defaults:
  56. try:
  57. field = self.__pydantic_fields__[field_key]
  58. except KeyError:
  59. pass
  60. else:
  61. if not field.is_required() and field.default == v:
  62. continue
  63. if by_alias and field_key in self.__pydantic_fields__:
  64. dict_key = self.__pydantic_fields__[field_key].alias or field_key
  65. else:
  66. dict_key = field_key
  67. if to_dict or value_include or value_exclude:
  68. v = _get_value(
  69. type(self),
  70. v,
  71. to_dict=to_dict,
  72. by_alias=by_alias,
  73. include=value_include and value_include.for_element(field_key),
  74. exclude=value_exclude and value_exclude.for_element(field_key),
  75. exclude_unset=exclude_unset,
  76. exclude_defaults=exclude_defaults,
  77. exclude_none=exclude_none,
  78. )
  79. yield dict_key, v
  80. def _copy_and_set_values(
  81. self: Model,
  82. values: dict[str, Any],
  83. fields_set: set[str],
  84. extra: dict[str, Any] | None = None,
  85. private: dict[str, Any] | None = None,
  86. *,
  87. deep: bool, # UP006
  88. ) -> Model:
  89. if deep:
  90. # chances of having empty dict here are quite low for using smart_deepcopy
  91. values = deepcopy(values)
  92. extra = deepcopy(extra)
  93. private = deepcopy(private)
  94. cls = self.__class__
  95. m = cls.__new__(cls)
  96. _object_setattr(m, '__dict__', values)
  97. _object_setattr(m, '__pydantic_extra__', extra)
  98. _object_setattr(m, '__pydantic_fields_set__', fields_set)
  99. _object_setattr(m, '__pydantic_private__', private)
  100. return m
  101. @typing.no_type_check
  102. def _get_value(
  103. cls: type[BaseModel],
  104. v: Any,
  105. to_dict: bool,
  106. by_alias: bool,
  107. include: AbstractSetIntStr | MappingIntStrAny | None,
  108. exclude: AbstractSetIntStr | MappingIntStrAny | None,
  109. exclude_unset: bool,
  110. exclude_defaults: bool,
  111. exclude_none: bool,
  112. ) -> Any:
  113. from .. import BaseModel
  114. if isinstance(v, BaseModel):
  115. if to_dict:
  116. return v.model_dump(
  117. by_alias=by_alias,
  118. exclude_unset=exclude_unset,
  119. exclude_defaults=exclude_defaults,
  120. include=include, # type: ignore
  121. exclude=exclude, # type: ignore
  122. exclude_none=exclude_none,
  123. )
  124. else:
  125. return v.copy(include=include, exclude=exclude)
  126. value_exclude = _utils.ValueItems(v, exclude) if exclude else None
  127. value_include = _utils.ValueItems(v, include) if include else None
  128. if isinstance(v, dict):
  129. return {
  130. k_: _get_value(
  131. cls,
  132. v_,
  133. to_dict=to_dict,
  134. by_alias=by_alias,
  135. exclude_unset=exclude_unset,
  136. exclude_defaults=exclude_defaults,
  137. include=value_include and value_include.for_element(k_),
  138. exclude=value_exclude and value_exclude.for_element(k_),
  139. exclude_none=exclude_none,
  140. )
  141. for k_, v_ in v.items()
  142. if (not value_exclude or not value_exclude.is_excluded(k_))
  143. and (not value_include or value_include.is_included(k_))
  144. }
  145. elif _utils.sequence_like(v):
  146. seq_args = (
  147. _get_value(
  148. cls,
  149. v_,
  150. to_dict=to_dict,
  151. by_alias=by_alias,
  152. exclude_unset=exclude_unset,
  153. exclude_defaults=exclude_defaults,
  154. include=value_include and value_include.for_element(i),
  155. exclude=value_exclude and value_exclude.for_element(i),
  156. exclude_none=exclude_none,
  157. )
  158. for i, v_ in enumerate(v)
  159. if (not value_exclude or not value_exclude.is_excluded(i))
  160. and (not value_include or value_include.is_included(i))
  161. )
  162. return v.__class__(*seq_args) if _typing_extra.is_namedtuple(v.__class__) else v.__class__(seq_args)
  163. elif isinstance(v, Enum) and getattr(cls.model_config, 'use_enum_values', False):
  164. return v.value
  165. else:
  166. return v
  167. def _calculate_keys(
  168. self: BaseModel,
  169. include: MappingIntStrAny | None,
  170. exclude: MappingIntStrAny | None,
  171. exclude_unset: bool,
  172. update: dict[str, Any] | None = None, # noqa UP006
  173. ) -> typing.AbstractSet[str] | None:
  174. if include is None and exclude is None and exclude_unset is False:
  175. return None
  176. keys: typing.AbstractSet[str]
  177. if exclude_unset:
  178. keys = self.__pydantic_fields_set__.copy()
  179. else:
  180. keys = set(self.__dict__.keys())
  181. keys = keys | (self.__pydantic_extra__ or {}).keys()
  182. if include is not None:
  183. keys &= include.keys()
  184. if update:
  185. keys -= update.keys()
  186. if exclude:
  187. keys -= {k for k, v in exclude.items() if _utils.ValueItems.is_true(v)}
  188. return keys