Coverage for /pythoncovmergedfiles/medio/medio/src/pydantic/pydantic/dataclasses.py: 43%

60 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-04-27 07:38 +0000

1""" 

2Provide an enhanced dataclass that performs validation. 

3""" 

4from __future__ import annotations as _annotations 

5 

6import dataclasses 

7import sys 

8import types 

9from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, overload 

10 

11from typing_extensions import Literal, dataclass_transform 

12 

13from ._internal import _config, _decorators 

14from ._internal import _dataclasses as _pydantic_dataclasses 

15from ._internal._dataclasses import is_builtin_dataclass 

16from ._migration import getattr_migration 

17from .config import ConfigDict 

18from .fields import Field 

19 

20if TYPE_CHECKING: 

21 from ._internal._dataclasses import PydanticDataclass 

22 

23__all__ = ('dataclass',) 

24 

25_T = TypeVar('_T') 

26 

27if sys.version_info >= (3, 10): 

28 

29 @dataclass_transform(field_specifiers=(dataclasses.field, Field)) 

30 @overload 

31 def dataclass( 

32 *, 

33 init: Literal[False] = False, 

34 repr: bool = True, 

35 eq: bool = True, 

36 order: bool = False, 

37 unsafe_hash: bool = False, 

38 frozen: bool = False, 

39 config: ConfigDict | type[object] | None = None, 

40 validate_on_init: bool | None = None, 

41 kw_only: bool = ..., 

42 ) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore 

43 """Overload for `dataclass`.""" 

44 ... 

45 

46 @dataclass_transform(field_specifiers=(dataclasses.field, Field)) 

47 @overload 

48 def dataclass( 

49 _cls: type[_T], # type: ignore 

50 *, 

51 init: Literal[False] = False, 

52 repr: bool = True, 

53 eq: bool = True, 

54 order: bool = False, 

55 unsafe_hash: bool = False, 

56 frozen: bool = False, 

57 config: ConfigDict | type[object] | None = None, 

58 validate_on_init: bool | None = None, 

59 kw_only: bool = ..., 

60 ) -> type[PydanticDataclass]: 

61 """Overload for `dataclass`.""" 

62 ... 

63 

64else: 

65 

66 @dataclass_transform(field_specifiers=(dataclasses.field, Field)) 

67 @overload 

68 def dataclass( 

69 *, 

70 init: Literal[False] = False, 

71 repr: bool = True, 

72 eq: bool = True, 

73 order: bool = False, 

74 unsafe_hash: bool = False, 

75 frozen: bool = False, 

76 config: ConfigDict | type[object] | None = None, 

77 validate_on_init: bool | None = None, 

78 ) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore 

79 """Overload for `dataclass`.""" 

80 ... 

81 

82 @dataclass_transform(field_specifiers=(dataclasses.field, Field)) 

83 @overload 

84 def dataclass( 

85 _cls: type[_T], # type: ignore 

86 *, 

87 init: Literal[False] = False, 

88 repr: bool = True, 

89 eq: bool = True, 

90 order: bool = False, 

91 unsafe_hash: bool = False, 

92 frozen: bool = False, 

93 config: ConfigDict | type[object] | None = None, 

94 validate_on_init: bool | None = None, 

95 ) -> type[PydanticDataclass]: 

96 """Overload for `dataclass`.""" 

97 ... 

98 

99 

100@dataclass_transform(field_specifiers=(dataclasses.field, Field)) 

101def dataclass( 

102 _cls: type[_T] | None = None, 

103 *, 

104 init: Literal[False] = False, 

105 repr: bool = True, 

106 eq: bool = True, 

107 order: bool = False, 

108 unsafe_hash: bool = False, 

109 frozen: bool = False, 

110 config: ConfigDict | type[object] | None = None, 

111 validate_on_init: bool | None = None, 

112 kw_only: bool = False, 

113) -> Callable[[type[_T]], type[PydanticDataclass]] | type[PydanticDataclass]: 

114 """ 

115 A decorator used to create a Pydantic-enhanced dataclass, similar to the standard Python `dataclasses`, 

116 but with added validation. 

117 

118 Args: 

119 _cls (type[_T] | None): The target dataclass. 

120 init (Literal[False]): If set to `False`, the `dataclass` will not generate an `__init__`, 

121 and you will need to provide one. Defaults to `False`. 

122 repr (bool): Determines if a `__repr__` should be generated for the class. Defaults to `True`. 

123 eq (bool): Determines if a `__eq__` should be generated for the class. Defaults to `True`. 

124 order (bool): Determines if comparison magic methods should be generated, such as `__lt__`, but 

125 not `__eq__`. Defaults to `False`. 

126 unsafe_hash (bool): Determines if an unsafe hashing function should be included in the class. 

127 frozen (bool): Determines if the generated class should be a 'frozen' dataclass, which does not allow its 

128 attributes to be modified from its constructor. Defaults to `False`. 

129 config (ConfigDict | type[object] | None): A configuration for the `dataclass` generation. Defaults to `None`. 

130 validate_on_init (bool | None): Determines whether the `dataclass` will be validated upon creation. 

131 kw_only (bool): Determines if keyword-only parameters should be used on the `__init__` method. Defaults 

132 to `False`. 

133 

134 Returns: 

135 A callable that takes a `type` as its argument, and returns a `type` of `PydanticDataclass`. This can 

136 also return a `tyoe` of `PydanticDataclass` directly. 

137 

138 Raises: 

139 AssertionError: Raised if `init` is not `False`. 

140 """ 

141 assert init is False, 'pydantic.dataclasses.dataclass only supports init=False' 

142 

143 if sys.version_info >= (3, 10): 

144 kwargs = dict(kw_only=kw_only) 

145 else: 

146 kwargs = {} 

147 

148 def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]: 

149 """Create a Pydantic dataclass from a regular dataclass. 

150 

151 Args: 

152 cls (type[Any]): The class to create the Pydantic dataclass from. 

153 

154 Returns: 

155 type[PydanticDataclass]: A Pydantic dataclass. 

156 

157 Raises: 

158 TypeError: If a non-class value is provided. 

159 """ 

160 

161 config_wrapper = _config.ConfigWrapper(config) 

162 decorators = _decorators.DecoratorInfos.build(cls) 

163 

164 # Keep track of the original __doc__ so that we can restore it after applying the dataclasses decorator 

165 # Otherwise, classes with no __doc__ will have their signature added into the JSON schema description, 

166 # since dataclasses.dataclass will set this as the __doc__ 

167 original_doc = cls.__doc__ 

168 

169 if is_builtin_dataclass(cls): 

170 # Don't preserve the docstring for vanilla dataclasses, as it may include the signature 

171 # This matches v1 behavior, and there was an explicit test for it 

172 original_doc = None 

173 

174 # We don't want to add validation to the existing std lib dataclass, so we will subclass it 

175 # If the class is generic, we need to make sure the subclass also inherits from Generic 

176 # with all the same parameters. 

177 bases = (cls,) 

178 if issubclass(cls, Generic): # type: ignore 

179 generic_base = Generic[cls.__parameters__] # type: ignore 

180 bases = bases + (generic_base,) 

181 cls = types.new_class(cls.__name__, bases) 

182 

183 cls = dataclasses.dataclass( # type: ignore[call-overload] 

184 cls, 

185 init=init, 

186 repr=repr, 

187 eq=eq, 

188 order=order, 

189 unsafe_hash=unsafe_hash, 

190 frozen=frozen, 

191 **kwargs, 

192 ) 

193 

194 cls.__pydantic_decorators__ = decorators # type: ignore 

195 _pydantic_dataclasses.set_dataclass_fields(cls) 

196 _pydantic_dataclasses.complete_dataclass(cls, config_wrapper) 

197 cls.__doc__ = original_doc 

198 return cls 

199 

200 if _cls is None: 

201 return create_dataclass 

202 

203 return create_dataclass(_cls) 

204 

205 

206__getattr__ = getattr_migration(__name__)