summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_traceback.py36
-rw-r--r--Lib/traceback.py16
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst2
-rw-r--r--Python/traceback.c17
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 = "~";