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

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

83 statements  

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

2 

3from __future__ import annotations as _annotations 

4 

5import dataclasses 

6import typing 

7import warnings 

8from functools import partial, wraps 

9from typing import Any, ClassVar 

10 

11from pydantic_core import ( 

12 ArgsKwargs, 

13 SchemaSerializer, 

14 SchemaValidator, 

15 core_schema, 

16) 

17from typing_extensions import TypeGuard 

18 

19from ..errors import PydanticUndefinedAnnotation 

20from ..plugin._schema_validator import PluggableSchemaValidator, create_schema_validator 

21from ..warnings import PydanticDeprecatedSince20 

22from . import _config, _decorators 

23from ._fields import collect_dataclass_fields 

24from ._generate_schema import GenerateSchema, InvalidSchemaError 

25from ._generics import get_standard_typevars_map 

26from ._mock_val_ser import set_dataclass_mocks 

27from ._namespace_utils import NsResolver 

28from ._signature import generate_pydantic_signature 

29from ._utils import LazyClassAttribute 

30 

31if typing.TYPE_CHECKING: 

32 from _typeshed import DataclassInstance as StandardDataclass 

33 

34 from ..config import ConfigDict 

35 from ..fields import FieldInfo 

36 

37 class PydanticDataclass(StandardDataclass, typing.Protocol): 

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

39 

40 Attributes: 

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

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

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

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

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

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

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

48 """ 

49 

50 __pydantic_config__: ClassVar[ConfigDict] 

51 __pydantic_complete__: ClassVar[bool] 

52 __pydantic_core_schema__: ClassVar[core_schema.CoreSchema] 

53 __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] 

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

55 __pydantic_serializer__: ClassVar[SchemaSerializer] 

56 __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator] 

57 

58 @classmethod 

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

60 

61else: 

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

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

64 DeprecationWarning = PydanticDeprecatedSince20 

65 

66 

67def set_dataclass_fields( 

68 cls: type[StandardDataclass], 

69 ns_resolver: NsResolver | None = None, 

70 config_wrapper: _config.ConfigWrapper | None = None, 

71) -> None: 

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

73 

74 Args: 

75 cls: The class. 

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

77 config_wrapper: The config wrapper instance, defaults to `None`. 

78 """ 

79 typevars_map = get_standard_typevars_map(cls) 

80 fields = collect_dataclass_fields( 

81 cls, ns_resolver=ns_resolver, typevars_map=typevars_map, config_wrapper=config_wrapper 

82 ) 

83 

84 cls.__pydantic_fields__ = fields # type: ignore 

85 

86 

87def complete_dataclass( 

88 cls: type[Any], 

89 config_wrapper: _config.ConfigWrapper, 

90 *, 

91 raise_errors: bool = True, 

92 ns_resolver: NsResolver | None = None, 

93 _force_build: bool = False, 

94) -> bool: 

95 """Finish building a pydantic dataclass. 

96 

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

98 

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

100 

101 Args: 

102 cls: The class. 

103 config_wrapper: The config wrapper instance. 

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

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

106 and during schema building. 

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

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

109 

110 Returns: 

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

112 

113 Raises: 

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

115 """ 

116 original_init = cls.__init__ 

117 

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

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

120 def __init__(__dataclass_self__: PydanticDataclass, *args: Any, **kwargs: Any) -> None: 

121 __tracebackhide__ = True 

122 s = __dataclass_self__ 

123 s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s) 

124 

125 __init__.__qualname__ = f'{cls.__qualname__}.__init__' 

126 

127 cls.__init__ = __init__ # type: ignore 

128 cls.__pydantic_config__ = config_wrapper.config_dict # type: ignore 

129 

130 set_dataclass_fields(cls, ns_resolver, config_wrapper=config_wrapper) 

131 

132 if not _force_build and config_wrapper.defer_build: 

133 set_dataclass_mocks(cls) 

134 return False 

135 

136 if hasattr(cls, '__post_init_post_parse__'): 

137 warnings.warn( 

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

139 ) 

140 

141 typevars_map = get_standard_typevars_map(cls) 

142 gen_schema = GenerateSchema( 

143 config_wrapper, 

144 ns_resolver=ns_resolver, 

145 typevars_map=typevars_map, 

146 ) 

147 

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

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

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

151 cls.__signature__ = LazyClassAttribute( 

152 '__signature__', 

153 partial( 

154 generate_pydantic_signature, 

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

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

157 init=original_init, 

158 fields=cls.__pydantic_fields__, # type: ignore 

159 validate_by_name=config_wrapper.validate_by_name, 

160 extra=config_wrapper.extra, 

161 is_dataclass=True, 

162 ), 

163 ) 

164 

165 try: 

166 schema = gen_schema.generate_schema(cls) 

167 except PydanticUndefinedAnnotation as e: 

168 if raise_errors: 

169 raise 

170 set_dataclass_mocks(cls, f'`{e.name}`') 

171 return False 

172 

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

174 

175 try: 

176 schema = gen_schema.clean_schema(schema) 

177 except InvalidSchemaError: 

178 set_dataclass_mocks(cls) 

179 return False 

180 

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

182 # __pydantic_decorators__ and __pydantic_fields__ should already be set 

183 cls = typing.cast('type[PydanticDataclass]', cls) 

184 # debug(schema) 

185 

186 cls.__pydantic_core_schema__ = schema 

187 cls.__pydantic_validator__ = validator = create_schema_validator( 

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

189 ) 

190 cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config) 

191 

192 if config_wrapper.validate_assignment: 

193 

194 @wraps(cls.__setattr__) 

195 def validated_setattr(instance: Any, field: str, value: str, /) -> None: 

196 validator.validate_assignment(instance, field, value) 

197 

198 cls.__setattr__ = validated_setattr.__get__(None, cls) # type: ignore 

199 

200 cls.__pydantic_complete__ = True 

201 return True 

202 

203 

204def is_builtin_dataclass(_cls: type[Any]) -> TypeGuard[type[StandardDataclass]]: 

205 """Returns True if a class is a stdlib dataclass and *not* a pydantic dataclass. 

206 

207 We check that 

208 - `_cls` is a dataclass 

209 - `_cls` does not inherit from a processed pydantic dataclass (and thus have a `__pydantic_validator__`) 

210 - `_cls` does not have any annotations that are not dataclass fields 

211 e.g. 

212 ```python 

213 import dataclasses 

214 

215 import pydantic.dataclasses 

216 

217 @dataclasses.dataclass 

218 class A: 

219 x: int 

220 

221 @pydantic.dataclasses.dataclass 

222 class B(A): 

223 y: int 

224 ``` 

225 In this case, when we first check `B`, we make an extra check and look at the annotations ('y'), 

226 which won't be a superset of all the dataclass fields (only the stdlib fields i.e. 'x') 

227 

228 Args: 

229 cls: The class. 

230 

231 Returns: 

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

233 """ 

234 return ( 

235 dataclasses.is_dataclass(_cls) 

236 and not hasattr(_cls, '__pydantic_validator__') 

237 and set(_cls.__dataclass_fields__).issuperset(set(getattr(_cls, '__annotations__', {}))) 

238 )