@@ -2,13 +2,12 @@ use pyo3::prelude::*;
22use pyo3:: types:: { PyDict , PyList , PyString } ;
33use pyo3:: { intern, PyTraverseError , PyVisit } ;
44use serde:: ser:: SerializeMap ;
5- use serde:: Serialize ;
65
76use crate :: build_tools:: py_schema_error_type;
87use crate :: definitions:: DefinitionsBuilder ;
98use crate :: py_gc:: PyGcTraverse ;
109use crate :: serializers:: filter:: SchemaFilter ;
11- use crate :: serializers:: shared:: { BuildSerializer , CombinedSerializer , PydanticSerializer , TypeSerializer } ;
10+ use crate :: serializers:: shared:: { BuildSerializer , CombinedSerializer , PydanticSerializer } ;
1211use crate :: tools:: SchemaDict ;
1312
1413use super :: errors:: py_err_se_err;
@@ -48,18 +47,31 @@ impl ComputedFields {
4847 exclude : Option < & Bound < ' _ , PyAny > > ,
4948 extra : & Extra ,
5049 ) -> PyResult < ( ) > {
51- if extra. round_trip {
52- // Do not serialize computed fields
53- return Ok ( ( ) ) ;
54- }
55- for computed_field in & self . 0 {
56- let field_extra = Extra {
57- field_name : Some ( computed_field. property_name . as_str ( ) ) ,
58- ..* extra
59- } ;
60- computed_field. to_python ( model, output_dict, filter, include, exclude, & field_extra) ?;
61- }
62- Ok ( ( ) )
50+ self . serialize_fields (
51+ model,
52+ filter,
53+ include,
54+ exclude,
55+ extra,
56+ |e| e,
57+ |ComputedFieldToSerialize {
58+ computed_field,
59+ value,
60+ include,
61+ exclude,
62+ field_extra,
63+ } | {
64+ let key = match field_extra. serialize_by_alias_or ( computed_field. serialize_by_alias ) {
65+ true => computed_field. alias_py . bind ( model. py ( ) ) ,
66+ false => computed_field. property_name_py . bind ( model. py ( ) ) ,
67+ } ;
68+ let value =
69+ computed_field
70+ . serializer
71+ . to_python ( & value, include. as_ref ( ) , exclude. as_ref ( ) , & field_extra) ?;
72+ output_dict. set_item ( key, value)
73+ } ,
74+ )
6375 }
6476
6577 pub fn serde_serialize < S : serde:: ser:: Serializer > (
@@ -71,44 +83,96 @@ impl ComputedFields {
7183 exclude : Option < & Bound < ' _ , PyAny > > ,
7284 extra : & Extra ,
7385 ) -> Result < ( ) , S :: Error > {
86+ self . serialize_fields (
87+ model,
88+ filter,
89+ include,
90+ exclude,
91+ extra,
92+ py_err_se_err,
93+ |ComputedFieldToSerialize {
94+ computed_field,
95+ value,
96+ include,
97+ exclude,
98+ field_extra,
99+ } | {
100+ let key = match field_extra. serialize_by_alias_or ( computed_field. serialize_by_alias ) {
101+ true => & computed_field. alias ,
102+ false => & computed_field. property_name ,
103+ } ;
104+ let s = PydanticSerializer :: new (
105+ & value,
106+ & computed_field. serializer ,
107+ include. as_ref ( ) ,
108+ exclude. as_ref ( ) ,
109+ & field_extra,
110+ ) ;
111+ map. serialize_entry ( key, & s)
112+ } ,
113+ )
114+ }
115+
116+ /// Iterate each field for serialization, filtering on
117+ /// `include` and `exclude` if provided.
118+ #[ allow( clippy:: too_many_arguments) ]
119+ fn serialize_fields < ' a , ' py , E > (
120+ & ' a self ,
121+ model : & ' a Bound < ' py , PyAny > ,
122+ filter : & ' a SchemaFilter < isize > ,
123+ include : Option < & ' a Bound < ' py , PyAny > > ,
124+ exclude : Option < & ' a Bound < ' py , PyAny > > ,
125+ extra : & ' a Extra ,
126+ convert_error : impl FnOnce ( PyErr ) -> E ,
127+ mut serialize : impl FnMut ( ComputedFieldToSerialize < ' a , ' py > ) -> Result < ( ) , E > ,
128+ ) -> Result < ( ) , E > {
74129 if extra. round_trip {
75130 // Do not serialize computed fields
76131 return Ok ( ( ) ) ;
77132 }
78133
79134 for computed_field in & self . 0 {
80135 let property_name_py = computed_field. property_name_py . bind ( model. py ( ) ) ;
136+ let ( next_include, next_exclude) = match filter. key_filter ( property_name_py, include, exclude) {
137+ Ok ( Some ( ( next_include, next_exclude) ) ) => ( next_include, next_exclude) ,
138+ Ok ( None ) => continue ,
139+ Err ( e) => return Err ( convert_error ( e) ) ,
140+ } ;
81141
82- if let Some ( ( next_include, next_exclude) ) = filter
83- . key_filter ( property_name_py, include, exclude)
84- . map_err ( py_err_se_err) ?
85- {
86- let value = model. getattr ( property_name_py) . map_err ( py_err_se_err) ?;
87- if extra. exclude_none && value. is_none ( ) {
88- continue ;
142+ let value = match model. getattr ( property_name_py) {
143+ Ok ( field_value) => field_value,
144+ Err ( e) => {
145+ return Err ( convert_error ( e) ) ;
89146 }
90- let field_extra = Extra {
91- field_name : Some ( computed_field. property_name . as_str ( ) ) ,
92- ..* extra
93- } ;
94- let cfs = ComputedFieldSerializer {
95- model,
96- computed_field,
97- include : next_include. as_ref ( ) ,
98- exclude : next_exclude. as_ref ( ) ,
99- extra : & field_extra,
100- } ;
101- let key = match extra. serialize_by_alias_or ( computed_field. serialize_by_alias ) {
102- true => computed_field. alias . as_str ( ) ,
103- false => computed_field. property_name . as_str ( ) ,
104- } ;
105- map. serialize_entry ( key, & cfs) ?;
147+ } ;
148+ if extra. exclude_none && value. is_none ( ) {
149+ continue ;
106150 }
151+
152+ let field_extra = Extra {
153+ field_name : Some ( & computed_field. property_name ) ,
154+ ..* extra
155+ } ;
156+ serialize ( ComputedFieldToSerialize {
157+ computed_field,
158+ value,
159+ include : next_include,
160+ exclude : next_exclude,
161+ field_extra,
162+ } ) ?;
107163 }
108164 Ok ( ( ) )
109165 }
110166}
111167
168+ struct ComputedFieldToSerialize < ' a , ' py > {
169+ computed_field : & ' a ComputedField ,
170+ value : Bound < ' py , PyAny > ,
171+ include : Option < Bound < ' py , PyAny > > ,
172+ exclude : Option < Bound < ' py , PyAny > > ,
173+ field_extra : Extra < ' a > ,
174+ }
175+
112176#[ derive( Debug ) ]
113177struct ComputedField {
114178 property_name : String ,
@@ -143,44 +207,6 @@ impl ComputedField {
143207 serialize_by_alias : config. get_as ( intern ! ( py, "serialize_by_alias" ) ) ?,
144208 } )
145209 }
146-
147- fn to_python (
148- & self ,
149- model : & Bound < ' _ , PyAny > ,
150- output_dict : & Bound < ' _ , PyDict > ,
151- filter : & SchemaFilter < isize > ,
152- include : Option < & Bound < ' _ , PyAny > > ,
153- exclude : Option < & Bound < ' _ , PyAny > > ,
154- extra : & Extra ,
155- ) -> PyResult < ( ) > {
156- let py = model. py ( ) ;
157- let property_name_py = self . property_name_py . bind ( py) ;
158-
159- if let Some ( ( next_include, next_exclude) ) = filter. key_filter ( property_name_py, include, exclude) ? {
160- let next_value = model. getattr ( property_name_py) ?;
161-
162- let value = self
163- . serializer
164- . to_python ( & next_value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , extra) ?;
165- if extra. exclude_none && value. is_none ( py) {
166- return Ok ( ( ) ) ;
167- }
168- let key = match extra. serialize_by_alias_or ( self . serialize_by_alias ) {
169- true => self . alias_py . bind ( py) ,
170- false => property_name_py,
171- } ;
172- output_dict. set_item ( key, value) ?;
173- }
174- Ok ( ( ) )
175- }
176- }
177-
178- pub ( crate ) struct ComputedFieldSerializer < ' py > {
179- model : & ' py Bound < ' py , PyAny > ,
180- computed_field : & ' py ComputedField ,
181- include : Option < & ' py Bound < ' py , PyAny > > ,
182- exclude : Option < & ' py Bound < ' py , PyAny > > ,
183- extra : & ' py Extra < ' py > ,
184210}
185211
186212impl_py_gc_traverse ! ( ComputedField { serializer } ) ;
@@ -190,21 +216,3 @@ impl PyGcTraverse for ComputedFields {
190216 self . 0 . py_gc_traverse ( visit)
191217 }
192218}
193-
194- impl_py_gc_traverse ! ( ComputedFieldSerializer <' _> { computed_field } ) ;
195-
196- impl Serialize for ComputedFieldSerializer < ' _ > {
197- fn serialize < S : serde:: ser:: Serializer > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error > {
198- let py = self . model . py ( ) ;
199- let property_name_py = self . computed_field . property_name_py . bind ( py) ;
200- let next_value = self . model . getattr ( property_name_py) . map_err ( py_err_se_err) ?;
201- let s = PydanticSerializer :: new (
202- & next_value,
203- & self . computed_field . serializer ,
204- self . include ,
205- self . exclude ,
206- self . extra ,
207- ) ;
208- s. serialize ( serializer)
209- }
210- }
0 commit comments