]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-106922: Fix error location for constructs with spaces and parentheses ...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 8 Sep 2023 16:56:10 +0000 (09:56 -0700)
committerGitHub <noreply@github.com>
Fri, 8 Sep 2023 16:56:10 +0000 (16:56 +0000)
Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst [new file with mode: 0644]
Python/traceback.c

index eadc9c440e32cc85b184263b781318bdcf56a3ab..544b24c01b37d8aaf354dcf40b562c1976911565 100644 (file)
@@ -566,6 +566,24 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
         result_lines = self.get_exception(f_with_binary_operator)
         self.assertEqual(result_lines, expected_error.splitlines())
 
+    def test_caret_for_binary_operators_with_spaces_and_parenthesis(self):
+        def f_with_binary_operator():
+            a = 1
+            b = ""
+            return ( a   )   + b
+
+        lineno_f = f_with_binary_operator.__code__.co_firstlineno
+        expected_error = (
+            'Traceback (most recent call last):\n'
+            f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
+            '    callable()\n'
+            f'  File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n'
+            '    return ( a   )   + b\n'
+            '           ~~~~~~~~~~^~~\n'
+        )
+        result_lines = self.get_exception(f_with_binary_operator)
+        self.assertEqual(result_lines, expected_error.splitlines())
+
     def test_caret_for_subscript(self):
         def f_with_subscript():
             some_dict = {'x': {'y': None}}
@@ -600,6 +618,24 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
         result_lines = self.get_exception(f_with_subscript)
         self.assertEqual(result_lines, expected_error.splitlines())
 
+    def test_caret_for_subscript_with_spaces_and_parenthesis(self):
+        def f_with_binary_operator():
+            a = []
+            b = c = 1
+            return b     [    a  ] + c
+
+        lineno_f = f_with_binary_operator.__code__.co_firstlineno
+        expected_error = (
+            'Traceback (most recent call last):\n'
+            f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
+            '    callable()\n'
+            f'  File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n'
+            '    return b     [    a  ] + c\n'
+            '           ~~~~~~^^^^^^^^^\n'
+        )
+        result_lines = self.get_exception(f_with_binary_operator)
+        self.assertEqual(result_lines, expected_error.splitlines())
+
     def test_traceback_specialization_with_syntax_error(self):
         bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec")
 
index 09950a8b55e33e58003cd09495e1ab4a45f8e180..d3edd3a63efed91e9620d941702326b27d338a8d 100644 (file)
@@ -603,11 +603,21 @@ def _extract_caret_anchors_from_line_segment(segment):
                         and not operator_str[operator_offset + 1].isspace()
                     ):
                         right_anchor += 1
+
+                    while left_anchor < len(segment) and ((ch := segment[left_anchor]).isspace() or ch in ")#"):
+                        left_anchor += 1
+                        right_anchor += 1
                     return _Anchors(normalize(left_anchor), normalize(right_anchor))
                 case ast.Subscript():
-                    subscript_start = normalize(expr.value.end_col_offset)
-                    subscript_end = normalize(expr.slice.end_col_offset + 1)
-                    return _Anchors(subscript_start, subscript_end)
+                    left_anchor = normalize(expr.value.end_col_offset)
+                    right_anchor = normalize(expr.slice.end_col_offset + 1)
+                    while left_anchor < len(segment) and ((ch := segment[left_anchor]).isspace() or ch != "["):
+                        left_anchor += 1
+                    while right_anchor < len(segment) and ((ch := segment[right_anchor]).isspace() or ch != "]"):
+                        right_anchor += 1
+                    if right_anchor < len(segment):
+                        right_anchor += 1
+                    return _Anchors(left_anchor, right_anchor)
 
     return None
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst
new file mode 100644 (file)
index 0000000..792bbc4
--- /dev/null
@@ -0,0 +1,2 @@
+Fix caret placement for error locations for subscript and binary operations
+that involve non-semantic parentheses and spaces. Patch by Pablo Galindo
index 7f47349a27a1ae6cdf427d60251b6205b4cce3b9..7010736ad0c539a848458e0b4f3e2663c359a768 100644 (file)
@@ -621,6 +621,11 @@ extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *lef
                     ++*right_anchor;
                 }
 
+                // Keep going if the current char is not ')'
+                if (i+1 < right->col_offset && (segment_str[i] == ')')) {
+                    continue;
+                }
+
                 // Set the error characters
                 *primary_error_char = "~";
                 *secondary_error_char = "^";
@@ -631,6 +636,18 @@ extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *lef
         case Subscript_kind: {
             *left_anchor = expr->v.Subscript.value->end_col_offset;
             *right_anchor = expr->v.Subscript.slice->end_col_offset + 1;
+            Py_ssize_t str_len = strlen(segment_str);
+
+            // Move right_anchor and left_anchor forward to the first non-whitespace character that is not ']' and '['
+            while (*left_anchor < str_len && (IS_WHITESPACE(segment_str[*left_anchor]) || segment_str[*left_anchor] != '[')) {
+                ++*left_anchor;
+            }
+            while (*right_anchor < str_len && (IS_WHITESPACE(segment_str[*right_anchor]) || segment_str[*right_anchor] != ']')) {
+                ++*right_anchor;
+            }
+            if (*right_anchor < str_len){
+                *right_anchor += 1;
+            }
 
             // Set the error characters
             *primary_error_char = "~";