@@ -7,7 +7,9 @@ use ahash::AHashSet;
7
7
8
8
use crate :: build_tools:: py_schema_err;
9
9
use crate :: build_tools:: { is_strict, schema_or_config_same, ExtraBehavior } ;
10
+ use crate :: errors:: LineErrorCollector ;
10
11
use crate :: errors:: LocItem ;
12
+ use crate :: errors:: ValResultExt ;
11
13
use crate :: errors:: { ErrorType , ErrorTypeDefaults , ValError , ValLineError , ValResult } ;
12
14
use crate :: input:: ConsumeIterator ;
13
15
use crate :: input:: { BorrowInput , Input , ValidatedDict , ValidationMatch } ;
@@ -148,7 +150,8 @@ impl Validator for ModelFieldsValidator {
148
150
149
151
let model_dict = PyDict :: new_bound ( py) ;
150
152
let mut model_extra_dict_op: Option < Bound < PyDict > > = None ;
151
- let mut errors: Vec < ValLineError > = Vec :: with_capacity ( self . fields . len ( ) ) ;
153
+ let mut errors = LineErrorCollector :: with_capacity ( self . fields . len ( ) ) ;
154
+
152
155
let mut fields_set_vec: Vec < Py < PyString > > = Vec :: with_capacity ( self . fields . len ( ) ) ;
153
156
let mut fields_set_count: usize = 0 ;
154
157
@@ -165,22 +168,20 @@ impl Validator for ModelFieldsValidator {
165
168
let state = & mut state. rebind_extra ( |extra| extra. data = Some ( model_dict. clone ( ) ) ) ;
166
169
167
170
for field in & self . fields {
168
- let op_key_value = match dict. get_item ( & field. lookup_key ) {
169
- Ok ( v) => v,
170
- Err ( ValError :: LineErrors ( line_errors) ) => {
171
- for err in line_errors {
172
- errors. push ( err. with_outer_location ( & field. name ) ) ;
173
- }
174
- continue ;
175
- }
176
- Err ( err) => return Err ( err) ,
171
+ let Some ( op_key_value) = dict
172
+ . get_item ( & field. lookup_key )
173
+ . collect_line_errors ( & mut errors, & field. name ) ?
174
+ else {
175
+ continue ;
177
176
} ;
177
+
178
178
if let Some ( ( lookup_path, value) ) = op_key_value {
179
179
if let Some ( ref mut used_keys) = used_keys {
180
180
// key is "used" whether or not validation passes, since we want to skip this key in
181
181
// extra logic either way
182
182
used_keys. insert ( lookup_path. first_key ( ) ) ;
183
183
}
184
+
184
185
match field. validator . validate ( py, value. borrow_input ( ) , state) {
185
186
Ok ( value) => {
186
187
model_dict. set_item ( & field. name_py , value) ?;
@@ -231,7 +232,7 @@ impl Validator for ModelFieldsValidator {
231
232
struct ValidateToModelExtra < ' a , ' s , ' py > {
232
233
py : Python < ' py > ,
233
234
used_keys : AHashSet < & ' a str > ,
234
- errors : & ' a mut Vec < ValLineError > ,
235
+ errors : & ' a mut LineErrorCollector ,
235
236
fields_set_vec : & ' a mut Vec < Py < PyString > > ,
236
237
extra_behavior : ExtraBehavior ,
237
238
extras_validator : Option < & ' a CombinedValidator > ,
@@ -287,17 +288,12 @@ impl Validator for ModelFieldsValidator {
287
288
ExtraBehavior :: Allow => {
288
289
let py_key = either_str. as_py_string ( self . py , self . state . cache_str ( ) ) ;
289
290
if let Some ( validator) = self . extras_validator {
290
- match validator. validate ( self . py , value, self . state ) {
291
- Ok ( value) => {
292
- model_extra_dict. set_item ( & py_key, value) ?;
293
- self . fields_set_vec . push ( py_key. into ( ) ) ;
294
- }
295
- Err ( ValError :: LineErrors ( line_errors) ) => {
296
- for err in line_errors {
297
- self . errors . push ( err. with_outer_location ( raw_key. clone ( ) ) ) ;
298
- }
299
- }
300
- Err ( err) => return Err ( err) ,
291
+ if let Some ( value) = validator
292
+ . validate ( self . py , value, self . state )
293
+ . collect_line_errors ( self . errors , raw_key. clone ( ) ) ?
294
+ {
295
+ model_extra_dict. set_item ( & py_key, value) ?;
296
+ self . fields_set_vec . push ( py_key. into ( ) ) ;
301
297
}
302
298
} else {
303
299
model_extra_dict. set_item ( & py_key, value. to_object ( self . py ) ) ?;
@@ -325,20 +321,18 @@ impl Validator for ModelFieldsValidator {
325
321
}
326
322
}
327
323
328
- if !errors. is_empty ( ) {
329
- Err ( ValError :: LineErrors ( errors) )
330
- } else {
331
- let fields_set = PySet :: new_bound ( py, & fields_set_vec) ?;
332
- state. add_fields_set ( fields_set_count) ;
324
+ errors. ensure_empty ( ) ?;
333
325
334
- // if we have extra=allow, but we didn't create a dict because we were validating
335
- // from attributes, set it now so __pydantic_extra__ is always a dict if extra=allow
336
- if matches ! ( self . extra_behavior, ExtraBehavior :: Allow ) && model_extra_dict_op. is_none ( ) {
337
- model_extra_dict_op = Some ( PyDict :: new_bound ( py) ) ;
338
- } ;
326
+ let fields_set = PySet :: new_bound ( py, & fields_set_vec) ?;
327
+ state. add_fields_set ( fields_set_count) ;
339
328
340
- Ok ( ( model_dict, model_extra_dict_op, fields_set) . to_object ( py) )
341
- }
329
+ // if we have extra=allow, but we didn't create a dict because we were validating
330
+ // from attributes, set it now so __pydantic_extra__ is always a dict if extra=allow
331
+ if matches ! ( self . extra_behavior, ExtraBehavior :: Allow ) && model_extra_dict_op. is_none ( ) {
332
+ model_extra_dict_op = Some ( PyDict :: new_bound ( py) ) ;
333
+ } ;
334
+
335
+ Ok ( ( model_dict, model_extra_dict_op, fields_set) . to_object ( py) )
342
336
}
343
337
344
338
fn validate_assignment < ' py > (
0 commit comments