Repository: Parquery/icontract
Pull request #237 (2021-11-25 10:41:42)

Added representation of subscripts
With this patch, whenever a contract violation occurs, the subscripts
(such as `arr[x]`) will be included in the error message.


Patch:
diff --git a/icontract/_recompute.py b/icontract/_recompute.py
index 2d01b5d..3dbf895 100644
--- a/icontract/_recompute.py
+++ b/icontract/_recompute.py
@@ -718,7 +718,6 @@ class Visitor(ast.NodeVisitor):
             return PLACEHOLDER
 
         result = value[a_slice]
-
         self.recomputed_values[node] = result
         return result
 
diff --git a/icontract/_represent.py b/icontract/_represent.py
index cb7197c..365d45b 100644
--- a/icontract/_represent.py
+++ b/icontract/_represent.py
@@ -154,6 +154,16 @@ class Visitor(ast.NodeVisitor):
 
         self.generic_visit(node=node)
 
+    def visit_Subscript(self, node: ast.Subscript) -> None:
+        """Represent the subscript with its source code."""
+        if node in self._recomputed_values:
+            value = self._recomputed_values[node]
+            text = self._atok.get_text(node)
+
+            self.reprs[text] = value
+
+        self.generic_visit(node=node)
+
 
 def is_lambda(a_function: CallableT) -> bool:
     """


Test patch:
diff --git a/tests/test_represent.py b/tests/test_represent.py
index 973d591..7f37a8b 100644
--- a/tests/test_represent.py
+++ b/tests/test_represent.py
@@ -363,6 +363,7 @@ class TestReprValues(unittest.TestCase):
             textwrap.dedent("""\
                 x > lst[1]:
                 lst was [1, 2, 3]
+                lst[1] was 2
                 x was 1"""), tests.error.wo_mandatory_location(str(violation_error)))
 
     def test_slice(self) -> None:
@@ -383,9 +384,36 @@ class TestReprValues(unittest.TestCase):
             textwrap.dedent("""\
                 x > sum(lst[1:2:1]):
                 lst was [1, 2, 3]
+                lst[1:2:1] was [2]
                 sum(lst[1:2:1]) was 2
                 x was 1"""), tests.error.wo_mandatory_location(str(violation_error)))
 
+    def test_ext_slice(self) -> None:
+        class SomeClass:
+            def __getitem__(self, item: Any) -> Any:
+                return item
+
+            def __repr__(self) -> str:
+                return "<instance of SomeClass>"
+
+        @icontract.require(lambda something: something[1, 2:3] is None)
+        def func(something: SomeClass) -> None:
+            pass
+
+        violation_err = None  # type: Optional[icontract.ViolationError]
+        try:
+            an_instance = SomeClass()
+            func(something=an_instance)
+        except icontract.ViolationError as err:
+            violation_err = err
+
+        self.assertIsNotNone(violation_err)
+        self.assertEqual(
+            textwrap.dedent('''\
+                something[1, 2:3] is None:
+                something was <instance of SomeClass>
+                something[1, 2:3] was (1, slice(2, 3, None))'''), tests.error.wo_mandatory_location(str(violation_err)))
+
     def test_lambda(self) -> None:
         @icontract.require(lambda x: x > (lambda y: y + 4).__call__(y=7))  # type: ignore
         def func(x: int) -> int:
@@ -507,6 +535,7 @@ class TestGeneratorExpr(unittest.TestCase):
                   result_cell = ''
                 layout was [['L', '.', '#'], ['.', '#', '#']]
                 result was ([['', '', ''], ['', '', '']], 0)
+                result[0] was [['', '', ''], ['', '', '']]
                 zip(layout, result[0]) was <zip object at some address>'''), text)
 
 
diff --git a/tests_3_8/test_error.py b/tests_3_8/test_error.py
index d6a8da6..b7e3677 100644
--- a/tests_3_8/test_error.py
+++ b/tests_3_8/test_error.py
@@ -31,6 +31,7 @@ class TestNoneSpecified(unittest.TestCase):
         self.assertEqual(
             textwrap.dedent("""\
                 (y := x + 3, x > 0)[1]:
+                (y := x + 3, x > 0)[1] was False
                 x was -1
                 y was 2"""), tests.error.wo_mandatory_location(str(violation_error)))