diff options
-rw-r--r-- | Lib/test/test_traceback.py | 36 | ||||
-rw-r--r-- | Lib/traceback.py | 16 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst | 2 | ||||
-rw-r--r-- | Python/traceback.c | 17 |
4 files changed, 68 insertions, 3 deletions
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 316ade2..be81082 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -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") diff --git a/Lib/traceback.py b/Lib/traceback.py index 354754b..67941ff 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -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 index 0000000..792bbc4 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst @@ -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 diff --git a/Python/traceback.c b/Python/traceback.c index 2fcfa7c..a75b783 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -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 = "~"; |