Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Lib/test/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2835,6 +2835,7 @@ def test_unicode(): """
Traceback (most recent call last):
File ...
exec(compile(example.source, filename, "single",
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<doctest foo-bär@baz[0]>", line 1, in <module>
raise Exception('clé')
^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ def f_with_multiline():
' ^^^^^^^^^^\n'
f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n'
' raise ValueError(\n'
' ^^^^^^^^^^^^^^^^^'
)
result_lines = self.get_exception(f_with_multiline)
self.assertEqual(result_lines, expected_f.splitlines())
Expand Down
18 changes: 10 additions & 8 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import itertools
import linecache
import sys
from contextlib import suppress

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

stripped_characters = len(frame._original_line) - len(frame.line.lstrip())
if (
frame.end_lineno == frame.lineno
and frame.colno is not None
frame.colno is not None
and frame.end_colno is not None
):
colno = _byte_offset_to_character_offset(frame._original_line, frame.colno)
end_colno = _byte_offset_to_character_offset(frame._original_line, frame.end_colno)

try:
anchors = _extract_caret_anchors_from_line_segment(
frame._original_line[colno - 1:end_colno - 1]
)
except Exception:
anchors = None
anchors = None
if frame.lineno == frame.end_lineno:
with suppress(Exception):
anchors = _extract_caret_anchors_from_line_segment(
frame._original_line[colno - 1:end_colno - 1]
)
else:
end_colno = stripped_characters + len(frame.line.strip())

row.append(' ')
row.append(' ' * (colno - stripped_characters))
Expand Down
37 changes: 28 additions & 9 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,11 +720,11 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
&end_line, &end_col_byte_offset)) {
goto done;
}
if (start_line != end_line) {
goto done;
}

if (start_col_byte_offset < 0 || end_col_byte_offset < 0) {
if (start_line < 0 || end_line < 0
|| start_col_byte_offset < 0
|| end_col_byte_offset < 0)
{
goto done;
}

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

int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset,
&left_end_offset, &right_start_offset,
&primary_error_char, &secondary_error_char);
if (res < 0 && ignore_source_errors() < 0) {
goto done;
if (start_line == end_line) {
int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset,
&left_end_offset, &right_start_offset,
&primary_error_char, &secondary_error_char);
if (res < 0 && ignore_source_errors() < 0) {
goto done;
}
}
else {
// If this is a multi-line expression, then we will highlight until
// the last non-whitespace character.
const char *source_line_str = PyUnicode_AsUTF8(source_line);
if (!source_line_str) {
goto done;
}

Py_ssize_t i = PyUnicode_GET_LENGTH(source_line);
while (--i >= 0) {
if (!IS_WHITESPACE(source_line_str[i])) {
break;
}
}

end_offset = i + 1;
}

err = print_error_location_carets(f, truncation, start_offset, end_offset,
Expand Down