@@ -55,7 +55,7 @@ class InnerModel:
5555 def __init__ (self , x : str ) -> None :
5656 self .x = x
5757
58- def serialize_inner (v : InnerModel , serializer ) -> str :
58+ def serialize_inner (v : InnerModel , serializer ) -> dict [ str , str ] | str :
5959 v .x = v .x + ' modified'
6060 return serializer (v )
6161
@@ -114,7 +114,7 @@ class InnerModel:
114114 def __init__ (self , x : str ) -> None :
115115 self .x = x
116116
117- def validate_inner (data , validator ) -> str :
117+ def validate_inner (data , validator ) -> InnerModel :
118118 data ['x' ] = data ['x' ] + ' modified'
119119 return validator (data )
120120
@@ -166,3 +166,118 @@ def __init__(self, inner: InnerModel) -> None:
166166 # but the outer model doesn't reuse the custom wrap validator function, so we see simple str val
167167 result_outer = outer_validator .validate_python ({'inner' : {'x' : 'hello' }})
168168 assert result_outer .inner .x == 'hello'
169+
170+
171+ def test_reuse_plain_serializer_ok () -> None :
172+ class InnerModel :
173+ x : str
174+
175+ def __init__ (self , x : str ) -> None :
176+ self .x = x
177+
178+ def serialize_inner (v : InnerModel ) -> str :
179+ return v .x + ' modified'
180+
181+ inner_schema = core_schema .model_schema (
182+ InnerModel ,
183+ schema = core_schema .model_fields_schema (
184+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
185+ ),
186+ serialization = core_schema .plain_serializer_function_ser_schema (serialize_inner ),
187+ )
188+
189+ inner_schema_serializer = SchemaSerializer (inner_schema )
190+ InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
191+ InnerModel .__pydantic_serializer__ = inner_schema_serializer # pyright: ignore[reportAttributeAccessIssue]
192+
193+ class OuterModel :
194+ inner : InnerModel
195+
196+ def __init__ (self , inner : InnerModel ) -> None :
197+ self .inner = inner
198+
199+ outer_schema = core_schema .model_schema (
200+ OuterModel ,
201+ schema = core_schema .model_fields_schema (
202+ {
203+ 'inner' : core_schema .model_field (
204+ schema = core_schema .model_schema (
205+ InnerModel ,
206+ schema = core_schema .model_fields_schema (
207+ # note, we use a simple str schema (with no custom serialization)
208+ # in order to verify that the prebuilt serializer from InnerModel is used instead
209+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
210+ ),
211+ )
212+ )
213+ }
214+ ),
215+ )
216+
217+ inner_serializer = SchemaSerializer (inner_schema )
218+ outer_serializer = SchemaSerializer (outer_schema )
219+
220+ # the custom serialization function does apply for the inner model
221+ inner_instance = InnerModel (x = 'hello' )
222+ assert inner_serializer .to_python (inner_instance ) == 'hello modified'
223+ assert 'FunctionPlainSerializer' in repr (inner_serializer )
224+
225+ # the custom ser function applies for the outer model as well, a plain serializer is permitted as a prebuilt candidate
226+ outer_instance = OuterModel (inner = InnerModel (x = 'hello' ))
227+ assert outer_serializer .to_python (outer_instance ) == {'inner' : 'hello modified' }
228+ assert 'PrebuiltSerializer' in repr (outer_serializer )
229+
230+
231+ def test_reuse_plain_validator_ok () -> None :
232+ class InnerModel :
233+ x : str
234+
235+ def __init__ (self , x : str ) -> None :
236+ self .x = x
237+
238+ def validate_inner (data ) -> InnerModel :
239+ data ['x' ] = data ['x' ] + ' modified'
240+ return InnerModel (** data )
241+
242+ inner_schema = core_schema .no_info_plain_validator_function (validate_inner )
243+
244+ inner_schema_validator = SchemaValidator (inner_schema )
245+ InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
246+ InnerModel .__pydantic_validator__ = inner_schema_validator # pyright: ignore[reportAttributeAccessIssue]
247+
248+ class OuterModel :
249+ inner : InnerModel
250+
251+ def __init__ (self , inner : InnerModel ) -> None :
252+ self .inner = inner
253+
254+ outer_schema = core_schema .model_schema (
255+ OuterModel ,
256+ schema = core_schema .model_fields_schema (
257+ {
258+ 'inner' : core_schema .model_field (
259+ schema = core_schema .model_schema (
260+ InnerModel ,
261+ schema = core_schema .model_fields_schema (
262+ # note, we use a simple str schema (with no custom validation)
263+ # in order to verify that the prebuilt validator from InnerModel is used instead
264+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
265+ ),
266+ )
267+ )
268+ }
269+ ),
270+ )
271+
272+ inner_validator = SchemaValidator (inner_schema )
273+ outer_validator = SchemaValidator (outer_schema )
274+
275+ # the custom validation function does apply for the inner model
276+ result_inner = inner_validator .validate_python ({'x' : 'hello' })
277+ assert result_inner .x == 'hello modified'
278+ assert 'FunctionPlainValidator' in repr (inner_validator )
279+
280+ # the custom validation function does apply for the outer model as well, a plain validator is permitted as a prebuilt candidate
281+ result_outer = outer_validator .validate_python ({'inner' : {'x' : 'hello' }})
282+ assert result_outer .inner .x == 'hello modified'
283+ assert 'PrebuiltValidator' in repr (outer_validator )
0 commit comments