]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-106922: Fix error location for constructs with spaces and parentheses (#108959)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Fri, 8 Sep 2023 16:18:35 +0000 (17:18 +0100)
committerGitHub <noreply@github.com>
Fri, 8 Sep 2023 16:18:35 +0000 (17:18 +0100)
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 316ade2171e94fbf278f7cf034cd97398ea21804..be81082bb194725b1326633d52c09bde77872ac4 100644 (file)
@@ -596,6 +596,24 @@ class TracebackErrorLocationCaretTestBase:
         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}}
@@ -630,6 +648,24 @@ class TracebackErrorLocationCaretTestBase:
         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 354754b9560a19dad590247c8460c5a782028a4d..67941ff45988c21b1bfffc0bb0d5a845febef8aa 100644 (file)
@@ -608,11 +608,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 2fcfa7ca56c14078bfe929af45ecd028a0d61cd4..a75b7833af4e05b9d22769881fc70f68a423285d 100644 (file)
@@ -616,6 +616,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 = "^";
@@ -626,6 +631,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 = "~";