Skip to content

Commit 8d8136f

Browse files
committed
bpo-43950: support multi-line expressions if the intruction covers the rest of the line
1 parent 4f5980a commit 8d8136f

File tree

4 files changed

+40
-17
lines changed

4 files changed

+40
-17
lines changed

Lib/test/test_doctest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2835,6 +2835,7 @@ def test_unicode(): """
28352835
Traceback (most recent call last):
28362836
File ...
28372837
exec(compile(example.source, filename, "single",
2838+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28382839
File "<doctest foo-bär@baz[0]>", line 1, in <module>
28392840
raise Exception('clé')
28402841
^^^^^^^^^^^^^^^^^^^^^^

Lib/test/test_traceback.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ def f_with_multiline():
429429
' ^^^^^^^^^^\n'
430430
f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n'
431431
' raise ValueError(\n'
432+
' ^^^^^^^^^^^^^^^^^'
432433
)
433434
result_lines = self.get_exception(f_with_multiline)
434435
self.assertEqual(result_lines, expected_f.splitlines())

Lib/traceback.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import itertools
55
import linecache
66
import sys
7+
from contextlib import suppress
78

89
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
910
'format_exception_only', 'format_list', 'format_stack',
@@ -463,19 +464,20 @@ def format_frame(self, frame):
463464

464465
stripped_characters = len(frame._original_line) - len(frame.line.lstrip())
465466
if (
466-
frame.end_lineno == frame.lineno
467-
and frame.colno is not None
467+
frame.colno is not None
468468
and frame.end_colno is not None
469469
):
470470
colno = _byte_offset_to_character_offset(frame._original_line, frame.colno)
471471
end_colno = _byte_offset_to_character_offset(frame._original_line, frame.end_colno)
472472

473-
try:
474-
anchors = _extract_caret_anchors_from_line_segment(
475-
frame._original_line[colno - 1:end_colno - 1]
476-
)
477-
except Exception:
478-
anchors = None
473+
anchors = None
474+
if frame.lineno == frame.end_lineno:
475+
with suppress(Exception):
476+
anchors = _extract_caret_anchors_from_line_segment(
477+
frame._original_line[colno - 1:end_colno - 1]
478+
)
479+
else:
480+
end_colno = stripped_characters + len(frame.line.strip())
479481

480482
row.append(' ')
481483
row.append(' ' * (colno - stripped_characters))

Python/traceback.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -720,11 +720,11 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
720720
&end_line, &end_col_byte_offset)) {
721721
goto done;
722722
}
723-
if (start_line != end_line) {
724-
goto done;
725-
}
726723

727-
if (start_col_byte_offset < 0 || end_col_byte_offset < 0) {
724+
if (start_line < 0 || end_line < 0
725+
|| start_col_byte_offset < 0
726+
|| end_col_byte_offset < 0)
727+
{
728728
goto done;
729729
}
730730

@@ -762,11 +762,30 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
762762
char *primary_error_char = "^";
763763
char *secondary_error_char = primary_error_char;
764764

765-
int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset,
766-
&left_end_offset, &right_start_offset,
767-
&primary_error_char, &secondary_error_char);
768-
if (res < 0 && ignore_source_errors() < 0) {
769-
goto done;
765+
if (start_line == end_line) {
766+
int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset,
767+
&left_end_offset, &right_start_offset,
768+
&primary_error_char, &secondary_error_char);
769+
if (res < 0 && ignore_source_errors() < 0) {
770+
goto done;
771+
}
772+
}
773+
else {
774+
// If this is a multi-line expression, then we will highlight until
775+
// the last non-whitespace character.
776+
const char *source_line_str = PyUnicode_AsUTF8(source_line);
777+
if (!source_line_str) {
778+
goto done;
779+
}
780+
781+
Py_ssize_t i = PyUnicode_GET_LENGTH(source_line);
782+
while (--i >= 0) {
783+
if (!IS_WHITESPACE(source_line_str[i])) {
784+
break;
785+
}
786+
}
787+
788+
end_offset = i + 1;
770789
}
771790

772791
err = print_error_location_carets(f, truncation, start_offset, end_offset,

0 commit comments

Comments
 (0)