@@ -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