3
3
from .class_validators import gather_all_validators
4
4
from .error_wrappers import ValidationError
5
5
from .errors import DataclassTypeError
6
- from .fields import Required
6
+ from .fields import Field , FieldInfo , Required , Undefined
7
7
from .main import create_model , validate_model
8
8
from .typing import resolve_annotations
9
9
from .utils import ClassAttribute
10
10
11
11
if TYPE_CHECKING :
12
12
from .main import BaseConfig , BaseModel # noqa: F401
13
- from .typing import CallableGenerator
13
+ from .typing import CallableGenerator , NoArgAnyCallable
14
14
15
15
DataclassT = TypeVar ('DataclassT' , bound = 'Dataclass' )
16
16
@@ -19,6 +19,7 @@ class Dataclass:
19
19
__initialised__ : bool
20
20
__post_init_original__ : Optional [Callable [..., None ]]
21
21
__processed__ : Optional [ClassAttribute ]
22
+ __has_field_info_default__ : bool # whether or not a `pydantic.Field` is used as default value
22
23
23
24
def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
24
25
pass
@@ -80,6 +81,30 @@ def is_builtin_dataclass(_cls: Type[Any]) -> bool:
80
81
return not hasattr (_cls , '__processed__' ) and dataclasses .is_dataclass (_cls )
81
82
82
83
84
+ def _generate_pydantic_post_init (
85
+ post_init_original : Optional [Callable [..., None ]], post_init_post_parse : Optional [Callable [..., None ]]
86
+ ) -> Callable [..., None ]:
87
+ def _pydantic_post_init (self : 'Dataclass' , * initvars : Any ) -> None :
88
+ if post_init_original is not None :
89
+ post_init_original (self , * initvars )
90
+
91
+ if getattr (self , '__has_field_info_default__' , False ):
92
+ # We need to remove `FieldInfo` values since they are not valid as input
93
+ # It's ok to do that because they are obviously the default values!
94
+ input_data = {k : v for k , v in self .__dict__ .items () if not isinstance (v , FieldInfo )}
95
+ else :
96
+ input_data = self .__dict__
97
+ d , _ , validation_error = validate_model (self .__pydantic_model__ , input_data , cls = self .__class__ )
98
+ if validation_error :
99
+ raise validation_error
100
+ object .__setattr__ (self , '__dict__' , d )
101
+ object .__setattr__ (self , '__initialised__' , True )
102
+ if post_init_post_parse is not None :
103
+ post_init_post_parse (self , * initvars )
104
+
105
+ return _pydantic_post_init
106
+
107
+
83
108
def _process_class (
84
109
_cls : Type [Any ],
85
110
init : bool ,
@@ -100,16 +125,7 @@ def _process_class(
100
125
101
126
post_init_post_parse = getattr (_cls , '__post_init_post_parse__' , None )
102
127
103
- def _pydantic_post_init (self : 'Dataclass' , * initvars : Any ) -> None :
104
- if post_init_original is not None :
105
- post_init_original (self , * initvars )
106
- d , _ , validation_error = validate_model (self .__pydantic_model__ , self .__dict__ , cls = self .__class__ )
107
- if validation_error :
108
- raise validation_error
109
- object .__setattr__ (self , '__dict__' , d )
110
- object .__setattr__ (self , '__initialised__' , True )
111
- if post_init_post_parse is not None :
112
- post_init_post_parse (self , * initvars )
128
+ _pydantic_post_init = _generate_pydantic_post_init (post_init_original , post_init_post_parse )
113
129
114
130
# If the class is already a dataclass, __post_init__ will not be called automatically
115
131
# so no validation will be added.
@@ -144,22 +160,31 @@ def _pydantic_post_init(self: 'Dataclass', *initvars: Any) -> None:
144
160
)
145
161
cls .__processed__ = ClassAttribute ('__processed__' , True )
146
162
147
- fields : Dict [str , Any ] = {}
163
+ field_definitions : Dict [str , Any ] = {}
148
164
for field in dataclasses .fields (cls ):
165
+ default : Any = Undefined
166
+ default_factory : Optional ['NoArgAnyCallable' ] = None
167
+ field_info : FieldInfo
149
168
150
- if field .default != dataclasses .MISSING :
151
- field_value = field .default
169
+ if field .default is not dataclasses .MISSING :
170
+ default = field .default
152
171
# mypy issue 7020 and 708
153
- elif field .default_factory != dataclasses .MISSING : # type: ignore
154
- field_value = field .default_factory () # type: ignore
172
+ elif field .default_factory is not dataclasses .MISSING : # type: ignore
173
+ default_factory = field .default_factory # type: ignore
174
+ else :
175
+ default = Required
176
+
177
+ if isinstance (default , FieldInfo ):
178
+ field_info = default
179
+ cls .__has_field_info_default__ = True
155
180
else :
156
- field_value = Required
181
+ field_info = Field ( default = default , default_factory = default_factory , ** field . metadata )
157
182
158
- fields [field .name ] = (field .type , field_value )
183
+ field_definitions [field .name ] = (field .type , field_info )
159
184
160
185
validators = gather_all_validators (cls )
161
186
cls .__pydantic_model__ = create_model (
162
- cls .__name__ , __config__ = config , __module__ = _cls .__module__ , __validators__ = validators , ** fields
187
+ cls .__name__ , __config__ = config , __module__ = _cls .__module__ , __validators__ = validators , ** field_definitions
163
188
)
164
189
165
190
cls .__initialised__ = False
0 commit comments