@@ -4,73 +4,27 @@ use pyo3::intern;
4
4
use pyo3:: prelude:: * ;
5
5
use pyo3:: types:: { PyDict , PyString } ;
6
6
7
- use ahash:: { AHashMap , AHashSet } ;
7
+ use ahash:: AHashMap ;
8
8
use serde:: ser:: SerializeMap ;
9
9
10
10
use crate :: build_tools:: { py_error_type, schema_or_config, ExtraBehavior , SchemaDict } ;
11
11
use crate :: definitions:: DefinitionsBuilder ;
12
12
use crate :: PydanticSerializationUnexpectedValue ;
13
13
14
14
use super :: {
15
- infer_json_key, infer_serialize, infer_to_python, py_err_se_err, BuildSerializer , CombinedSerializer ,
16
- ComputedFields , Extra , PydanticSerializer , SchemaFilter , SerializeInfer , TypeSerializer ,
15
+ exclude_default, infer_json_key, infer_serialize, infer_to_python, py_err_se_err, BuildSerializer ,
16
+ CombinedSerializer , ComputedFields , Extra , FieldSerializer , PydanticSerializer , SchemaFilter , SerializeInfer ,
17
+ TypeSerializer ,
17
18
} ;
18
19
19
- /// representation of a field for serialization, used by `TypedDictSerializer` and `ModelFieldsSerializer`
20
- #[ derive( Debug , Clone ) ]
21
- pub ( super ) struct FieldSerializer {
22
- key_py : Py < PyString > ,
23
- alias : Option < String > ,
24
- alias_py : Option < Py < PyString > > ,
25
- // None serializer means exclude
26
- serializer : Option < CombinedSerializer > ,
27
- required : bool ,
28
- }
29
-
30
- impl FieldSerializer {
31
- pub ( super ) fn new (
32
- py : Python ,
33
- key_py : Py < PyString > ,
34
- alias : Option < String > ,
35
- serializer : Option < CombinedSerializer > ,
36
- required : bool ,
37
- ) -> Self {
38
- let alias_py = alias. as_ref ( ) . map ( |alias| PyString :: new ( py, alias. as_str ( ) ) . into ( ) ) ;
39
- Self {
40
- key_py,
41
- alias,
42
- alias_py,
43
- serializer,
44
- required,
45
- }
46
- }
47
-
48
- fn get_key_py < ' py > ( & ' py self , py : Python < ' py > , extra : & Extra ) -> & ' py PyAny {
49
- if extra. by_alias {
50
- if let Some ( ref alias_py) = self . alias_py {
51
- return alias_py. as_ref ( py) ;
52
- }
53
- }
54
- self . key_py . as_ref ( py)
55
- }
56
-
57
- fn get_key_json < ' a > ( & ' a self , key_str : & ' a str , extra : & Extra ) -> Cow < ' a , str > {
58
- if extra. by_alias {
59
- if let Some ( ref alias) = self . alias {
60
- return Cow :: Borrowed ( alias. as_str ( ) ) ;
61
- }
62
- }
63
- Cow :: Borrowed ( key_str)
64
- }
65
- }
66
-
67
20
#[ derive( Debug , Clone ) ]
68
21
pub struct TypedDictSerializer {
69
22
fields : AHashMap < String , FieldSerializer > ,
70
23
computed_fields : Option < ComputedFields > ,
71
24
include_extra : bool ,
72
25
// isize because we look up filter via `.hash()` which returns an isize
73
26
filter : SchemaFilter < isize > ,
27
+ required_fields : usize ,
74
28
}
75
29
76
30
impl BuildSerializer for TypedDictSerializer {
@@ -126,11 +80,13 @@ impl TypedDictSerializer {
126
80
include_extra : bool ,
127
81
computed_fields : Option < ComputedFields > ,
128
82
) -> Self {
83
+ let required_fields = fields. values ( ) . filter ( |f| f. required ) . count ( ) ;
129
84
Self {
130
85
fields,
131
86
include_extra,
132
87
filter : SchemaFilter :: default ( ) ,
133
88
computed_fields,
89
+ required_fields,
134
90
}
135
91
}
136
92
@@ -140,17 +96,6 @@ impl TypedDictSerializer {
140
96
Some ( ref computed_fields) => computed_fields. len ( ) ,
141
97
}
142
98
}
143
-
144
- fn exclude_default ( & self , value : & PyAny , extra : & Extra , serializer : & CombinedSerializer ) -> PyResult < bool > {
145
- if extra. exclude_defaults {
146
- if let Some ( default) = serializer. get_default ( value. py ( ) ) ? {
147
- if value. eq ( default) ? {
148
- return Ok ( true ) ;
149
- }
150
- }
151
- }
152
- Ok ( false )
153
- }
154
99
}
155
100
156
101
impl TypeSerializer for TypedDictSerializer {
@@ -173,11 +118,7 @@ impl TypeSerializer for TypedDictSerializer {
173
118
Ok ( py_dict) => {
174
119
// NOTE! we maintain the order of the input dict assuming that's right
175
120
let output_dict = PyDict :: new ( py) ;
176
- let mut used_fields = if td_extra. check . enabled ( ) {
177
- Some ( AHashSet :: with_capacity ( self . fields . len ( ) ) )
178
- } else {
179
- None
180
- } ;
121
+ let mut used_req_fields: usize = 0 ;
181
122
182
123
for ( key, value) in py_dict {
183
124
if extra. exclude_none && value. is_none ( ) {
@@ -191,19 +132,10 @@ impl TypeSerializer for TypedDictSerializer {
191
132
if let Ok ( key_py_str) = key. downcast :: < PyString > ( ) {
192
133
let key_str = key_py_str. to_str ( ) ?;
193
134
if let Some ( field) = self . fields . get ( key_str) {
194
- let serializer = match field. serializer {
195
- Some ( ref serializer) => serializer,
196
- None => continue ,
197
- } ;
198
- if self . exclude_default ( value, & extra, serializer) ? {
199
- continue ;
200
- }
201
- let value = serializer. to_python ( value, next_include, next_exclude, & extra) ?;
202
- let output_key = field. get_key_py ( py, & extra) ;
203
- output_dict. set_item ( output_key, value) ?;
135
+ field. to_python ( output_dict, value, next_include, next_exclude, & extra) ?;
204
136
205
- if let Some ( ref mut used_fields ) = used_fields {
206
- used_fields . insert ( key_str ) ;
137
+ if field . required {
138
+ used_req_fields += 1 ;
207
139
}
208
140
continue ;
209
141
}
@@ -217,14 +149,8 @@ impl TypeSerializer for TypedDictSerializer {
217
149
}
218
150
}
219
151
}
220
- if let Some ( ref used_fields) = used_fields {
221
- let unused_fields = self
222
- . fields
223
- . iter ( )
224
- . any ( |( k, v) | v. required && !used_fields. contains ( k. as_str ( ) ) ) ;
225
- if unused_fields {
226
- return Err ( PydanticSerializationUnexpectedValue :: new_err ( None ) ) ;
227
- }
152
+ if td_extra. check . enabled ( ) && self . required_fields != used_req_fields {
153
+ return Err ( PydanticSerializationUnexpectedValue :: new_err ( None ) ) ;
228
154
}
229
155
if let Some ( ref computed_fields) = self . computed_fields {
230
156
if let Some ( model) = td_extra. model {
@@ -283,16 +209,19 @@ impl TypeSerializer for TypedDictSerializer {
283
209
if let Ok ( key_py_str) = key. downcast :: < PyString > ( ) {
284
210
let key_str = key_py_str. to_str ( ) . map_err ( py_err_se_err) ?;
285
211
if let Some ( field) = self . fields . get ( key_str) {
286
- let serializer = match field. serializer {
287
- Some ( ref serializer) => serializer,
288
- None => continue ,
289
- } ;
290
- if self . exclude_default ( value, & extra, serializer) . map_err ( py_err_se_err) ? {
291
- continue ;
212
+ if let Some ( ref serializer) = field. serializer {
213
+ if !exclude_default ( value, & extra, serializer) . map_err ( py_err_se_err) ? {
214
+ let s = PydanticSerializer :: new (
215
+ value,
216
+ serializer,
217
+ next_include,
218
+ next_exclude,
219
+ & extra,
220
+ ) ;
221
+ let output_key = field. get_key_json ( key_str, & extra) ;
222
+ map. serialize_entry ( & output_key, & s) ?;
223
+ }
292
224
}
293
- let output_key = field. get_key_json ( key_str, & extra) ;
294
- let s = PydanticSerializer :: new ( value, serializer, next_include, next_exclude, & extra) ;
295
- map. serialize_entry ( & output_key, & s) ?;
296
225
continue ;
297
226
}
298
227
}
0 commit comments