@@ -14,11 +14,11 @@ class InnerModel:
1414 ),
1515 )
1616
17- inner_schema_validator = SchemaValidator (inner_schema )
18- inner_schema_serializer = SchemaSerializer (inner_schema )
17+ inner_validator = SchemaValidator (inner_schema )
18+ inner_serializer = SchemaSerializer (inner_schema )
1919 InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
20- InnerModel .__pydantic_validator__ = inner_schema_validator # pyright: ignore[reportAttributeAccessIssue]
21- InnerModel .__pydantic_serializer__ = inner_schema_serializer # pyright: ignore[reportAttributeAccessIssue]
20+ InnerModel .__pydantic_validator__ = inner_validator # pyright: ignore[reportAttributeAccessIssue]
21+ InnerModel .__pydantic_serializer__ = inner_serializer # pyright: ignore[reportAttributeAccessIssue]
2222
2323 class OuterModel :
2424 inner : InnerModel
@@ -69,9 +69,9 @@ def serialize_inner(v: InnerModel, serializer) -> Union[dict[str, str], str]:
6969 serialization = core_schema .wrap_serializer_function_ser_schema (serialize_inner ),
7070 )
7171
72- inner_schema_serializer = SchemaSerializer (inner_schema )
72+ inner_serializer = SchemaSerializer (inner_schema )
7373 InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
74- InnerModel .__pydantic_serializer__ = inner_schema_serializer # pyright: ignore[reportAttributeAccessIssue]
74+ InnerModel .__pydantic_serializer__ = inner_serializer # pyright: ignore[reportAttributeAccessIssue]
7575
7676 class OuterModel :
7777 inner : InnerModel
@@ -97,7 +97,6 @@ def __init__(self, inner: InnerModel) -> None:
9797 ),
9898 )
9999
100- inner_serializer = SchemaSerializer (inner_schema )
101100 outer_serializer = SchemaSerializer (outer_schema )
102101
103102 # the custom serialization function does apply for the inner model
@@ -130,9 +129,9 @@ def validate_inner(data, validator) -> InnerModel:
130129 ),
131130 )
132131
133- inner_schema_validator = SchemaValidator (inner_schema )
132+ inner_validator = SchemaValidator (inner_schema )
134133 InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
135- InnerModel .__pydantic_validator__ = inner_schema_validator # pyright: ignore[reportAttributeAccessIssue]
134+ InnerModel .__pydantic_validator__ = inner_validator # pyright: ignore[reportAttributeAccessIssue]
136135
137136 class OuterModel :
138137 inner : InnerModel
@@ -158,7 +157,6 @@ def __init__(self, inner: InnerModel) -> None:
158157 ),
159158 )
160159
161- inner_validator = SchemaValidator (inner_schema )
162160 outer_validator = SchemaValidator (outer_schema )
163161
164162 # the custom validation function does apply for the inner model
@@ -170,6 +168,66 @@ def __init__(self, inner: InnerModel) -> None:
170168 assert result_outer .inner .x == 'hello'
171169
172170
171+ def test_prebuilt_not_used_for_after_validator_functions () -> None :
172+ class InnerModel :
173+ x : str
174+
175+ def __init__ (self , x : str ) -> None :
176+ self .x = x
177+
178+ def validate_after (self ) -> InnerModel :
179+ self .x = self .x + ' modified'
180+ return self
181+
182+ inner_schema = core_schema .no_info_after_validator_function (
183+ validate_after ,
184+ core_schema .model_schema (
185+ InnerModel ,
186+ schema = core_schema .model_fields_schema (
187+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
188+ ),
189+ ),
190+ )
191+
192+ inner_validator = SchemaValidator (inner_schema )
193+ InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
194+ InnerModel .__pydantic_validator__ = inner_validator # pyright: ignore[reportAttributeAccessIssue]
195+
196+ class OuterModel :
197+ inner : InnerModel
198+
199+ def __init__ (self , inner : InnerModel ) -> None :
200+ self .inner = inner
201+
202+ outer_schema = core_schema .model_schema (
203+ OuterModel ,
204+ schema = core_schema .model_fields_schema (
205+ {
206+ 'inner' : core_schema .model_field (
207+ schema = core_schema .model_schema (
208+ InnerModel ,
209+ schema = core_schema .model_fields_schema (
210+ # note, we use a simple str schema (with no custom validation)
211+ # in order to verify that the prebuilt validator from InnerModel is not used
212+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
213+ ),
214+ )
215+ )
216+ }
217+ ),
218+ )
219+
220+ outer_validator = SchemaValidator (outer_schema )
221+
222+ # the custom validation function does apply for the inner model
223+ result_inner = inner_validator .validate_python ({'x' : 'hello' })
224+ assert result_inner .x == 'hello modified'
225+
226+ # but the outer model doesn't reuse the custom after validator function, so we see simple str val
227+ result_outer = outer_validator .validate_python ({'inner' : {'x' : 'hello' }})
228+ assert result_outer .inner .x == 'hello'
229+
230+
173231def test_reuse_plain_serializer_ok () -> None :
174232 class InnerModel :
175233 x : str
@@ -188,9 +246,9 @@ def serialize_inner(v: InnerModel) -> str:
188246 serialization = core_schema .plain_serializer_function_ser_schema (serialize_inner ),
189247 )
190248
191- inner_schema_serializer = SchemaSerializer (inner_schema )
249+ inner_serializer = SchemaSerializer (inner_schema )
192250 InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
193- InnerModel .__pydantic_serializer__ = inner_schema_serializer # pyright: ignore[reportAttributeAccessIssue]
251+ InnerModel .__pydantic_serializer__ = inner_serializer # pyright: ignore[reportAttributeAccessIssue]
194252
195253 class OuterModel :
196254 inner : InnerModel
@@ -216,7 +274,6 @@ def __init__(self, inner: InnerModel) -> None:
216274 ),
217275 )
218276
219- inner_serializer = SchemaSerializer (inner_schema )
220277 outer_serializer = SchemaSerializer (outer_schema )
221278
222279 # the custom serialization function does apply for the inner model
@@ -243,9 +300,9 @@ def validate_inner(data) -> InnerModel:
243300
244301 inner_schema = core_schema .no_info_plain_validator_function (validate_inner )
245302
246- inner_schema_validator = SchemaValidator (inner_schema )
303+ inner_validator = SchemaValidator (inner_schema )
247304 InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
248- InnerModel .__pydantic_validator__ = inner_schema_validator # pyright: ignore[reportAttributeAccessIssue]
305+ InnerModel .__pydantic_validator__ = inner_validator # pyright: ignore[reportAttributeAccessIssue]
249306
250307 class OuterModel :
251308 inner : InnerModel
@@ -271,7 +328,6 @@ def __init__(self, inner: InnerModel) -> None:
271328 ),
272329 )
273330
274- inner_validator = SchemaValidator (inner_schema )
275331 outer_validator = SchemaValidator (outer_schema )
276332
277333 # the custom validation function does apply for the inner model
@@ -283,3 +339,67 @@ def __init__(self, inner: InnerModel) -> None:
283339 result_outer = outer_validator .validate_python ({'inner' : {'x' : 'hello' }})
284340 assert result_outer .inner .x == 'hello modified'
285341 assert 'PrebuiltValidator' in repr (outer_validator )
342+
343+
344+ def test_reuse_before_validator_ok () -> None :
345+ class InnerModel :
346+ x : str
347+
348+ def __init__ (self , x : str ) -> None :
349+ self .x = x
350+
351+ def validate_before (data ) -> dict :
352+ data ['x' ] = data ['x' ] + ' modified'
353+ return data
354+
355+ inner_schema = core_schema .no_info_before_validator_function (
356+ validate_before ,
357+ core_schema .model_schema (
358+ InnerModel ,
359+ schema = core_schema .model_fields_schema (
360+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
361+ ),
362+ ),
363+ )
364+
365+ inner_validator = SchemaValidator (inner_schema )
366+ InnerModel .__pydantic_complete__ = True # pyright: ignore[reportAttributeAccessIssue]
367+ InnerModel .__pydantic_validator__ = inner_validator # pyright: ignore[reportAttributeAccessIssue]
368+
369+ class OuterModel :
370+ inner : InnerModel
371+
372+ def __init__ (self , inner : InnerModel ) -> None :
373+ self .inner = inner
374+
375+ outer_schema = core_schema .model_schema (
376+ OuterModel ,
377+ schema = core_schema .model_fields_schema (
378+ {
379+ 'inner' : core_schema .model_field (
380+ schema = core_schema .model_schema (
381+ InnerModel ,
382+ schema = core_schema .model_fields_schema (
383+ # note, we use a simple str schema (with no custom validation)
384+ # in order to verify that the prebuilt validator from InnerModel is used instead
385+ {'x' : core_schema .model_field (schema = core_schema .str_schema ())},
386+ ),
387+ )
388+ )
389+ }
390+ ),
391+ )
392+
393+ outer_validator = SchemaValidator (outer_schema )
394+ print (inner_validator )
395+ print (outer_validator )
396+
397+ # the custom validation function does apply for the inner model
398+ result_inner = inner_validator .validate_python ({'x' : 'hello' })
399+ assert result_inner .x == 'hello modified'
400+ assert 'FunctionBeforeValidator' in repr (inner_validator )
401+
402+ # the custom validation function does apply for the outer model as well, a before validator is permitted as a prebuilt candidate
403+ result_outer = outer_validator .validate_python ({'inner' : {'x' : 'hello' }})
404+ assert result_outer .inner .x == 'hello modified'
405+ assert 'PrebuiltValidator' in repr (outer_validator )
0 commit comments