@@ -14,6 +14,7 @@ use crate::recursion_guard::RecursionGuard;
1414use super :: function:: convert_err;
1515use super :: { build_validator, BuildValidator , CombinedValidator , Definitions , DefinitionsBuilder , Extra , Validator } ;
1616
17+ const ROOT_FIELD : & str = "root" ;
1718const DUNDER_DICT : & str = "__dict__" ;
1819const DUNDER_FIELDS_SET_KEY : & str = "__pydantic_fields_set__" ;
1920const DUNDER_MODEL_EXTRA_KEY : & str = "__pydantic_extra__" ;
@@ -52,9 +53,10 @@ pub struct ModelValidator {
5253 validator : Box < CombinedValidator > ,
5354 class : Py < PyType > ,
5455 post_init : Option < Py < PyString > > ,
55- name : String ,
5656 frozen : bool ,
5757 custom_init : bool ,
58+ root_model : bool ,
59+ name : String ,
5860}
5961
6062impl BuildValidator for ModelValidator {
@@ -87,11 +89,12 @@ impl BuildValidator for ModelValidator {
8789 post_init : schema
8890 . get_as :: < & str > ( intern ! ( py, "post_init" ) ) ?
8991 . map ( |s| PyString :: intern ( py, s) . into_py ( py) ) ,
92+ frozen : schema. get_as ( intern ! ( py, "frozen" ) ) ?. unwrap_or ( false ) ,
93+ custom_init : schema. get_as ( intern ! ( py, "custom_init" ) ) ?. unwrap_or ( false ) ,
94+ root_model : schema. get_as ( intern ! ( py, "root_model" ) ) ?. unwrap_or ( false ) ,
9095 // Get the class's `__name__`, not using `class.name()` since it uses `__qualname__`
9196 // which is not what we want here
9297 name : class. getattr ( intern ! ( py, "__name__" ) ) ?. extract ( ) ?,
93- frozen : schema. get_as ( intern ! ( py, "frozen" ) ) ?. unwrap_or ( false ) ,
94- custom_init : schema. get_as ( intern ! ( py, "custom_init" ) ) ?. unwrap_or ( false ) ,
9598 }
9699 . into ( ) )
97100 }
@@ -125,28 +128,24 @@ impl Validator for ModelValidator {
125128 // mask 0 so JSON is input is never true here
126129 if input. input_is_instance ( class, 0 ) ? {
127130 if self . revalidate . should_revalidate ( input, class) {
128- let fields_set = input. input_get_attr ( intern ! ( py, DUNDER_FIELDS_SET_KEY ) ) . unwrap ( ) ?;
129-
130- // get dict here so from_attributes logic doesn't apply
131- let dict = input. input_get_attr ( intern ! ( py, DUNDER_DICT ) ) . unwrap ( ) ?;
132- let model_extra = input. input_get_attr ( intern ! ( py, DUNDER_MODEL_EXTRA_KEY ) ) . unwrap ( ) ?;
133-
134- let full_model_dict: & PyAny = if model_extra. is_none ( ) {
135- dict
131+ if self . root_model {
132+ let inner_input: & PyAny = input. input_get_attr ( intern ! ( py, ROOT_FIELD ) ) . unwrap ( ) ?;
133+ self . validate_construct ( py, inner_input, None , extra, definitions, recursion_guard)
136134 } else {
137- let full_model_dict = dict. downcast :: < PyDict > ( ) ?. copy ( ) ?;
138- full_model_dict. update ( model_extra. downcast ( ) ?) ?;
139- full_model_dict
140- } ;
141-
142- let output = self
143- . validator
144- . validate ( py, full_model_dict, extra, definitions, recursion_guard) ?;
145-
146- let ( model_dict, model_extra, _) : ( & PyAny , & PyAny , & PyAny ) = output. extract ( py) ?;
147- let instance = self . create_class ( model_dict, model_extra, fields_set) ?;
148-
149- self . call_post_init ( py, instance, input, extra)
135+ let fields_set = input. input_get_attr ( intern ! ( py, DUNDER_FIELDS_SET_KEY ) ) . unwrap ( ) ?;
136+ // get dict here so from_attributes logic doesn't apply
137+ let dict = input. input_get_attr ( intern ! ( py, DUNDER_DICT ) ) . unwrap ( ) ?;
138+ let model_extra = input. input_get_attr ( intern ! ( py, DUNDER_MODEL_EXTRA_KEY ) ) . unwrap ( ) ?;
139+
140+ let inner_input: & PyAny = if model_extra. is_none ( ) {
141+ dict
142+ } else {
143+ let full_model_dict = dict. downcast :: < PyDict > ( ) ?. copy ( ) ?;
144+ full_model_dict. update ( model_extra. downcast ( ) ?) ?;
145+ full_model_dict
146+ } ;
147+ self . validate_construct ( py, inner_input, Some ( fields_set) , extra, definitions, recursion_guard)
148+ }
150149 } else {
151150 Ok ( input. to_object ( py) )
152151 }
@@ -158,22 +157,7 @@ impl Validator for ModelValidator {
158157 input,
159158 ) )
160159 } else {
161- if self . custom_init {
162- // If we wanted, we could introspect the __init__ signature, and store the
163- // keyword arguments and types, and create a validator for them.
164- // Perhaps something similar to `validate_call`? Could probably make
165- // this work with from_attributes, and would essentially allow you to
166- // handle init vars by adding them to the __init__ signature.
167- if let Some ( kwargs) = input. as_kwargs ( py) {
168- return Ok ( self . class . call ( py, ( ) , Some ( kwargs) ) ?) ;
169- }
170- }
171- let output = self
172- . validator
173- . validate ( py, input, extra, definitions, recursion_guard) ?;
174- let ( model_dict, model_extra, fields_set) : ( & PyAny , & PyAny , & PyAny ) = output. extract ( py) ?;
175- let instance = self . create_class ( model_dict, model_extra, fields_set) ?;
176- self . call_post_init ( py, instance, input, extra)
160+ self . validate_construct ( py, input, None , extra, definitions, recursion_guard)
177161 }
178162 }
179163
@@ -189,9 +173,29 @@ impl Validator for ModelValidator {
189173 ) -> ValResult < ' data , PyObject > {
190174 if self . frozen {
191175 return Err ( ValError :: new ( ErrorType :: FrozenInstance , field_value) ) ;
176+ } else if self . root_model {
177+ return if field_name != ROOT_FIELD {
178+ Err ( ValError :: new_with_loc (
179+ ErrorType :: NoSuchAttribute {
180+ attribute : field_name. to_string ( ) ,
181+ } ,
182+ field_value,
183+ field_name. to_string ( ) ,
184+ ) )
185+ } else {
186+ let field_extra = Extra {
187+ field_name : Some ( field_name) ,
188+ ..* extra
189+ } ;
190+ let output = self
191+ . validator
192+ . validate ( py, field_value, & field_extra, definitions, recursion_guard) ?;
193+
194+ force_setattr ( py, model, intern ! ( py, ROOT_FIELD ) , output) ?;
195+ Ok ( model. into_py ( py) )
196+ } ;
192197 }
193- let dict_py_str = intern ! ( py, DUNDER_DICT ) ;
194- let dict: & PyDict = model. getattr ( dict_py_str) ?. downcast ( ) ?;
198+ let dict: & PyDict = model. getattr ( intern ! ( py, DUNDER_DICT ) ) ?. downcast ( ) ?;
195199
196200 let new_dict = dict. copy ( ) ?;
197201 new_dict. set_item ( field_name, field_value) ?;
@@ -216,7 +220,7 @@ impl Validator for ModelValidator {
216220 }
217221 let output = output. to_object ( py) ;
218222
219- force_setattr ( py, model, dict_py_str , output) ?;
223+ force_setattr ( py, model, intern ! ( py , DUNDER_DICT ) , output) ?;
220224 Ok ( model. into_py ( py) )
221225 }
222226
@@ -262,11 +266,61 @@ impl ModelValidator {
262266 let output = self
263267 . validator
264268 . validate ( py, input, & new_extra, definitions, recursion_guard) ?;
265- let ( model_dict, model_extra, fields_set) : ( & PyAny , & PyAny , & PyAny ) = output. extract ( py) ?;
266- set_model_attrs ( self_instance, model_dict, model_extra, fields_set) ?;
269+
270+ if self . root_model {
271+ force_setattr ( py, self_instance, intern ! ( py, ROOT_FIELD ) , output. as_ref ( py) ) ?;
272+ } else {
273+ let ( model_dict, model_extra, fields_set) : ( & PyAny , & PyAny , & PyAny ) = output. extract ( py) ?;
274+ set_model_attrs ( self_instance, model_dict, model_extra, fields_set) ?;
275+ }
267276 self . call_post_init ( py, self_instance. into_py ( py) , input, extra)
268277 }
269278
279+ fn validate_construct < ' s , ' data > (
280+ & ' s self ,
281+ py : Python < ' data > ,
282+ input : & ' data impl Input < ' data > ,
283+ existing_fields_set : Option < & ' data PyAny > ,
284+ extra : & Extra ,
285+ definitions : & ' data Definitions < CombinedValidator > ,
286+ recursion_guard : & ' s mut RecursionGuard ,
287+ ) -> ValResult < ' data , PyObject > {
288+ if self . custom_init {
289+ // If we wanted, we could introspect the __init__ signature, and store the
290+ // keyword arguments and types, and create a validator for them.
291+ // Perhaps something similar to `validate_call`? Could probably make
292+ // this work with from_attributes, and would essentially allow you to
293+ // handle init vars by adding them to the __init__ signature.
294+ if let Some ( kwargs) = input. as_kwargs ( py) {
295+ return Ok ( self . class . call ( py, ( ) , Some ( kwargs) ) ?) ;
296+ }
297+ }
298+
299+ let output = if self . root_model {
300+ let field_extra = Extra {
301+ field_name : Some ( ROOT_FIELD ) ,
302+ ..* extra
303+ } ;
304+ self . validator
305+ . validate ( py, input, & field_extra, definitions, recursion_guard) ?
306+ } else {
307+ self . validator
308+ . validate ( py, input, extra, definitions, recursion_guard) ?
309+ } ;
310+
311+ let instance = create_class ( self . class . as_ref ( py) ) ?;
312+ let instance_ref = instance. as_ref ( py) ;
313+
314+ if self . root_model {
315+ force_setattr ( py, instance_ref, intern ! ( py, ROOT_FIELD ) , output) ?;
316+ } else {
317+ let ( model_dict, model_extra, val_fields_set) : ( & PyAny , & PyAny , & PyAny ) = output. extract ( py) ?;
318+ let fields_set = existing_fields_set. unwrap_or ( val_fields_set) ;
319+ set_model_attrs ( instance_ref, model_dict, model_extra, fields_set) ?;
320+ }
321+ self . call_post_init ( py, instance, input, extra)
322+ }
323+
270324 fn call_post_init < ' s , ' data > (
271325 & ' s self ,
272326 py : Python < ' data > ,
@@ -281,13 +335,6 @@ impl ModelValidator {
281335 }
282336 Ok ( instance)
283337 }
284-
285- fn create_class ( & self , model_dict : & PyAny , model_extra : & PyAny , fields_set : & PyAny ) -> PyResult < PyObject > {
286- let py = model_dict. py ( ) ;
287- let instance = create_class ( self . class . as_ref ( py) ) ?;
288- set_model_attrs ( instance. as_ref ( py) , model_dict, model_extra, fields_set) ?;
289- Ok ( instance)
290- }
291338}
292339
293340/// based on the following but with the second argument of new_func set to an empty tuple as required
0 commit comments