Skip to content

Commit a3480ec

Browse files
authored
[3.11] pythongh-99103: Normalize specialized traceback anchors against the current line (python#99423)
[3.11] pythongh-99103: Normalize specialized traceback anchors against the current line (pythonGH-99145) Automerge-Triggered-By: GH:isidentical. (cherry picked from commit 57be545) Co-authored-by: Batuhan Taskaya <[email protected]>
1 parent 2d5f4ba commit a3480ec

File tree

4 files changed

+51
-5
lines changed

4 files changed

+51
-5
lines changed

Lib/test/test_traceback.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,23 @@ def f_with_binary_operator():
532532
result_lines = self.get_exception(f_with_binary_operator)
533533
self.assertEqual(result_lines, expected_error.splitlines())
534534

535+
def test_caret_for_binary_operators_with_unicode(self):
536+
def f_with_binary_operator():
537+
áóí = 20
538+
return 10 + áóí / 0 + 30
539+
540+
lineno_f = f_with_binary_operator.__code__.co_firstlineno
541+
expected_error = (
542+
'Traceback (most recent call last):\n'
543+
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
544+
' callable()\n'
545+
f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n'
546+
' return 10 + áóí / 0 + 30\n'
547+
' ~~~~^~~\n'
548+
)
549+
result_lines = self.get_exception(f_with_binary_operator)
550+
self.assertEqual(result_lines, expected_error.splitlines())
551+
535552
def test_caret_for_binary_operators_two_char(self):
536553
def f_with_binary_operator():
537554
divisor = 20
@@ -566,6 +583,23 @@ def f_with_subscript():
566583
result_lines = self.get_exception(f_with_subscript)
567584
self.assertEqual(result_lines, expected_error.splitlines())
568585

586+
def test_caret_for_subscript_unicode(self):
587+
def f_with_subscript():
588+
some_dict = {'ó': {'á': {'í': {'theta': 1}}}}
589+
return some_dict['ó']['á']['í']['beta']
590+
591+
lineno_f = f_with_subscript.__code__.co_firstlineno
592+
expected_error = (
593+
'Traceback (most recent call last):\n'
594+
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
595+
' callable()\n'
596+
f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n'
597+
" return some_dict['ó']['á']['í']['beta']\n"
598+
' ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^\n'
599+
)
600+
result_lines = self.get_exception(f_with_subscript)
601+
self.assertEqual(result_lines, expected_error.splitlines())
602+
569603
def test_traceback_specialization_with_syntax_error(self):
570604
bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec")
571605

Lib/traceback.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,12 +585,15 @@ def _extract_caret_anchors_from_line_segment(segment):
585585
if len(tree.body) != 1:
586586
return None
587587

588+
normalize = lambda offset: _byte_offset_to_character_offset(segment, offset)
588589
statement = tree.body[0]
589590
match statement:
590591
case ast.Expr(expr):
591592
match expr:
592593
case ast.BinOp():
593-
operator_str = segment[expr.left.end_col_offset:expr.right.col_offset]
594+
operator_start = normalize(expr.left.end_col_offset)
595+
operator_end = normalize(expr.right.col_offset)
596+
operator_str = segment[operator_start:operator_end]
594597
operator_offset = len(operator_str) - len(operator_str.lstrip())
595598

596599
left_anchor = expr.left.end_col_offset + operator_offset
@@ -600,9 +603,11 @@ def _extract_caret_anchors_from_line_segment(segment):
600603
and not operator_str[operator_offset + 1].isspace()
601604
):
602605
right_anchor += 1
603-
return _Anchors(left_anchor, right_anchor)
606+
return _Anchors(normalize(left_anchor), normalize(right_anchor))
604607
case ast.Subscript():
605-
return _Anchors(expr.value.end_col_offset, expr.slice.end_col_offset + 1)
608+
subscript_start = normalize(expr.value.end_col_offset)
609+
subscript_end = normalize(expr.slice.end_col_offset + 1)
610+
return _Anchors(subscript_start, subscript_end)
606611

607612
return None
608613

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the error reporting positions of specialized traceback anchors when the
2+
source line contains Unicode characters.

Python/traceback.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,8 +705,13 @@ extract_anchors_from_line(PyObject *filename, PyObject *line,
705705

706706
done:
707707
if (res > 0) {
708-
*left_anchor += start_offset;
709-
*right_anchor += start_offset;
708+
// Normalize the AST offsets to byte offsets and adjust them with the
709+
// start of the actual line (instead of the source code segment).
710+
assert(segment != NULL);
711+
assert(*left_anchor >= 0);
712+
assert(*right_anchor >= 0);
713+
*left_anchor = _PyPegen_byte_offset_to_character_offset(segment, *left_anchor) + start_offset;
714+
*right_anchor = _PyPegen_byte_offset_to_character_offset(segment, *right_anchor) + start_offset;
710715
}
711716
Py_XDECREF(segment);
712717
if (arena) {

0 commit comments

Comments
 (0)