66import pytest
77from attrs import define , fields
88
9- from cattrs import BaseConverter
9+ from cattrs import BaseConverter , transform_error
10+ from cattrs .converters import Converter
11+ from cattrs .errors import IterableValidationError
12+ from cattrs .gen import make_dict_structure_fn
1013from cattrs .strategies import configure_list_from_dict
1114
1215
1316@define
1417class AttrsA :
1518 a : int
16- b : str
19+ b : int
1720
1821
1922@dataclass
2023class DataclassA :
2124 a : int
22- b : str
25+ b : int
2326
2427
2528class TypedDictA (TypedDict ):
2629 a : int
27- b : str
30+ b : int
2831
2932
3033@pytest .mark .parametrize ("cls" , [AttrsA , DataclassA , TypedDictA ])
@@ -33,18 +36,54 @@ def test_simple_roundtrip(
3336):
3437 hook , hook2 = configure_list_from_dict (list [cls ], "a" , converter )
3538
36- structured = [cls (a = 1 , b = "2" ), cls (a = 3 , b = "4" )]
39+ structured = [cls (a = 1 , b = 2 ), cls (a = 3 , b = 4 )]
3740 unstructured = hook2 (structured )
38- assert unstructured == {1 : {"b" : "2" }, 3 : {"b" : "4" }}
41+ assert unstructured == {1 : {"b" : 2 }, 3 : {"b" : 4 }}
3942
4043 assert hook (unstructured ) == structured
4144
4245
4346def test_simple_roundtrip_attrs (converter : BaseConverter ):
4447 hook , hook2 = configure_list_from_dict (list [AttrsA ], fields (AttrsA ).a , converter )
4548
46- structured = [AttrsA (a = 1 , b = "2" ), AttrsA (a = 3 , b = "4" )]
49+ structured = [AttrsA (a = 1 , b = 2 ), AttrsA (a = 3 , b = 4 )]
4750 unstructured = hook2 (structured )
48- assert unstructured == {1 : {"b" : "2" }, 3 : {"b" : "4" }}
51+ assert unstructured == {1 : {"b" : 2 }, 3 : {"b" : 4 }}
4952
5053 assert hook (unstructured ) == structured
54+
55+
56+ def test_validation_errors ():
57+ """
58+ With detailed validation, validation errors should be adjusted for the
59+ extracted keys.
60+ """
61+ conv = Converter (detailed_validation = True )
62+ hook , _ = configure_list_from_dict (list [AttrsA ], "a" , conv )
63+
64+ # Key failure
65+ with pytest .raises (IterableValidationError ) as exc :
66+ hook ({"a" : {"b" : "1" }})
67+
68+ assert transform_error (exc .value ) == [
69+ "invalid value for type, expected int @ $['a']"
70+ ]
71+
72+ # Value failure
73+ with pytest .raises (IterableValidationError ) as exc :
74+ hook ({1 : {"b" : "a" }})
75+
76+ assert transform_error (exc .value ) == [
77+ "invalid value for type, expected int @ $[1].b"
78+ ]
79+
80+ conv .register_structure_hook (
81+ AttrsA , make_dict_structure_fn (AttrsA , conv , _cattrs_forbid_extra_keys = True )
82+ )
83+ hook , _ = configure_list_from_dict (list [AttrsA ], "a" , conv )
84+
85+ # Value failure, not attribute related
86+ with pytest .raises (IterableValidationError ) as exc :
87+ hook ({1 : {"b" : 1 , "c" : 2 }})
88+
89+ assert transform_error (exc .value ) == ["extra fields found (c) @ $[1]" ]
0 commit comments