Skip to content

Commit 51ab22b

Browse files
committed
Hetero tuple extended validation
1 parent 544c14b commit 51ab22b

File tree

3 files changed

+36
-9
lines changed

3 files changed

+36
-9
lines changed

src/cattr/converters.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ def _structure_tuple(self, obj, tup: Type[T]):
585585
try:
586586
res.append(conv(e, tup_type))
587587
except Exception as exc:
588-
exc.__note__ = f"Structuring tuple @ index {ix}"
588+
exc.__note__ = f"Structuring {tup} @ index {ix}"
589589
errors.append(exc)
590590
if errors:
591591
raise IterableValidationError(f"While structuring {tup.__name__}", errors, tup)
@@ -594,10 +594,24 @@ def _structure_tuple(self, obj, tup: Type[T]):
594594
return tuple(conv(e, tup_type) for e in obj)
595595
else:
596596
# We're dealing with a heterogenous tuple.
597-
return tuple(
598-
self._structure_func.dispatch(t)(e, t)
599-
for t, e in zip(tup_params, obj)
600-
)
597+
if self.extended_validation:
598+
errors = []
599+
res = []
600+
for ix, (t, e) in enumerate(zip(tup_params, obj)):
601+
try:
602+
conv = self._structure_func.dispatch(t)
603+
res.append(conv(e, t))
604+
except Exception as exc:
605+
exc.__note__ = f"Structuring {tup} @ index {ix}"
606+
errors.append(exc)
607+
if errors:
608+
raise IterableValidationError(f"While structuring {tup.__name__}", errors, tup)
609+
return tuple(res)
610+
else:
611+
return tuple(
612+
(e, t)
613+
for t, e in zip(tup_params, obj)
614+
)
601615

602616
@staticmethod
603617
def _get_dis_func(union):

src/cattrs/gen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ def make_dict_structure_fn(
340340
lines.append(f"{i}except Exception as e:")
341341
i = f"{i} "
342342
lines.append(
343-
f"{i}e.__note__ = 'Structuring class @ attribute {an}'"
343+
f"{i}e.__note__ = 'Structuring class {cl_name} @ attribute {an}'"
344344
)
345345
lines.append(f"{i}errors.append(e)")
346346
post_lines.append(f" if errors: raise __c_cve('While structuring {cl.__name__}', errors, __cl)")

tests/test_validation.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ class Test:
2525
assert repr(exc.value.exceptions[0]) == repr(
2626
ValueError("invalid literal for int() with base 10: 'a'")
2727
)
28-
assert exc.value.exceptions[0].__note__ == "Structuring class @ attribute a"
28+
assert exc.value.exceptions[0].__note__ == "Structuring class Test @ attribute a"
2929

3030
assert repr(exc.value.exceptions[1]) == repr(KeyError("c"))
31-
assert exc.value.exceptions[1].__note__ == "Structuring class @ attribute c"
31+
assert exc.value.exceptions[1].__note__ == "Structuring class Test @ attribute c"
3232

3333

3434
def test_list_validation():
@@ -103,4 +103,17 @@ def test_homo_tuple_validation():
103103
assert repr(exc.value.exceptions[0]) == repr(
104104
ValueError("invalid literal for int() with base 10: 'a'")
105105
)
106-
assert exc.value.exceptions[0].__note__ == "Structuring tuple @ index 2"
106+
assert exc.value.exceptions[0].__note__ == "Structuring typing.Tuple[int, ...] @ index 2"
107+
108+
109+
def test_hetero_tuple_validation():
110+
"""Proper validation errors are raised structuring heterogenous tuples."""
111+
c = GenConverter(extended_validation=True)
112+
113+
with pytest.raises(IterableValidationError) as exc:
114+
c.structure(["1", 2, "a"], Tuple[int, int, int])
115+
116+
assert repr(exc.value.exceptions[0]) == repr(
117+
ValueError("invalid literal for int() with base 10: 'a'")
118+
)
119+
assert exc.value.exceptions[0].__note__ == "Structuring typing.Tuple[int, int, int] @ index 2"

0 commit comments

Comments
 (0)