Coverage for pydantic/_internal/_dataclasses.py: 95.59%

62 statements  

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

1"""Private logic for creating pydantic dataclasses.""" 

2 

3from __future__ import annotations as _annotations 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

4 

5import typing 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

6import warnings 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

7from functools import partial 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

8from typing import Any, ClassVar 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

9 

10from pydantic_core import ( 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

11 ArgsKwargs, 

12 SchemaSerializer, 

13 SchemaValidator, 

14 core_schema, 

15) 

16from typing_extensions import TypeIs 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

17 

18from ..errors import PydanticUndefinedAnnotation 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

19from ..plugin._schema_validator import PluggableSchemaValidator, create_schema_validator 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

20from ..warnings import PydanticDeprecatedSince20 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

21from . import _config, _decorators 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

22from ._fields import collect_dataclass_fields 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

23from ._generate_schema import GenerateSchema, InvalidSchemaError 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

24from ._generics import get_standard_typevars_map 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

25from ._mock_val_ser import set_dataclass_mocks 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

26from ._namespace_utils import NsResolver 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

27from ._signature import generate_pydantic_signature 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

28from ._utils import LazyClassAttribute 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

29 

30if typing.TYPE_CHECKING: 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

31 from _typeshed import DataclassInstance as StandardDataclass 

32 

33 from ..config import ConfigDict 

34 from ..fields import FieldInfo 

35 

36 class PydanticDataclass(StandardDataclass, typing.Protocol): 

37 """A protocol containing attributes only available once a class has been decorated as a Pydantic dataclass. 

38 

39 Attributes: 

40 __pydantic_config__: Pydantic-specific configuration settings for the dataclass. 

41 __pydantic_complete__: Whether dataclass building is completed, or if there are still undefined fields. 

42 __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer. 

43 __pydantic_decorators__: Metadata containing the decorators defined on the dataclass. 

44 __pydantic_fields__: Metadata about the fields defined on the dataclass. 

45 __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the dataclass. 

46 __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the dataclass. 

47 """ 

48 

49 __pydantic_config__: ClassVar[ConfigDict] 

50 __pydantic_complete__: ClassVar[bool] 

51 __pydantic_core_schema__: ClassVar[core_schema.CoreSchema] 

52 __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] 

53 __pydantic_fields__: ClassVar[dict[str, FieldInfo]] 

54 __pydantic_serializer__: ClassVar[SchemaSerializer] 

55 __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator] 

56 

57 @classmethod 

58 def __pydantic_fields_complete__(cls) -> bool: ... 

59 

60else: 

61 # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 

62 # and https://youtrack.jetbrains.com/issue/PY-51428 

63 DeprecationWarning = PydanticDeprecatedSince20 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

64 

65 

66def set_dataclass_fields( 1abcdefghijklKmnopqrstuvwxNPyzABCDEFGHIJO

67 cls: type[StandardDataclass], 

68 config_wrapper: _config.ConfigWrapper, 

69 ns_resolver: NsResolver | None = None, 

70) -> None: 

71 """Collect and set `cls.__pydantic_fields__`. 

72 

73 Args: 

74 cls: The class. 

75 config_wrapper: The config wrapper instance. 

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

77 """ 

78 typevars_map = get_standard_typevars_map(cls) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

79 fields = collect_dataclass_fields( 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

80 cls, ns_resolver=ns_resolver, typevars_map=typevars_map, config_wrapper=config_wrapper 

81 ) 

82 

83 cls.__pydantic_fields__ = fields # type: ignore 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

84 

85 

86def complete_dataclass( 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

87 cls: type[Any], 

88 config_wrapper: _config.ConfigWrapper, 

89 *, 

90 raise_errors: bool = True, 

91 ns_resolver: NsResolver | None = None, 

92 _force_build: bool = False, 

93) -> bool: 

94 """Finish building a pydantic dataclass. 

95 

96 This logic is called on a class which has already been wrapped in `dataclasses.dataclass()`. 

97 

98 This is somewhat analogous to `pydantic._internal._model_construction.complete_model_class`. 

99 

100 Args: 

101 cls: The class. 

102 config_wrapper: The config wrapper instance. 

103 raise_errors: Whether to raise errors, defaults to `True`. 

104 ns_resolver: The namespace resolver instance to use when collecting dataclass fields 

105 and during schema building. 

106 _force_build: Whether to force building the dataclass, no matter if 

107 [`defer_build`][pydantic.config.ConfigDict.defer_build] is set. 

108 

109 Returns: 

110 `True` if building a pydantic dataclass is successfully completed, `False` otherwise. 

111 

112 Raises: 

113 PydanticUndefinedAnnotation: If `raise_error` is `True` and there is an undefined annotations. 

114 """ 

115 original_init = cls.__init__ 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

116 

117 # dataclass.__init__ must be defined here so its `__qualname__` can be changed since functions can't be copied, 

118 # and so that the mock validator is used if building was deferred: 

119 def __init__(__dataclass_self__: PydanticDataclass, *args: Any, **kwargs: Any) -> None: 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

120 __tracebackhide__ = True 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

121 s = __dataclass_self__ 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

122 s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

123 

124 __init__.__qualname__ = f'{cls.__qualname__}.__init__' 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

125 

126 cls.__init__ = __init__ # type: ignore 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

127 cls.__pydantic_config__ = config_wrapper.config_dict # type: ignore 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

128 

129 set_dataclass_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

130 

131 if not _force_build and config_wrapper.defer_build: 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

132 set_dataclass_mocks(cls) 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

133 return False 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

134 

135 if hasattr(cls, '__post_init_post_parse__'): 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

136 warnings.warn( 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

137 'Support for `__post_init_post_parse__` has been dropped, the method will not be called', DeprecationWarning 

138 ) 

139 

140 typevars_map = get_standard_typevars_map(cls) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

141 gen_schema = GenerateSchema( 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

142 config_wrapper, 

143 ns_resolver=ns_resolver, 

144 typevars_map=typevars_map, 

145 ) 

146 

147 # set __signature__ attr only for the class, but not for its instances 

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

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

150 cls.__signature__ = LazyClassAttribute( 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

151 '__signature__', 

152 partial( 

153 generate_pydantic_signature, 

154 # It's important that we reference the `original_init` here, 

155 # as it is the one synthesized by the stdlib `dataclass` module: 

156 init=original_init, 

157 fields=cls.__pydantic_fields__, # type: ignore 

158 validate_by_name=config_wrapper.validate_by_name, 

159 extra=config_wrapper.extra, 

160 is_dataclass=True, 

161 ), 

162 ) 

163 

164 try: 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

165 schema = gen_schema.generate_schema(cls) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

166 except PydanticUndefinedAnnotation as e: 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

167 if raise_errors: 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

168 raise 1abcdefghijklLMmnopqrstuvwxyzABCDEFGHIJ

169 set_dataclass_mocks(cls, f'`{e.name}`') 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

170 return False 1abcdefghijklKLMmnopqrstuvwxNyzABCDEFGHIJO

171 

172 core_config = config_wrapper.core_config(title=cls.__name__) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

173 

174 try: 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

175 schema = gen_schema.clean_schema(schema) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

176 except InvalidSchemaError: 

177 set_dataclass_mocks(cls) 

178 return False 

179 

180 # We are about to set all the remaining required properties expected for this cast; 

181 # __pydantic_decorators__ and __pydantic_fields__ should already be set 

182 cls = typing.cast('type[PydanticDataclass]', cls) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

183 

184 cls.__pydantic_core_schema__ = schema 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

185 cls.__pydantic_validator__ = create_schema_validator( 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

186 schema, cls, cls.__module__, cls.__qualname__, 'dataclass', core_config, config_wrapper.plugin_settings 

187 ) 

188 cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config) 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

189 cls.__pydantic_complete__ = True 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

190 return True 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

191 

192 

193def is_stdlib_dataclass(cls: type[Any], /) -> TypeIs[type[StandardDataclass]]: 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO

194 """Returns `True` if the class is a stdlib dataclass and *not* a Pydantic dataclass. 

195 

196 Unlike the stdlib `dataclasses.is_dataclass()` function, this does *not* include subclasses 

197 of a dataclass that are themselves not dataclasses. 

198 

199 Args: 

200 cls: The class. 

201 

202 Returns: 

203 `True` if the class is a stdlib dataclass, `False` otherwise. 

204 """ 

205 return '__dataclass_fields__' in cls.__dict__ and not hasattr(cls, '__pydantic_validator__') 1abcdefghijklKLMmnopqrstuvwxNPyzABCDEFGHIJO