@@ -1358,3 +1358,83 @@ class Model:
1358
1358
1359
1359
assert isinstance (validator .validate_python ({'x' : {'foo' : 'foo' }}).x , Foo )
1360
1360
assert isinstance (validator .validate_python ({'x' : {'bar' : 'bar' }}).x , Bar )
1361
+
1362
+
1363
+ def test_smart_union_wrap_validator_should_not_change_nested_model_field_counts () -> None :
1364
+ """Adding a wrap validator on a union member should not affect smart union behavior"""
1365
+
1366
+ class SubModel :
1367
+ x : str = 'x'
1368
+
1369
+ class ModelA :
1370
+ type : str = 'A'
1371
+ sub : SubModel
1372
+
1373
+ class ModelB :
1374
+ type : str = 'B'
1375
+ sub : SubModel
1376
+
1377
+ submodel_schema = core_schema .model_schema (
1378
+ SubModel ,
1379
+ core_schema .model_fields_schema (fields = {'x' : core_schema .model_field (core_schema .str_schema ())}),
1380
+ )
1381
+
1382
+ wrapped_submodel_schema = core_schema .no_info_wrap_validator_function (
1383
+ lambda v , handler : handler (v ), submodel_schema
1384
+ )
1385
+
1386
+ model_a_schema = core_schema .model_schema (
1387
+ ModelA ,
1388
+ core_schema .model_fields_schema (
1389
+ fields = {
1390
+ 'type' : core_schema .model_field (
1391
+ core_schema .with_default_schema (core_schema .literal_schema (['A' ]), default = 'A' ),
1392
+ ),
1393
+ 'sub' : core_schema .model_field (wrapped_submodel_schema ),
1394
+ },
1395
+ ),
1396
+ )
1397
+
1398
+ model_b_schema = core_schema .model_schema (
1399
+ ModelB ,
1400
+ core_schema .model_fields_schema (
1401
+ fields = {
1402
+ 'type' : core_schema .model_field (
1403
+ core_schema .with_default_schema (core_schema .literal_schema (['B' ]), default = 'B' ),
1404
+ ),
1405
+ 'sub' : core_schema .model_field (submodel_schema ),
1406
+ },
1407
+ ),
1408
+ )
1409
+
1410
+ for choices in permute_choices ([model_a_schema , model_b_schema ]):
1411
+ schema = core_schema .union_schema (choices )
1412
+ validator = SchemaValidator (schema )
1413
+
1414
+ assert isinstance (validator .validate_python ({'type' : 'A' , 'sub' : {'x' : 'x' }}), ModelA )
1415
+ assert isinstance (validator .validate_python ({'type' : 'B' , 'sub' : {'x' : 'x' }}), ModelB )
1416
+
1417
+ # defaults to leftmost choice if there's a tie
1418
+ assert isinstance (validator .validate_python ({'sub' : {'x' : 'x' }}), choices [0 ]['cls' ])
1419
+
1420
+ # test validate_assignment
1421
+ class RootModel :
1422
+ ab : Union [ModelA , ModelB ]
1423
+
1424
+ root_model = core_schema .model_schema (
1425
+ RootModel ,
1426
+ core_schema .model_fields_schema (
1427
+ fields = {'ab' : core_schema .model_field (core_schema .union_schema ([model_a_schema , model_b_schema ]))}
1428
+ ),
1429
+ )
1430
+
1431
+ validator = SchemaValidator (root_model )
1432
+ m = validator .validate_python ({'ab' : {'type' : 'B' , 'sub' : {'x' : 'x' }}})
1433
+ assert isinstance (m , RootModel )
1434
+ assert isinstance (m .ab , ModelB )
1435
+ assert m .ab .sub .x == 'x'
1436
+
1437
+ m = validator .validate_assignment (m , 'ab' , {'sub' : {'x' : 'y' }})
1438
+ assert isinstance (m , RootModel )
1439
+ assert isinstance (m .ab , ModelA )
1440
+ assert m .ab .sub .x == 'y'
0 commit comments