@@ -9,14 +9,16 @@ use ahash::AHashMap;
99use  pyo3:: IntoPyObjectExt ; 
1010
1111use  super :: { 
12-     infer_json_key,  infer_json_key_known,  infer_serialize,  infer_to_python,  py_err_se_err ,   BuildSerializer , 
13-     CombinedSerializer ,   ComputedFields ,  Extra ,  FieldsMode ,  GeneralFieldsSerializer ,  ObType ,  SerCheck ,  SerField , 
14-     TypeSerializer , 
12+     infer_json_key,  infer_json_key_known,  infer_serialize,  infer_to_python,  BuildSerializer ,   CombinedSerializer , 
13+     ComputedFields ,  Extra ,  FieldsMode ,  GeneralFieldsSerializer ,  ObType ,  SerCheck ,  SerField ,   TypeSerializer , 
14+     WrappedSerError , 
1515} ; 
1616use  crate :: build_tools:: py_schema_err; 
1717use  crate :: build_tools:: { py_schema_error_type,  ExtraBehavior } ; 
1818use  crate :: definitions:: DefinitionsBuilder ; 
19- use  crate :: serializers:: errors:: PydanticSerializationUnexpectedValue ; 
19+ use  crate :: serializers:: type_serializers:: any:: AnySerializer ; 
20+ use  crate :: serializers:: type_serializers:: function:: FunctionPlainSerializer ; 
21+ use  crate :: serializers:: type_serializers:: function:: FunctionWrapSerializer ; 
2022use  crate :: tools:: SchemaDict ; 
2123
2224const  ROOT_FIELD :  & str  = "root" ; 
@@ -138,17 +140,80 @@ fn has_extra(schema: &Bound<'_, PyDict>, config: Option<&Bound<'_, PyDict>>) ->
138140} 
139141
140142impl  ModelSerializer  { 
141-     fn  allow_value ( & self ,  value :  & Bound < ' _ ,  PyAny > ,  extra :  & Extra )  -> PyResult < bool >  { 
142-         let  class = self . class . bind ( value. py ( ) ) ; 
143-         match  extra. check  { 
144-             SerCheck :: Strict  => Ok ( value. get_type ( ) . is ( class) ) , 
145-             SerCheck :: Lax  => value. is_instance ( class) , 
143+     fn  allow_value ( & self ,  value :  & Bound < ' _ ,  PyAny > ,  check :  SerCheck )  -> PyResult < bool >  { 
144+         match  check { 
145+             SerCheck :: Strict  => Ok ( value. get_type ( ) . is ( & self . class ) ) , 
146+             SerCheck :: Lax  => value. is_instance ( self . class . bind ( value. py ( ) ) ) , 
146147            SerCheck :: None  => value. hasattr ( intern ! ( value. py( ) ,  "__dict__" ) ) , 
147148        } 
148149    } 
149150
151+     fn  allow_value_root_model ( & self ,  value :  & Bound < ' _ ,  PyAny > ,  check :  SerCheck )  -> PyResult < bool >  { 
152+         match  check { 
153+             SerCheck :: Strict  => Ok ( value. get_type ( ) . is ( & self . class ) ) , 
154+             SerCheck :: Lax  | SerCheck :: None  => value. is_instance ( self . class . bind ( value. py ( ) ) ) , 
155+         } 
156+     } 
157+ 
158+     /// Performs serialization for the model. This handles 
159+ /// - compatibility checks 
160+ /// - extracting the inner value for root models 
161+ /// - applying `serialize_as_any` where needed 
162+ /// 
163+ /// `do_serialize` should be a function which performs the actual serialization, and should not 
164+ /// apply any type inference. (`Model` serialization is strongly coupled with its child 
165+ /// serializer, and in the few cases where `serialize_as_any` applies, it is handled here.) 
166+ /// 
167+ /// If the value is not applicable, `do_serialize` will be called with `None` to indicate fallback 
168+ /// behaviour should be used. 
169+ fn  serialize < T ,  E :  From < PyErr > > ( 
170+         & self , 
171+         value :  & Bound < ' _ ,  PyAny > , 
172+         extra :  & Extra , 
173+         do_serialize :  impl  FnOnce ( Option < ( & Arc < CombinedSerializer > ,  & Bound < ' _ ,  PyAny > ,  & Extra ) > )  -> Result < T ,  E > , 
174+     )  -> Result < T ,  E >  { 
175+         match  self . root_model  { 
176+             true  if  self . allow_value_root_model ( value,  extra. check ) ? => { 
177+                 let  root_extra = Extra  { 
178+                     field_name :  Some ( ROOT_FIELD ) , 
179+                     model :  Some ( value) , 
180+                     ..extra. clone ( ) 
181+                 } ; 
182+                 let  root = value. getattr ( intern ! ( value. py( ) ,  ROOT_FIELD ) ) ?; 
183+ 
184+                 // for root models, `serialize_as_any` may apply unless a `field_serializer` is used 
185+                 let  serializer = if  root_extra. serialize_as_any 
186+                     && !matches ! ( 
187+                         self . serializer. as_ref( ) , 
188+                         CombinedSerializer :: Function ( FunctionPlainSerializer  { 
189+                             is_field_serializer:  true , 
190+                             ..
191+                         } )  | CombinedSerializer :: FunctionWrap ( FunctionWrapSerializer  { 
192+                             is_field_serializer:  true , 
193+                             ..
194+                         } ) , 
195+                     )  { 
196+                     AnySerializer :: get ( ) 
197+                 }  else  { 
198+                     & self . serializer 
199+                 } ; 
200+ 
201+                 do_serialize ( Some ( ( serializer,  & root,  & root_extra) ) ) 
202+             } 
203+             false  if  self . allow_value ( value,  extra. check ) ? => { 
204+                 let  model_extra = Extra  { 
205+                     model :  Some ( value) , 
206+                     ..extra. clone ( ) 
207+                 } ; 
208+                 let  inner_value = self . get_inner_value ( value,  & model_extra) ?; 
209+                 do_serialize ( Some ( ( & self . serializer ,  & inner_value,  & model_extra) ) ) 
210+             } 
211+             _ => do_serialize ( None ) , 
212+         } 
213+     } 
214+ 
150215    fn  get_inner_value < ' py > ( & self ,  model :  & Bound < ' py ,  PyAny > ,  extra :  & Extra )  -> PyResult < Bound < ' py ,  PyAny > >  { 
151-         let  py = model. py ( ) ; 
216+         let  py:   Python < ' _ >  = model. py ( ) ; 
152217        let  mut  attrs = model. getattr ( intern ! ( py,  "__dict__" ) ) ?. downcast_into :: < PyDict > ( ) ?; 
153218
154219        if  extra. exclude_unset  { 
@@ -184,38 +249,18 @@ impl TypeSerializer for ModelSerializer {
184249        exclude :  Option < & Bound < ' _ ,  PyAny > > , 
185250        extra :  & Extra , 
186251    )  -> PyResult < Py < PyAny > >  { 
187-         let  model = Some ( value) ; 
188- 
189-         let  model_extra = Extra  {  model,  ..* extra } ; 
190-         if  self . root_model  { 
191-             let  field_name = Some ( ROOT_FIELD ) ; 
192-             let  root_extra = Extra  { 
193-                 field_name, 
194-                 ..model_extra
195-             } ; 
196-             let  py = value. py ( ) ; 
197-             let  root = value. getattr ( intern ! ( py,  ROOT_FIELD ) ) . map_err ( |original_err| { 
198-                 if  root_extra. check . enabled ( )  { 
199-                     PydanticSerializationUnexpectedValue :: new_from_msg ( None ) . to_py_err ( ) 
200-                 }  else  { 
201-                     original_err
202-                 } 
203-             } ) ?; 
204-             self . serializer . to_python ( & root,  include,  exclude,  & root_extra) 
205-         }  else  if  self . allow_value ( value,  & model_extra) ? { 
206-             let  inner_value = self . get_inner_value ( value,  & model_extra) ?; 
207-             // There is strong coupling between a model serializer and its child, we should 
208-             // not fall back to type inference in the middle. 
209-             self . serializer 
210-                 . to_python_no_infer ( & inner_value,  include,  exclude,  & model_extra) 
211-         }  else  { 
212-             extra. warnings . on_fallback_py ( self . get_name ( ) ,  value,  & model_extra) ?; 
213-             infer_to_python ( value,  include,  exclude,  & model_extra) 
214-         } 
252+         self . serialize ( value,  extra,  |resolved| match  resolved { 
253+             Some ( ( serializer,  value,  extra) )  => serializer. to_python_no_infer ( value,  include,  exclude,  extra) , 
254+             None  => { 
255+                 extra. warnings . on_fallback_py ( self . get_name ( ) ,  value,  extra) ?; 
256+                 infer_to_python ( value,  include,  exclude,  extra) 
257+             } 
258+         } ) 
215259    } 
216260
217261    fn  json_key < ' a > ( & self ,  key :  & ' a  Bound < ' _ ,  PyAny > ,  extra :  & Extra )  -> PyResult < Cow < ' a ,  str > >  { 
218-         if  self . allow_value ( key,  extra) ? { 
262+         // FIXME: root model in json key position should serialize as inner value? 
263+         if  self . allow_value ( key,  extra. check ) ? { 
219264            infer_json_key_known ( ObType :: PydanticSerializable ,  key,  extra) 
220265        }  else  { 
221266            extra. warnings . on_fallback_py ( & self . name ,  key,  extra) ?; 
@@ -231,30 +276,19 @@ impl TypeSerializer for ModelSerializer {
231276        exclude :  Option < & Bound < ' _ ,  PyAny > > , 
232277        extra :  & Extra , 
233278    )  -> Result < S :: Ok ,  S :: Error >  { 
234-         let  model = Some ( value) ; 
235-         let  model_extra = Extra  {  model,  ..* extra } ; 
236-         if  self . root_model  { 
237-             let  field_name = Some ( ROOT_FIELD ) ; 
238-             let  root_extra = Extra  { 
239-                 field_name, 
240-                 ..model_extra
241-             } ; 
242-             let  py = value. py ( ) ; 
243-             let  root = value. getattr ( intern ! ( py,  ROOT_FIELD ) ) . map_err ( py_err_se_err) ?; 
244-             self . serializer 
245-                 . serde_serialize ( & root,  serializer,  include,  exclude,  & root_extra) 
246-         }  else  if  self . allow_value ( value,  & model_extra) . map_err ( py_err_se_err) ? { 
247-             let  inner_value = self . get_inner_value ( value,  & model_extra) . map_err ( py_err_se_err) ?; 
248-             // There is strong coupling between a model serializer and its child, we should 
249-             // not fall back to type inference in the midddle. 
250-             self . serializer 
251-                 . serde_serialize_no_infer ( & inner_value,  serializer,  include,  exclude,  & model_extra) 
252-         }  else  { 
253-             extra
254-                 . warnings 
255-                 . on_fallback_ser :: < S > ( self . get_name ( ) ,  value,  & model_extra) ?; 
256-             infer_serialize ( value,  serializer,  include,  exclude,  & model_extra) 
257-         } 
279+         self . serialize ( value,  extra,  |resolved| match  resolved { 
280+             Some ( ( cs,  value,  extra) )  => cs
281+                 . serde_serialize_no_infer ( value,  serializer,  include,  exclude,  extra) 
282+                 . map_err ( WrappedSerError ) , 
283+             None  => { 
284+                 extra
285+                     . warnings 
286+                     . on_fallback_ser :: < S > ( self . get_name ( ) ,  value,  extra) 
287+                     . map_err ( WrappedSerError ) ?; 
288+                 infer_serialize ( value,  serializer,  include,  exclude,  extra) . map_err ( WrappedSerError ) 
289+             } 
290+         } ) 
291+         . map_err ( |e| e. 0 ) 
258292    } 
259293
260294    fn  get_name ( & self )  -> & str  { 
0 commit comments