@@ -7,8 +7,10 @@ use std::iter;
7
7
use serde:: ser:: SerializeSeq ;
8
8
9
9
use crate :: definitions:: DefinitionsBuilder ;
10
+ use crate :: serializers:: extra:: SerCheck ;
10
11
use crate :: serializers:: type_serializers:: any:: AnySerializer ;
11
12
use crate :: tools:: SchemaDict ;
13
+ use crate :: PydanticSerializationUnexpectedValue ;
12
14
13
15
use super :: {
14
16
infer_json_key, infer_serialize, infer_to_python, py_err_se_err, BuildSerializer , CombinedSerializer , Extra ,
@@ -70,52 +72,14 @@ impl TypeSerializer for TupleSerializer {
70
72
let py = value. py ( ) ;
71
73
72
74
let n_items = py_tuple. len ( ) ;
73
- let mut py_tuple_iter = py_tuple. iter ( ) ;
74
75
let mut items = Vec :: with_capacity ( n_items) ;
75
76
76
- macro_rules! use_serializers {
77
- ( $serializers_iter: expr) => {
78
- for ( index, serializer) in $serializers_iter. enumerate( ) {
79
- let element = match py_tuple_iter. next( ) {
80
- Some ( value) => value,
81
- None => break ,
82
- } ;
83
- let op_next = self
84
- . filter
85
- . index_filter( index, include, exclude, Some ( n_items) ) ?;
86
- if let Some ( ( next_include, next_exclude) ) = op_next {
87
- items. push( serializer. to_python( element, next_include, next_exclude, extra) ?) ;
88
- }
89
- }
90
- } ;
91
- }
92
-
93
- if let Some ( variadic_item_index) = self . variadic_item_index {
94
- // Need `saturating_sub` to handle items with too few elements without panicking
95
- let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
96
- let serializers_iter = self . serializers [ ..variadic_item_index]
97
- . iter ( )
98
- . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
99
- . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
100
- use_serializers ! ( serializers_iter) ;
101
- } else {
102
- use_serializers ! ( self . serializers. iter( ) ) ;
103
- let mut warned = false ;
104
- for ( i, element) in py_tuple_iter. enumerate ( ) {
105
- if !warned {
106
- extra
107
- . warnings
108
- . custom_warning ( "Unexpected extra items present in tuple" . to_string ( ) ) ;
109
- warned = true ;
110
- }
111
- let op_next =
112
- self . filter
113
- . index_filter ( i + self . serializers . len ( ) , include, exclude, Some ( n_items) ) ?;
114
- if let Some ( ( next_include, next_exclude) ) = op_next {
115
- items. push ( AnySerializer . to_python ( element, next_include, next_exclude, extra) ?) ;
116
- }
117
- }
118
- } ;
77
+ self . for_each_tuple_item_and_serializer ( py_tuple, include, exclude, extra, |entry| {
78
+ entry
79
+ . serializer
80
+ . to_python ( entry. item , entry. include , entry. exclude , extra)
81
+ . map ( |item| items. push ( item) )
82
+ } ) ??;
119
83
120
84
match extra. mode {
121
85
SerMode :: Json => Ok ( PyList :: new ( py, items) . into_py ( py) ) ,
@@ -132,35 +96,14 @@ impl TypeSerializer for TupleSerializer {
132
96
fn json_key < ' py > ( & self , key : & ' py PyAny , extra : & Extra ) -> PyResult < Cow < ' py , str > > {
133
97
match key. downcast :: < PyTuple > ( ) {
134
98
Ok ( py_tuple) => {
135
- let mut py_tuple_iter = py_tuple. iter ( ) ;
136
-
137
99
let mut key_builder = KeyBuilder :: new ( ) ;
138
100
139
- let n_items = py_tuple. len ( ) ;
140
-
141
- macro_rules! use_serializers {
142
- ( $serializers_iter: expr) => {
143
- for serializer in $serializers_iter {
144
- let element = match py_tuple_iter. next( ) {
145
- Some ( value) => value,
146
- None => break ,
147
- } ;
148
- key_builder. push( & serializer. json_key( element, extra) ?) ;
149
- }
150
- } ;
151
- }
152
-
153
- if let Some ( variadic_item_index) = self . variadic_item_index {
154
- // Need `saturating_sub` to handle items with too few elements without panicking
155
- let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
156
- let serializers_iter = self . serializers [ ..variadic_item_index]
157
- . iter ( )
158
- . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
159
- . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
160
- use_serializers ! ( serializers_iter) ;
161
- } else {
162
- use_serializers ! ( self . serializers. iter( ) ) ;
163
- } ;
101
+ self . for_each_tuple_item_and_serializer ( py_tuple, None , None , extra, |entry| {
102
+ entry
103
+ . serializer
104
+ . json_key ( entry. item , extra)
105
+ . map ( |key| key_builder. push ( & key) )
106
+ } ) ??;
164
107
165
108
Ok ( Cow :: Owned ( key_builder. finish ( ) ) )
166
109
}
@@ -184,63 +127,18 @@ impl TypeSerializer for TupleSerializer {
184
127
let py_tuple: & PyTuple = py_tuple. downcast ( ) . map_err ( py_err_se_err) ?;
185
128
186
129
let n_items = py_tuple. len ( ) ;
187
- let mut py_tuple_iter = py_tuple. iter ( ) ;
188
130
let mut seq = serializer. serialize_seq ( Some ( n_items) ) ?;
189
131
190
- macro_rules! use_serializers {
191
- ( $serializers_iter: expr) => {
192
- for ( index, serializer) in $serializers_iter. enumerate( ) {
193
- let element = match py_tuple_iter. next( ) {
194
- Some ( value) => value,
195
- None => break ,
196
- } ;
197
- let op_next = self
198
- . filter
199
- . index_filter( index, include, exclude, Some ( n_items) )
200
- . map_err( py_err_se_err) ?;
201
- if let Some ( ( next_include, next_exclude) ) = op_next {
202
- let item_serialize =
203
- PydanticSerializer :: new( element, serializer, next_include, next_exclude, extra) ;
204
- seq. serialize_element( & item_serialize) ?;
205
- }
206
- }
207
- } ;
208
- }
209
-
210
- if let Some ( variadic_item_index) = self . variadic_item_index {
211
- // Need `saturating_sub` to handle items with too few elements without panicking
212
- let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
213
- let serializers_iter = self . serializers [ ..variadic_item_index]
214
- . iter ( )
215
- . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
216
- . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
217
- use_serializers ! ( serializers_iter) ;
218
- } else {
219
- use_serializers ! ( self . serializers. iter( ) ) ;
220
- let mut warned = false ;
221
- for ( i, element) in py_tuple_iter. enumerate ( ) {
222
- if !warned {
223
- extra
224
- . warnings
225
- . custom_warning ( "Unexpected extra items present in tuple" . to_string ( ) ) ;
226
- warned = true ;
227
- }
228
- let op_next = self
229
- . filter
230
- . index_filter ( i + self . serializers . len ( ) , include, exclude, Some ( n_items) )
231
- . map_err ( py_err_se_err) ?;
232
- if let Some ( ( next_include, next_exclude) ) = op_next {
233
- let item_serialize = PydanticSerializer :: new (
234
- element,
235
- & CombinedSerializer :: Any ( AnySerializer ) ,
236
- next_include,
237
- next_exclude,
238
- extra,
239
- ) ;
240
- seq. serialize_element ( & item_serialize) ?;
241
- }
242
- }
243
- } ;
132
+ self . for_each_tuple_item_and_serializer ( py_tuple, include, exclude, extra, |entry| {
133
+ seq. serialize_element ( & PydanticSerializer :: new (
134
+ entry. item ,
135
+ entry. serializer ,
136
+ entry. include ,
137
+ entry. exclude ,
138
+ extra,
139
+ ) )
140
+ } )
141
+ . map_err ( py_err_se_err) ??;
244
142
245
143
seq. end ( )
246
144
}
@@ -254,6 +152,100 @@ impl TypeSerializer for TupleSerializer {
254
152
fn get_name ( & self ) -> & str {
255
153
& self . name
256
154
}
155
+
156
+ fn retry_with_lax_check ( & self ) -> bool {
157
+ true
158
+ }
159
+ }
160
+
161
+ struct TupleSerializerEntry < ' a , ' py > {
162
+ item : & ' py PyAny ,
163
+ include : Option < & ' py PyAny > ,
164
+ exclude : Option < & ' py PyAny > ,
165
+ serializer : & ' a CombinedSerializer ,
166
+ }
167
+
168
+ impl TupleSerializer {
169
+ /// Try to serialize each item in the tuple with the corresponding serializer.
170
+ ///
171
+ /// If the tuple doesn't match the length of the serializer, in strict mode, an error is returned.
172
+ ///
173
+ /// The error type E is the type of the error returned by the closure, which is why there are two
174
+ /// levels of `Result`.
175
+ fn for_each_tuple_item_and_serializer < E > (
176
+ & self ,
177
+ tuple : & PyTuple ,
178
+ include : Option < & PyAny > ,
179
+ exclude : Option < & PyAny > ,
180
+ extra : & Extra ,
181
+ mut f : impl for <' a , ' py > FnMut ( TupleSerializerEntry < ' a , ' py > ) -> Result < ( ) , E > ,
182
+ ) -> PyResult < Result < ( ) , E > > {
183
+ let n_items = tuple. len ( ) ;
184
+ let mut py_tuple_iter = tuple. iter ( ) ;
185
+
186
+ macro_rules! use_serializers {
187
+ ( $serializers_iter: expr) => {
188
+ for ( index, serializer) in $serializers_iter. enumerate( ) {
189
+ let element = match py_tuple_iter. next( ) {
190
+ Some ( value) => value,
191
+ None => break ,
192
+ } ;
193
+ let op_next = self . filter. index_filter( index, include, exclude, Some ( n_items) ) ?;
194
+ if let Some ( ( next_include, next_exclude) ) = op_next {
195
+ if let Err ( e) = f( TupleSerializerEntry {
196
+ item: element,
197
+ include: next_include,
198
+ exclude: next_exclude,
199
+ serializer,
200
+ } ) {
201
+ return Ok ( Err ( e) ) ;
202
+ } ;
203
+ }
204
+ }
205
+ } ;
206
+ }
207
+
208
+ if let Some ( variadic_item_index) = self . variadic_item_index {
209
+ // Need `saturating_sub` to handle items with too few elements without panicking
210
+ let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
211
+ let serializers_iter = self . serializers [ ..variadic_item_index]
212
+ . iter ( )
213
+ . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
214
+ . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
215
+ use_serializers ! ( serializers_iter) ;
216
+ } else if extra. check == SerCheck :: Strict && n_items != self . serializers . len ( ) {
217
+ return Err ( PydanticSerializationUnexpectedValue :: new_err ( Some ( format ! (
218
+ "Expected {} items, but got {}" ,
219
+ self . serializers. len( ) ,
220
+ n_items
221
+ ) ) ) ) ;
222
+ } else {
223
+ use_serializers ! ( self . serializers. iter( ) ) ;
224
+ let mut warned = false ;
225
+ for ( i, element) in py_tuple_iter. enumerate ( ) {
226
+ if !warned {
227
+ extra
228
+ . warnings
229
+ . custom_warning ( "Unexpected extra items present in tuple" . to_string ( ) ) ;
230
+ warned = true ;
231
+ }
232
+ let op_next = self
233
+ . filter
234
+ . index_filter ( i + self . serializers . len ( ) , include, exclude, Some ( n_items) ) ?;
235
+ if let Some ( ( next_include, next_exclude) ) = op_next {
236
+ if let Err ( e) = f ( TupleSerializerEntry {
237
+ item : element,
238
+ include : next_include,
239
+ exclude : next_exclude,
240
+ serializer : & CombinedSerializer :: Any ( AnySerializer ) ,
241
+ } ) {
242
+ return Ok ( Err ( e) ) ;
243
+ } ;
244
+ }
245
+ }
246
+ } ;
247
+ Ok ( Ok ( ( ) ) )
248
+ }
257
249
}
258
250
259
251
pub ( crate ) struct KeyBuilder {
0 commit comments