Repository: algorand/pyteal

Issue:
Improve error message in `Tuple.set`
`Tuple.set` currently returns an error if any values given to it do not match the types that the tuple was defined to contain. Right now this error message does not mention any field names or expected/actual types, which is suboptimal, especially for large tuples where it may require a bit of work to diagnose the exact issues.

The current error is raised here: https://github.com/algorand/pyteal/blob/4c716b44819b9d529df98776423e74dac31883b1/pyteal/ast/abi/tuple.py#L354

Discussion:
CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/algorand/pyteal?pullRequest=655) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/algorand/pyteal?pullRequest=655) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/algorand/pyteal?pullRequest=655) it.</sub>
shyba: Thank you for taking the time to review!
Added changelog and the proposed changes.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f42c4d8..f14ea87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 # Unreleased
 
 ## Added
+* Improved error handling for tuple type mismatch: added information on position and expected type. ([#655](https://github.com/algorand/pyteal/pull/655))
 
 ## Fixed
 
diff --git a/pyteal/ast/abi/tuple.py b/pyteal/ast/abi/tuple.py
index 0b1ed00..21782af 100644
--- a/pyteal/ast/abi/tuple.py
+++ b/pyteal/ast/abi/tuple.py
@@ -341,17 +341,18 @@ class Tuple(BaseType):
         if len(values) == 1 and isinstance(values[0], ComputedValue):
             return self._set_with_computed_type(values[0])
 
-        for value in values:
-            if not isinstance(value, BaseType):
-                raise TealInputError(f"Expected BaseType, got {value}")
-
         myTypes = self.type_spec().value_type_specs()
         if len(myTypes) != len(values):
             raise TealInputError(
                 f"Incorrect length for values. Expected {len(myTypes)}, got {len(values)}"
             )
-        if not all(myTypes[i] == values[i].type_spec() for i in range(len(myTypes))):
-            raise TealInputError("Input values do not match type")
+        for index, (value, myType) in enumerate(zip(values, myTypes)):
+            if not isinstance(value, BaseType):
+                raise TealInputError(f"Expected BaseType, got {value}")
+            if myType != value.type_spec():
+                raise TealInputError(
+                    f"Input values do not match type at {index=}: {value.type_spec()} != {myType}"
+                )
         return self._stored_value.store(_encode_tuple(values))
 
     def encode(self) -> Expr:


Test patch:
diff --git a/pyteal/ast/abi/tuple_test.py b/pyteal/ast/abi/tuple_test.py
index b175ce7..db7f280 100644
--- a/pyteal/ast/abi/tuple_test.py
+++ b/pyteal/ast/abi/tuple_test.py
@@ -662,7 +662,7 @@ def test_Tuple_set():
     with pytest.raises(pt.TealInputError):
         tupleValue.set(uint8, uint16, uint32, uint32)
 
-    with pytest.raises(pt.TealInputError):
+    with pytest.raises(pt.TealInputError, match="at index=1: uint32 != uint16"):
         tupleValue.set(uint8, uint32, uint16)
 
     with pytest.raises(pt.TealInputError):