mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
[3.11] gh-93883: elide traceback indicators when possible (GH-93994) (GH-94740)
Elide traceback column indicators when the entire line of the frame is implicated. This reduces traceback length and draws more attention to the remaining (very relevant) indicators. Example: ``` Traceback (most recent call last): File "query.py", line 99, in <module> bar() File "query.py", line 66, in bar foo() File "query.py", line 37, in foo magic_arithmetic('foo') File "query.py", line 18, in magic_arithmetic return add_counts(x) / 25 ^^^^^^^^^^^^^ File "query.py", line 24, in add_counts return 25 + query_user(user1) + query_user(user2) ^^^^^^^^^^^^^^^^^ File "query.py", line 32, in query_user return 1 + query_count(db, response['a']['b']['c']['user'], retry=True) ~~~~~~~~~~~~~~~~~~^^^^^ TypeError: 'NoneType' object is not subscriptable ``` Automerge-Triggered-By: GH:pablogsal
This commit is contained in:
parent
f3212b1ec7
commit
45896f2a02
9 changed files with 111 additions and 139 deletions
|
@ -464,32 +464,27 @@ The output for the example would look similar to this:
|
||||||
*** print_tb:
|
*** print_tb:
|
||||||
File "<doctest...>", line 10, in <module>
|
File "<doctest...>", line 10, in <module>
|
||||||
lumberjack()
|
lumberjack()
|
||||||
^^^^^^^^^^^^
|
|
||||||
*** print_exception:
|
*** print_exception:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
File "<doctest...>", line 10, in <module>
|
File "<doctest...>", line 10, in <module>
|
||||||
lumberjack()
|
lumberjack()
|
||||||
^^^^^^^^^^^^
|
|
||||||
File "<doctest...>", line 4, in lumberjack
|
File "<doctest...>", line 4, in lumberjack
|
||||||
bright_side_of_death()
|
bright_side_of_death()
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
IndexError: tuple index out of range
|
IndexError: tuple index out of range
|
||||||
*** print_exc:
|
*** print_exc:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
File "<doctest...>", line 10, in <module>
|
File "<doctest...>", line 10, in <module>
|
||||||
lumberjack()
|
lumberjack()
|
||||||
^^^^^^^^^^^^
|
|
||||||
File "<doctest...>", line 4, in lumberjack
|
File "<doctest...>", line 4, in lumberjack
|
||||||
bright_side_of_death()
|
bright_side_of_death()
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
IndexError: tuple index out of range
|
IndexError: tuple index out of range
|
||||||
*** format_exc, first and last line:
|
*** format_exc, first and last line:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
IndexError: tuple index out of range
|
IndexError: tuple index out of range
|
||||||
*** format_exception:
|
*** format_exception:
|
||||||
['Traceback (most recent call last):\n',
|
['Traceback (most recent call last):\n',
|
||||||
' File "<doctest default[0]>", line 10, in <module>\n lumberjack()\n ^^^^^^^^^^^^\n',
|
' File "<doctest default[0]>", line 10, in <module>\n lumberjack()\n',
|
||||||
' File "<doctest default[0]>", line 4, in lumberjack\n bright_side_of_death()\n ^^^^^^^^^^^^^^^^^^^^^^\n',
|
' File "<doctest default[0]>", line 4, in lumberjack\n bright_side_of_death()\n',
|
||||||
' File "<doctest default[0]>", line 7, in bright_side_of_death\n return tuple()[0]\n ~~~~~~~^^^\n',
|
' File "<doctest default[0]>", line 7, in bright_side_of_death\n return tuple()[0]\n ~~~~~~~^^^\n',
|
||||||
'IndexError: tuple index out of range\n']
|
'IndexError: tuple index out of range\n']
|
||||||
*** extract_tb:
|
*** extract_tb:
|
||||||
|
@ -497,8 +492,8 @@ The output for the example would look similar to this:
|
||||||
<FrameSummary file <doctest...>, line 4 in lumberjack>,
|
<FrameSummary file <doctest...>, line 4 in lumberjack>,
|
||||||
<FrameSummary file <doctest...>, line 7 in bright_side_of_death>]
|
<FrameSummary file <doctest...>, line 7 in bright_side_of_death>]
|
||||||
*** format_tb:
|
*** format_tb:
|
||||||
[' File "<doctest default[0]>", line 10, in <module>\n lumberjack()\n ^^^^^^^^^^^^\n',
|
[' File "<doctest default[0]>", line 10, in <module>\n lumberjack()\n',
|
||||||
' File "<doctest default[0]>", line 4, in lumberjack\n bright_side_of_death()\n ^^^^^^^^^^^^^^^^^^^^^^\n',
|
' File "<doctest default[0]>", line 4, in lumberjack\n bright_side_of_death()\n',
|
||||||
' File "<doctest default[0]>", line 7, in bright_side_of_death\n return tuple()[0]\n ~~~~~~~^^^\n']
|
' File "<doctest default[0]>", line 7, in bright_side_of_death\n return tuple()[0]\n ~~~~~~~^^^\n']
|
||||||
*** tb_lineno: 10
|
*** tb_lineno: 10
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,6 @@ when dealing with deeply nested dictionary objects and multiple function calls,
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
File "query.py", line 37, in <module>
|
File "query.py", line 37, in <module>
|
||||||
magic_arithmetic('foo')
|
magic_arithmetic('foo')
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
File "query.py", line 18, in magic_arithmetic
|
File "query.py", line 18, in magic_arithmetic
|
||||||
return add_counts(x) / 25
|
return add_counts(x) / 25
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from idlelib import run
|
from idlelib import run
|
||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
from test.support import captured_output, captured_stderr, has_no_debug_ranges
|
from test.support import captured_output, captured_stderr
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
import idlelib
|
import idlelib
|
||||||
|
@ -33,14 +33,9 @@ class ExceptionTest(unittest.TestCase):
|
||||||
run.print_exception()
|
run.print_exception()
|
||||||
|
|
||||||
tb = output.getvalue().strip().splitlines()
|
tb = output.getvalue().strip().splitlines()
|
||||||
if has_no_debug_ranges():
|
self.assertEqual(11, len(tb))
|
||||||
self.assertEqual(11, len(tb))
|
self.assertIn('UnhashableException: ex2', tb[3])
|
||||||
self.assertIn('UnhashableException: ex2', tb[3])
|
self.assertIn('UnhashableException: ex1', tb[10])
|
||||||
self.assertIn('UnhashableException: ex1', tb[10])
|
|
||||||
else:
|
|
||||||
self.assertEqual(13, len(tb))
|
|
||||||
self.assertIn('UnhashableException: ex2', tb[4])
|
|
||||||
self.assertIn('UnhashableException: ex1', tb[12])
|
|
||||||
|
|
||||||
data = (('1/0', ZeroDivisionError, "division by zero\n"),
|
data = (('1/0', ZeroDivisionError, "division by zero\n"),
|
||||||
('abc', NameError, "name 'abc' is not defined. "
|
('abc', NameError, "name 'abc' is not defined. "
|
||||||
|
|
|
@ -549,10 +549,10 @@ class CmdLineTest(unittest.TestCase):
|
||||||
script_name = _make_test_script(script_dir, 'script', script)
|
script_name = _make_test_script(script_dir, 'script', script)
|
||||||
exitcode, stdout, stderr = assert_python_failure(script_name)
|
exitcode, stdout, stderr = assert_python_failure(script_name)
|
||||||
text = stderr.decode('ascii').split('\n')
|
text = stderr.decode('ascii').split('\n')
|
||||||
self.assertEqual(len(text), 6)
|
self.assertEqual(len(text), 5)
|
||||||
self.assertTrue(text[0].startswith('Traceback'))
|
self.assertTrue(text[0].startswith('Traceback'))
|
||||||
self.assertTrue(text[1].startswith(' File '))
|
self.assertTrue(text[1].startswith(' File '))
|
||||||
self.assertTrue(text[4].startswith('NameError'))
|
self.assertTrue(text[3].startswith('NameError'))
|
||||||
|
|
||||||
def test_non_ascii(self):
|
def test_non_ascii(self):
|
||||||
# Mac OS X denies the creation of a file with an invalid UTF-8 name.
|
# Mac OS X denies the creation of a file with an invalid UTF-8 name.
|
||||||
|
|
|
@ -2854,7 +2854,7 @@ except UnicodeEncodeError:
|
||||||
# Skip the test: the filesystem encoding is unable to encode the filename
|
# Skip the test: the filesystem encoding is unable to encode the filename
|
||||||
supports_unicode = False
|
supports_unicode = False
|
||||||
|
|
||||||
if supports_unicode and not support.has_no_debug_ranges():
|
if supports_unicode:
|
||||||
def test_unicode(): """
|
def test_unicode(): """
|
||||||
Check doctest with a non-ascii filename:
|
Check doctest with a non-ascii filename:
|
||||||
|
|
||||||
|
@ -2876,10 +2876,8 @@ Check doctest with a non-ascii filename:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
File ...
|
File ...
|
||||||
exec(compile(example.source, filename, "single",
|
exec(compile(example.source, filename, "single",
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
File "<doctest foo-bär@baz[0]>", line 1, in <module>
|
File "<doctest foo-bär@baz[0]>", line 1, in <module>
|
||||||
raise Exception('clé')
|
raise Exception('clé')
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
Exception: clé
|
Exception: clé
|
||||||
TestResults(failed=1, attempted=1)
|
TestResults(failed=1, attempted=1)
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -253,7 +253,7 @@ class TracebackCases(unittest.TestCase):
|
||||||
self.assertTrue(stdout[2].endswith(err_line),
|
self.assertTrue(stdout[2].endswith(err_line),
|
||||||
"Invalid traceback line: {0!r} instead of {1!r}".format(
|
"Invalid traceback line: {0!r} instead of {1!r}".format(
|
||||||
stdout[2], err_line))
|
stdout[2], err_line))
|
||||||
actual_err_msg = stdout[3 if has_no_debug_ranges() else 4]
|
actual_err_msg = stdout[3]
|
||||||
self.assertTrue(actual_err_msg == err_msg,
|
self.assertTrue(actual_err_msg == err_msg,
|
||||||
"Invalid error message: {0!r} instead of {1!r}".format(
|
"Invalid error message: {0!r} instead of {1!r}".format(
|
||||||
actual_err_msg, err_msg))
|
actual_err_msg, err_msg))
|
||||||
|
@ -387,18 +387,19 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
callable_line = get_exception.__code__.co_firstlineno + 2
|
callable_line = get_exception.__code__.co_firstlineno + 2
|
||||||
|
|
||||||
def test_basic_caret(self):
|
def test_basic_caret(self):
|
||||||
|
# NOTE: In caret tests, "if True:" is used as a way to force indicator
|
||||||
|
# display, since the raising expression spans only part of the line.
|
||||||
def f():
|
def f():
|
||||||
raise ValueError("basic caret tests")
|
if True: raise ValueError("basic caret tests")
|
||||||
|
|
||||||
lineno_f = f.__code__.co_firstlineno
|
lineno_f = f.__code__.co_firstlineno
|
||||||
expected_f = (
|
expected_f = (
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
||||||
' raise ValueError("basic caret tests")\n'
|
' if True: raise ValueError("basic caret tests")\n'
|
||||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
||||||
)
|
)
|
||||||
result_lines = self.get_exception(f)
|
result_lines = self.get_exception(f)
|
||||||
self.assertEqual(result_lines, expected_f.splitlines())
|
self.assertEqual(result_lines, expected_f.splitlines())
|
||||||
|
@ -407,17 +408,16 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
# Make sure that even if a line contains multi-byte unicode characters
|
# Make sure that even if a line contains multi-byte unicode characters
|
||||||
# the correct carets are printed.
|
# the correct carets are printed.
|
||||||
def f_with_unicode():
|
def f_with_unicode():
|
||||||
raise ValueError("Ĥellö Wörld")
|
if True: raise ValueError("Ĥellö Wörld")
|
||||||
|
|
||||||
lineno_f = f_with_unicode.__code__.co_firstlineno
|
lineno_f = f_with_unicode.__code__.co_firstlineno
|
||||||
expected_f = (
|
expected_f = (
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f_with_unicode\n'
|
f' File "{__file__}", line {lineno_f+1}, in f_with_unicode\n'
|
||||||
' raise ValueError("Ĥellö Wörld")\n'
|
' if True: raise ValueError("Ĥellö Wörld")\n'
|
||||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
||||||
)
|
)
|
||||||
result_lines = self.get_exception(f_with_unicode)
|
result_lines = self.get_exception(f_with_unicode)
|
||||||
self.assertEqual(result_lines, expected_f.splitlines())
|
self.assertEqual(result_lines, expected_f.splitlines())
|
||||||
|
@ -432,7 +432,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f_with_type\n'
|
f' File "{__file__}", line {lineno_f+1}, in f_with_type\n'
|
||||||
' def foo(a: THIS_DOES_NOT_EXIST ) -> int:\n'
|
' def foo(a: THIS_DOES_NOT_EXIST ) -> int:\n'
|
||||||
' ^^^^^^^^^^^^^^^^^^^\n'
|
' ^^^^^^^^^^^^^^^^^^^\n'
|
||||||
|
@ -444,7 +443,7 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
# Make sure no carets are printed for expressions spanning multiple
|
# Make sure no carets are printed for expressions spanning multiple
|
||||||
# lines.
|
# lines.
|
||||||
def f_with_multiline():
|
def f_with_multiline():
|
||||||
raise ValueError(
|
if True: raise ValueError(
|
||||||
"error over multiple lines"
|
"error over multiple lines"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -453,10 +452,9 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n'
|
f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n'
|
||||||
' raise ValueError(\n'
|
' if True: raise ValueError(\n'
|
||||||
' ^^^^^^^^^^^^^^^^^'
|
' ^^^^^^^^^^^^^^^^^'
|
||||||
)
|
)
|
||||||
result_lines = self.get_exception(f_with_multiline)
|
result_lines = self.get_exception(f_with_multiline)
|
||||||
self.assertEqual(result_lines, expected_f.splitlines())
|
self.assertEqual(result_lines, expected_f.splitlines())
|
||||||
|
@ -485,7 +483,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n'
|
f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n'
|
||||||
' return compile(code, "?", "exec")\n'
|
' return compile(code, "?", "exec")\n'
|
||||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
' ^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
||||||
|
@ -502,9 +499,8 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
# lines.
|
# lines.
|
||||||
def f_with_multiline():
|
def f_with_multiline():
|
||||||
return (
|
return (
|
||||||
1 /
|
2 + 1 /
|
||||||
0 +
|
0
|
||||||
2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
lineno_f = f_with_multiline.__code__.co_firstlineno
|
lineno_f = f_with_multiline.__code__.co_firstlineno
|
||||||
|
@ -512,10 +508,9 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n'
|
f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n'
|
||||||
' 1 /\n'
|
' 2 + 1 /\n'
|
||||||
' ^^^'
|
' ^^^'
|
||||||
)
|
)
|
||||||
result_lines = self.get_exception(f_with_multiline)
|
result_lines = self.get_exception(f_with_multiline)
|
||||||
self.assertEqual(result_lines, expected_f.splitlines())
|
self.assertEqual(result_lines, expected_f.splitlines())
|
||||||
|
@ -530,7 +525,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n'
|
f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n'
|
||||||
' return 10 + divisor / 0 + 30\n'
|
' return 10 + divisor / 0 + 30\n'
|
||||||
' ~~~~~~~~^~~\n'
|
' ~~~~~~~~^~~\n'
|
||||||
|
@ -548,7 +542,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n'
|
f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n'
|
||||||
' return 10 + divisor // 0 + 30\n'
|
' return 10 + divisor // 0 + 30\n'
|
||||||
' ~~~~~~~~^^~~\n'
|
' ~~~~~~~~^^~~\n'
|
||||||
|
@ -566,7 +559,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n'
|
f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n'
|
||||||
" return some_dict['x']['y']['z']\n"
|
" return some_dict['x']['y']['z']\n"
|
||||||
' ~~~~~~~~~~~~~~~~~~~^^^^^\n'
|
' ~~~~~~~~~~~~~~~~~~~^^^^^\n'
|
||||||
|
@ -590,7 +582,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{TESTFN}", line {lineno_f}, in <module>\n'
|
f' File "{TESTFN}", line {lineno_f}, in <module>\n'
|
||||||
" 1 $ 0 / 1 / 2\n"
|
" 1 $ 0 / 1 / 2\n"
|
||||||
' ^^^^^\n'
|
' ^^^^^\n'
|
||||||
|
@ -598,7 +589,7 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
self.assertEqual(result_lines, expected_error.splitlines())
|
self.assertEqual(result_lines, expected_error.splitlines())
|
||||||
|
|
||||||
def test_traceback_very_long_line(self):
|
def test_traceback_very_long_line(self):
|
||||||
source = "a" * 256
|
source = "if True: " + "a" * 256
|
||||||
bytecode = compile(source, TESTFN, "exec")
|
bytecode = compile(source, TESTFN, "exec")
|
||||||
|
|
||||||
with open(TESTFN, "w") as file:
|
with open(TESTFN, "w") as file:
|
||||||
|
@ -613,13 +604,54 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{TESTFN}", line {lineno_f}, in <module>\n'
|
f' File "{TESTFN}", line {lineno_f}, in <module>\n'
|
||||||
f' {source}\n'
|
f' {source}\n'
|
||||||
f' {"^"*len(source)}\n'
|
f' {" "*len("if True: ") + "^"*256}\n'
|
||||||
)
|
)
|
||||||
self.assertEqual(result_lines, expected_error.splitlines())
|
self.assertEqual(result_lines, expected_error.splitlines())
|
||||||
|
|
||||||
|
def test_secondary_caret_not_elided(self):
|
||||||
|
# Always show a line's indicators if they include the secondary character.
|
||||||
|
def f_with_subscript():
|
||||||
|
some_dict = {'x': {'y': None}}
|
||||||
|
some_dict['x']['y']['z']
|
||||||
|
|
||||||
|
lineno_f = f_with_subscript.__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+2}, in f_with_subscript\n'
|
||||||
|
" some_dict['x']['y']['z']\n"
|
||||||
|
' ~~~~~~~~~~~~~~~~~~~^^^^^\n'
|
||||||
|
)
|
||||||
|
result_lines = self.get_exception(f_with_subscript)
|
||||||
|
self.assertEqual(result_lines, expected_error.splitlines())
|
||||||
|
|
||||||
|
def test_caret_exception_group(self):
|
||||||
|
# Notably, this covers whether indicators handle margin strings correctly.
|
||||||
|
# (Exception groups use margin strings to display vertical indicators.)
|
||||||
|
# The implementation must account for both "indent" and "margin" offsets.
|
||||||
|
|
||||||
|
def exc():
|
||||||
|
if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])
|
||||||
|
|
||||||
|
expected_error = (
|
||||||
|
f' + Exception Group Traceback (most recent call last):\n'
|
||||||
|
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
|
f' | callable()\n'
|
||||||
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n'
|
||||||
|
f' | if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
|
||||||
|
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
||||||
|
f' | ExceptionGroup: eg (2 sub-exceptions)\n'
|
||||||
|
f' +-+---------------- 1 ----------------\n'
|
||||||
|
f' | ValueError: 1\n'
|
||||||
|
f' +---------------- 2 ----------------\n'
|
||||||
|
f' | TypeError: 2\n')
|
||||||
|
|
||||||
|
result_lines = self.get_exception(exc)
|
||||||
|
self.assertEqual(result_lines, expected_error.splitlines())
|
||||||
|
|
||||||
def assertSpecialized(self, func, expected_specialization):
|
def assertSpecialized(self, func, expected_specialization):
|
||||||
result_lines = self.get_exception(func)
|
result_lines = self.get_exception(func)
|
||||||
specialization_line = result_lines[-1]
|
specialization_line = result_lines[-1]
|
||||||
|
@ -673,13 +705,11 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_applydescs + 1}, in applydecs\n'
|
f' File "{__file__}", line {lineno_applydescs + 1}, in applydecs\n'
|
||||||
' @dec_error\n'
|
' @dec_error\n'
|
||||||
' ^^^^^^^^^\n'
|
' ^^^^^^^^^\n'
|
||||||
f' File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n'
|
f' File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n'
|
||||||
' raise TypeError\n'
|
' raise TypeError\n'
|
||||||
' ^^^^^^^^^^^^^^^\n'
|
|
||||||
)
|
)
|
||||||
self.assertEqual(result_lines, expected_error.splitlines())
|
self.assertEqual(result_lines, expected_error.splitlines())
|
||||||
|
|
||||||
|
@ -693,13 +723,11 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
' callable()\n'
|
' callable()\n'
|
||||||
' ^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_applydescs_class + 1}, in applydecs_class\n'
|
f' File "{__file__}", line {lineno_applydescs_class + 1}, in applydecs_class\n'
|
||||||
' @dec_error\n'
|
' @dec_error\n'
|
||||||
' ^^^^^^^^^\n'
|
' ^^^^^^^^^\n'
|
||||||
f' File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n'
|
f' File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n'
|
||||||
' raise TypeError\n'
|
' raise TypeError\n'
|
||||||
' ^^^^^^^^^^^^^^^\n'
|
|
||||||
)
|
)
|
||||||
self.assertEqual(result_lines, expected_error.splitlines())
|
self.assertEqual(result_lines, expected_error.splitlines())
|
||||||
|
|
||||||
|
@ -713,7 +741,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
f"Traceback (most recent call last):",
|
f"Traceback (most recent call last):",
|
||||||
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
|
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
|
||||||
f" callable()",
|
f" callable()",
|
||||||
f" ^^^^^^^^^^",
|
|
||||||
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
|
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
|
||||||
f" .method",
|
f" .method",
|
||||||
f" ^^^^^^",
|
f" ^^^^^^",
|
||||||
|
@ -730,10 +757,8 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
f"Traceback (most recent call last):",
|
f"Traceback (most recent call last):",
|
||||||
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
|
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
|
||||||
f" callable()",
|
f" callable()",
|
||||||
f" ^^^^^^^^^^",
|
|
||||||
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
|
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
|
||||||
f" method",
|
f" method",
|
||||||
f" ^^^^^^",
|
|
||||||
]
|
]
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
@ -747,7 +772,6 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
|
||||||
f"Traceback (most recent call last):",
|
f"Traceback (most recent call last):",
|
||||||
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
|
f" File \"{__file__}\", line {self.callable_line}, in get_exception",
|
||||||
f" callable()",
|
f" callable()",
|
||||||
f" ^^^^^^^^^^",
|
|
||||||
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
|
f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
|
||||||
f" . method",
|
f" . method",
|
||||||
f" ^^^^^^",
|
f" ^^^^^^",
|
||||||
|
@ -817,12 +841,8 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
# Make sure that the traceback is properly indented.
|
# Make sure that the traceback is properly indented.
|
||||||
tb_lines = python_fmt.splitlines()
|
tb_lines = python_fmt.splitlines()
|
||||||
banner = tb_lines[0]
|
banner = tb_lines[0]
|
||||||
if has_no_debug_ranges():
|
self.assertEqual(len(tb_lines), 5)
|
||||||
self.assertEqual(len(tb_lines), 5)
|
location, source_line = tb_lines[-2], tb_lines[-1]
|
||||||
location, source_line = tb_lines[-2], tb_lines[-1]
|
|
||||||
else:
|
|
||||||
self.assertEqual(len(tb_lines), 7)
|
|
||||||
location, source_line = tb_lines[-3], tb_lines[-2]
|
|
||||||
self.assertTrue(banner.startswith('Traceback'))
|
self.assertTrue(banner.startswith('Traceback'))
|
||||||
self.assertTrue(location.startswith(' File'))
|
self.assertTrue(location.startswith(' File'))
|
||||||
self.assertTrue(source_line.startswith(' raise'))
|
self.assertTrue(source_line.startswith(' raise'))
|
||||||
|
@ -886,16 +906,12 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
|
f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
|
||||||
' f()\n'
|
' f()\n'
|
||||||
' ^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
||||||
' f()\n'
|
' f()\n'
|
||||||
' ^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
||||||
' f()\n'
|
' f()\n'
|
||||||
' ^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
f' File "{__file__}", line {lineno_f+1}, in f\n'
|
||||||
' f()\n'
|
' f()\n'
|
||||||
' ^^^\n'
|
|
||||||
# XXX: The following line changes depending on whether the tests
|
# XXX: The following line changes depending on whether the tests
|
||||||
# are run through the interactive interpreter or with -m
|
# are run through the interactive interpreter or with -m
|
||||||
# It also varies depending on the platform (stack size)
|
# It also varies depending on the platform (stack size)
|
||||||
|
@ -946,14 +962,12 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
' [Previous line repeated 7 more times]\n'
|
' [Previous line repeated 7 more times]\n'
|
||||||
f' File "{__file__}", line {lineno_g+3}, in g\n'
|
f' File "{__file__}", line {lineno_g+3}, in g\n'
|
||||||
' raise ValueError\n'
|
' raise ValueError\n'
|
||||||
' ^^^^^^^^^^^^^^^^\n'
|
|
||||||
'ValueError\n'
|
'ValueError\n'
|
||||||
)
|
)
|
||||||
tb_line = (
|
tb_line = (
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
|
f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
|
||||||
' g()\n'
|
' g()\n'
|
||||||
' ^^^\n'
|
|
||||||
)
|
)
|
||||||
expected = (tb_line + result_g).splitlines()
|
expected = (tb_line + result_g).splitlines()
|
||||||
actual = stderr_g.getvalue().splitlines()
|
actual = stderr_g.getvalue().splitlines()
|
||||||
|
@ -978,7 +992,6 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
|
f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
|
||||||
' h()\n'
|
' h()\n'
|
||||||
' ^^^\n'
|
|
||||||
f' File "{__file__}", line {lineno_h+2}, in h\n'
|
f' File "{__file__}", line {lineno_h+2}, in h\n'
|
||||||
' return h(count-1)\n'
|
' return h(count-1)\n'
|
||||||
' ^^^^^^^^^^\n'
|
' ^^^^^^^^^^\n'
|
||||||
|
@ -991,7 +1004,6 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
' [Previous line repeated 7 more times]\n'
|
' [Previous line repeated 7 more times]\n'
|
||||||
f' File "{__file__}", line {lineno_h+3}, in h\n'
|
f' File "{__file__}", line {lineno_h+3}, in h\n'
|
||||||
' g()\n'
|
' g()\n'
|
||||||
' ^^^\n'
|
|
||||||
)
|
)
|
||||||
expected = (result_h + result_g).splitlines()
|
expected = (result_h + result_g).splitlines()
|
||||||
actual = stderr_h.getvalue().splitlines()
|
actual = stderr_h.getvalue().splitlines()
|
||||||
|
@ -1017,14 +1029,12 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
' ^^^^^^^^^^\n'
|
' ^^^^^^^^^^\n'
|
||||||
f' File "{__file__}", line {lineno_g+3}, in g\n'
|
f' File "{__file__}", line {lineno_g+3}, in g\n'
|
||||||
' raise ValueError\n'
|
' raise ValueError\n'
|
||||||
' ^^^^^^^^^^^^^^^^\n'
|
|
||||||
'ValueError\n'
|
'ValueError\n'
|
||||||
)
|
)
|
||||||
tb_line = (
|
tb_line = (
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {lineno_g+81}, in _check_recursive_traceback_display\n'
|
f' File "{__file__}", line {lineno_g+77}, in _check_recursive_traceback_display\n'
|
||||||
' g(traceback._RECURSIVE_CUTOFF)\n'
|
' g(traceback._RECURSIVE_CUTOFF)\n'
|
||||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
)
|
)
|
||||||
expected = (tb_line + result_g).splitlines()
|
expected = (tb_line + result_g).splitlines()
|
||||||
actual = stderr_g.getvalue().splitlines()
|
actual = stderr_g.getvalue().splitlines()
|
||||||
|
@ -1051,14 +1061,12 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
' [Previous line repeated 1 more time]\n'
|
' [Previous line repeated 1 more time]\n'
|
||||||
f' File "{__file__}", line {lineno_g+3}, in g\n'
|
f' File "{__file__}", line {lineno_g+3}, in g\n'
|
||||||
' raise ValueError\n'
|
' raise ValueError\n'
|
||||||
' ^^^^^^^^^^^^^^^^\n'
|
|
||||||
'ValueError\n'
|
'ValueError\n'
|
||||||
)
|
)
|
||||||
tb_line = (
|
tb_line = (
|
||||||
'Traceback (most recent call last):\n'
|
'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {lineno_g+114}, in _check_recursive_traceback_display\n'
|
f' File "{__file__}", line {lineno_g+108}, in _check_recursive_traceback_display\n'
|
||||||
' g(traceback._RECURSIVE_CUTOFF + 1)\n'
|
' g(traceback._RECURSIVE_CUTOFF + 1)\n'
|
||||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
)
|
)
|
||||||
expected = (tb_line + result_g).splitlines()
|
expected = (tb_line + result_g).splitlines()
|
||||||
actual = stderr_g.getvalue().splitlines()
|
actual = stderr_g.getvalue().splitlines()
|
||||||
|
@ -1111,16 +1119,10 @@ class TracebackFormatTests(unittest.TestCase):
|
||||||
exception_print(exc_val)
|
exception_print(exc_val)
|
||||||
|
|
||||||
tb = stderr_f.getvalue().strip().splitlines()
|
tb = stderr_f.getvalue().strip().splitlines()
|
||||||
if has_no_debug_ranges():
|
self.assertEqual(11, len(tb))
|
||||||
self.assertEqual(11, len(tb))
|
self.assertEqual(context_message.strip(), tb[5])
|
||||||
self.assertEqual(context_message.strip(), tb[5])
|
self.assertIn('UnhashableException: ex2', tb[3])
|
||||||
self.assertIn('UnhashableException: ex2', tb[3])
|
self.assertIn('UnhashableException: ex1', tb[10])
|
||||||
self.assertIn('UnhashableException: ex1', tb[10])
|
|
||||||
else:
|
|
||||||
self.assertEqual(13, len(tb))
|
|
||||||
self.assertEqual(context_message.strip(), tb[6])
|
|
||||||
self.assertIn('UnhashableException: ex2', tb[4])
|
|
||||||
self.assertIn('UnhashableException: ex1', tb[12])
|
|
||||||
|
|
||||||
def deep_eg(self):
|
def deep_eg(self):
|
||||||
e = TypeError(1)
|
e = TypeError(1)
|
||||||
|
@ -1256,12 +1258,8 @@ class BaseExceptionReportingTests:
|
||||||
except ZeroDivisionError as _:
|
except ZeroDivisionError as _:
|
||||||
e = _
|
e = _
|
||||||
lines = self.get_report(e).splitlines()
|
lines = self.get_report(e).splitlines()
|
||||||
if has_no_debug_ranges():
|
self.assertEqual(len(lines), 4)
|
||||||
self.assertEqual(len(lines), 4)
|
self.assertTrue(lines[3].startswith('ZeroDivisionError'))
|
||||||
self.assertTrue(lines[3].startswith('ZeroDivisionError'))
|
|
||||||
else:
|
|
||||||
self.assertEqual(len(lines), 5)
|
|
||||||
self.assertTrue(lines[4].startswith('ZeroDivisionError'))
|
|
||||||
self.assertTrue(lines[0].startswith('Traceback'))
|
self.assertTrue(lines[0].startswith('Traceback'))
|
||||||
self.assertTrue(lines[1].startswith(' File'))
|
self.assertTrue(lines[1].startswith(' File'))
|
||||||
self.assertIn('ZeroDivisionError from None', lines[2])
|
self.assertIn('ZeroDivisionError from None', lines[2])
|
||||||
|
@ -1511,10 +1509,8 @@ class BaseExceptionReportingTests:
|
||||||
f' + Exception Group Traceback (most recent call last):\n'
|
f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
f' | exception_or_callable()\n'
|
f' | exception_or_callable()\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n'
|
||||||
f' | raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
|
f' | raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: eg (2 sub-exceptions)\n'
|
f' | ExceptionGroup: eg (2 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 1\n'
|
f' | ValueError: 1\n'
|
||||||
|
@ -1536,7 +1532,6 @@ class BaseExceptionReportingTests:
|
||||||
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
|
||||||
f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n'
|
f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: eg1 (2 sub-exceptions)\n'
|
f' | ExceptionGroup: eg1 (2 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 1\n'
|
f' | ValueError: 1\n'
|
||||||
|
@ -1549,10 +1544,8 @@ class BaseExceptionReportingTests:
|
||||||
f' + Exception Group Traceback (most recent call last):\n'
|
f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
f' | exception_or_callable()\n'
|
f' | exception_or_callable()\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
||||||
f' | raise EG("eg2", [ValueError(3), TypeError(4)]) from e\n'
|
f' | raise EG("eg2", [ValueError(3), TypeError(4)]) from e\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: eg2 (2 sub-exceptions)\n'
|
f' | ExceptionGroup: eg2 (2 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 3\n'
|
f' | ValueError: 3\n'
|
||||||
|
@ -1578,7 +1571,6 @@ class BaseExceptionReportingTests:
|
||||||
f' + Exception Group Traceback (most recent call last):\n'
|
f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 4}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 4}, in exc\n'
|
||||||
f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n'
|
f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: eg1 (2 sub-exceptions)\n'
|
f' | ExceptionGroup: eg1 (2 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 1\n'
|
f' | ValueError: 1\n'
|
||||||
|
@ -1591,7 +1583,6 @@ class BaseExceptionReportingTests:
|
||||||
f' + Exception Group Traceback (most recent call last):\n'
|
f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
|
||||||
f' | raise EG("eg2", [ValueError(3), TypeError(4)])\n'
|
f' | raise EG("eg2", [ValueError(3), TypeError(4)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: eg2 (2 sub-exceptions)\n'
|
f' | ExceptionGroup: eg2 (2 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 3\n'
|
f' | ValueError: 3\n'
|
||||||
|
@ -1604,10 +1595,8 @@ class BaseExceptionReportingTests:
|
||||||
f'Traceback (most recent call last):\n'
|
f'Traceback (most recent call last):\n'
|
||||||
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
f' exception_or_callable()\n'
|
f' exception_or_callable()\n'
|
||||||
f' ^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' File "{__file__}", line {exc.__code__.co_firstlineno + 8}, in exc\n'
|
f' File "{__file__}", line {exc.__code__.co_firstlineno + 8}, in exc\n'
|
||||||
f' raise ImportError(5)\n'
|
f' raise ImportError(5)\n'
|
||||||
f' ^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f'ImportError: 5\n')
|
f'ImportError: 5\n')
|
||||||
|
|
||||||
report = self.get_report(exc)
|
report = self.get_report(exc)
|
||||||
|
@ -1630,7 +1619,6 @@ class BaseExceptionReportingTests:
|
||||||
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n'
|
||||||
f' | raise EG("eg", [VE(1), exc, VE(4)])\n'
|
f' | raise EG("eg", [VE(1), exc, VE(4)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: eg (3 sub-exceptions)\n'
|
f' | ExceptionGroup: eg (3 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 1\n'
|
f' | ValueError: 1\n'
|
||||||
|
@ -1638,7 +1626,6 @@ class BaseExceptionReportingTests:
|
||||||
f' | Exception Group Traceback (most recent call last):\n'
|
f' | Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
|
||||||
f' | raise EG("nested", [TE(2), TE(3)])\n'
|
f' | raise EG("nested", [TE(2), TE(3)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
|
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | TypeError: 2\n'
|
f' | TypeError: 2\n'
|
||||||
|
@ -1654,10 +1641,8 @@ class BaseExceptionReportingTests:
|
||||||
f' + Exception Group Traceback (most recent call last):\n'
|
f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
f' | exception_or_callable()\n'
|
f' | exception_or_callable()\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 11}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 11}, in exc\n'
|
||||||
f' | raise EG("top", [VE(5)])\n'
|
f' | raise EG("top", [VE(5)])\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: top (1 sub-exception)\n'
|
f' | ExceptionGroup: top (1 sub-exception)\n'
|
||||||
f' +-+---------------- 1 ----------------\n'
|
f' +-+---------------- 1 ----------------\n'
|
||||||
f' | ValueError: 5\n'
|
f' | ValueError: 5\n'
|
||||||
|
@ -1815,10 +1800,8 @@ class BaseExceptionReportingTests:
|
||||||
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
f' | exception_or_callable()\n'
|
f' | exception_or_callable()\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n'
|
||||||
f' | raise ExceptionGroup("nested", excs)\n'
|
f' | raise ExceptionGroup("nested", excs)\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
|
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
|
||||||
f' | >> Multi line note\n'
|
f' | >> Multi line note\n'
|
||||||
f' | >> Because I am such\n'
|
f' | >> Because I am such\n'
|
||||||
|
@ -1830,14 +1813,12 @@ class BaseExceptionReportingTests:
|
||||||
f' | Traceback (most recent call last):\n'
|
f' | Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
||||||
f' | raise ValueError(msg)\n'
|
f' | raise ValueError(msg)\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ValueError: bad value\n'
|
f' | ValueError: bad value\n'
|
||||||
f' | the bad value\n'
|
f' | the bad value\n'
|
||||||
f' +---------------- 2 ----------------\n'
|
f' +---------------- 2 ----------------\n'
|
||||||
f' | Traceback (most recent call last):\n'
|
f' | Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
||||||
f' | raise ValueError(msg)\n'
|
f' | raise ValueError(msg)\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ValueError: terrible value\n'
|
f' | ValueError: terrible value\n'
|
||||||
f' | the terrible value\n'
|
f' | the terrible value\n'
|
||||||
f' +------------------------------------\n')
|
f' +------------------------------------\n')
|
||||||
|
@ -1870,10 +1851,8 @@ class BaseExceptionReportingTests:
|
||||||
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
expected = (f' + Exception Group Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
|
||||||
f' | exception_or_callable()\n'
|
f' | exception_or_callable()\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 10}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 10}, in exc\n'
|
||||||
f' | raise ExceptionGroup("nested", excs)\n'
|
f' | raise ExceptionGroup("nested", excs)\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
|
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
|
||||||
f' | >> Multi line note\n'
|
f' | >> Multi line note\n'
|
||||||
f' | >> Because I am such\n'
|
f' | >> Because I am such\n'
|
||||||
|
@ -1886,7 +1865,6 @@ class BaseExceptionReportingTests:
|
||||||
f' | Traceback (most recent call last):\n'
|
f' | Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
||||||
f' | raise ValueError(msg)\n'
|
f' | raise ValueError(msg)\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ValueError: bad value\n'
|
f' | ValueError: bad value\n'
|
||||||
f' | the bad value\n'
|
f' | the bad value\n'
|
||||||
f' | Goodbye bad value\n'
|
f' | Goodbye bad value\n'
|
||||||
|
@ -1894,7 +1872,6 @@ class BaseExceptionReportingTests:
|
||||||
f' | Traceback (most recent call last):\n'
|
f' | Traceback (most recent call last):\n'
|
||||||
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
|
||||||
f' | raise ValueError(msg)\n'
|
f' | raise ValueError(msg)\n'
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
|
|
||||||
f' | ValueError: terrible value\n'
|
f' | ValueError: terrible value\n'
|
||||||
f' | the terrible value\n'
|
f' | the terrible value\n'
|
||||||
f' | Goodbye terrible value\n'
|
f' | Goodbye terrible value\n'
|
||||||
|
@ -2670,19 +2647,16 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||||
f' + Exception Group Traceback (most recent call last):',
|
f' + Exception Group Traceback (most recent call last):',
|
||||||
f' | File "{__file__}", line {lno_g+23}, in _get_exception_group',
|
f' | File "{__file__}", line {lno_g+23}, in _get_exception_group',
|
||||||
f' | raise ExceptionGroup("eg2", [exc3, exc4])',
|
f' | raise ExceptionGroup("eg2", [exc3, exc4])',
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^',
|
|
||||||
f' | ExceptionGroup: eg2 (2 sub-exceptions)',
|
f' | ExceptionGroup: eg2 (2 sub-exceptions)',
|
||||||
f' +-+---------------- 1 ----------------',
|
f' +-+---------------- 1 ----------------',
|
||||||
f' | Exception Group Traceback (most recent call last):',
|
f' | Exception Group Traceback (most recent call last):',
|
||||||
f' | File "{__file__}", line {lno_g+16}, in _get_exception_group',
|
f' | File "{__file__}", line {lno_g+16}, in _get_exception_group',
|
||||||
f' | raise ExceptionGroup("eg1", [exc1, exc2])',
|
f' | raise ExceptionGroup("eg1", [exc1, exc2])',
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^',
|
|
||||||
f' | ExceptionGroup: eg1 (2 sub-exceptions)',
|
f' | ExceptionGroup: eg1 (2 sub-exceptions)',
|
||||||
f' +-+---------------- 1 ----------------',
|
f' +-+---------------- 1 ----------------',
|
||||||
f' | Traceback (most recent call last):',
|
f' | Traceback (most recent call last):',
|
||||||
f' | File "{__file__}", line {lno_g+9}, in _get_exception_group',
|
f' | File "{__file__}", line {lno_g+9}, in _get_exception_group',
|
||||||
f' | f()',
|
f' | f()',
|
||||||
f' | ^^^',
|
|
||||||
f' | File "{__file__}", line {lno_f+1}, in f',
|
f' | File "{__file__}", line {lno_f+1}, in f',
|
||||||
f' | 1/0',
|
f' | 1/0',
|
||||||
f' | ~^~',
|
f' | ~^~',
|
||||||
|
@ -2691,20 +2665,16 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||||
f' | Traceback (most recent call last):',
|
f' | Traceback (most recent call last):',
|
||||||
f' | File "{__file__}", line {lno_g+13}, in _get_exception_group',
|
f' | File "{__file__}", line {lno_g+13}, in _get_exception_group',
|
||||||
f' | g(42)',
|
f' | g(42)',
|
||||||
f' | ^^^^^',
|
|
||||||
f' | File "{__file__}", line {lno_g+1}, in g',
|
f' | File "{__file__}", line {lno_g+1}, in g',
|
||||||
f' | raise ValueError(v)',
|
f' | raise ValueError(v)',
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^',
|
|
||||||
f' | ValueError: 42',
|
f' | ValueError: 42',
|
||||||
f' +------------------------------------',
|
f' +------------------------------------',
|
||||||
f' +---------------- 2 ----------------',
|
f' +---------------- 2 ----------------',
|
||||||
f' | Traceback (most recent call last):',
|
f' | Traceback (most recent call last):',
|
||||||
f' | File "{__file__}", line {lno_g+20}, in _get_exception_group',
|
f' | File "{__file__}", line {lno_g+20}, in _get_exception_group',
|
||||||
f' | g(24)',
|
f' | g(24)',
|
||||||
f' | ^^^^^',
|
|
||||||
f' | File "{__file__}", line {lno_g+1}, in g',
|
f' | File "{__file__}", line {lno_g+1}, in g',
|
||||||
f' | raise ValueError(v)',
|
f' | raise ValueError(v)',
|
||||||
f' | ^^^^^^^^^^^^^^^^^^^',
|
|
||||||
f' | ValueError: 24',
|
f' | ValueError: 24',
|
||||||
f' +------------------------------------',
|
f' +------------------------------------',
|
||||||
f'']
|
f'']
|
||||||
|
|
|
@ -465,7 +465,8 @@ class StackSummary(list):
|
||||||
row.append(' File "{}", line {}, in {}\n'.format(
|
row.append(' File "{}", line {}, in {}\n'.format(
|
||||||
frame_summary.filename, frame_summary.lineno, frame_summary.name))
|
frame_summary.filename, frame_summary.lineno, frame_summary.name))
|
||||||
if frame_summary.line:
|
if frame_summary.line:
|
||||||
row.append(' {}\n'.format(frame_summary.line.strip()))
|
stripped_line = frame_summary.line.strip()
|
||||||
|
row.append(' {}\n'.format(stripped_line))
|
||||||
|
|
||||||
orig_line_len = len(frame_summary._original_line)
|
orig_line_len = len(frame_summary._original_line)
|
||||||
frame_line_len = len(frame_summary.line.lstrip())
|
frame_line_len = len(frame_summary.line.lstrip())
|
||||||
|
@ -486,19 +487,22 @@ class StackSummary(list):
|
||||||
frame_summary._original_line[colno - 1:end_colno - 1]
|
frame_summary._original_line[colno - 1:end_colno - 1]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
end_colno = stripped_characters + len(frame_summary.line.strip())
|
end_colno = stripped_characters + len(stripped_line)
|
||||||
|
|
||||||
row.append(' ')
|
# show indicators if primary char doesn't span the frame line
|
||||||
row.append(' ' * (colno - stripped_characters))
|
if end_colno - colno < len(stripped_line) or (
|
||||||
|
anchors and anchors.right_start_offset - anchors.left_end_offset > 0):
|
||||||
|
row.append(' ')
|
||||||
|
row.append(' ' * (colno - stripped_characters))
|
||||||
|
|
||||||
if anchors:
|
if anchors:
|
||||||
row.append(anchors.primary_char * (anchors.left_end_offset))
|
row.append(anchors.primary_char * (anchors.left_end_offset))
|
||||||
row.append(anchors.secondary_char * (anchors.right_start_offset - anchors.left_end_offset))
|
row.append(anchors.secondary_char * (anchors.right_start_offset - anchors.left_end_offset))
|
||||||
row.append(anchors.primary_char * (end_colno - colno - anchors.right_start_offset))
|
row.append(anchors.primary_char * (end_colno - colno - anchors.right_start_offset))
|
||||||
else:
|
else:
|
||||||
row.append('^' * (end_colno - colno))
|
row.append('^' * (end_colno - colno))
|
||||||
|
|
||||||
row.append('\n')
|
row.append('\n')
|
||||||
|
|
||||||
if frame_summary.locals:
|
if frame_summary.locals:
|
||||||
for name, value in sorted(frame_summary.locals.items()):
|
for name, value in sorted(frame_summary.locals.items()):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Revise the display strategy of traceback enhanced error locations. The indicators are only shown when the location doesn't span the whole line.
|
|
@ -592,7 +592,6 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent,
|
||||||
* Traceback (most recent call last):
|
* Traceback (most recent call last):
|
||||||
* File "/home/isidentical/cpython/cpython/t.py", line 10, in <module>
|
* File "/home/isidentical/cpython/cpython/t.py", line 10, in <module>
|
||||||
* add_values(1, 2, 'x', 3, 4)
|
* add_values(1, 2, 'x', 3, 4)
|
||||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
* File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values
|
* File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values
|
||||||
* return a + b + c + d + e
|
* return a + b + c + d + e
|
||||||
* ~~~~~~^~~
|
* ~~~~~~^~~
|
||||||
|
@ -736,7 +735,7 @@ print_error_location_carets(PyObject *f, int offset, Py_ssize_t start_offset, Py
|
||||||
int special_chars = (left_end_offset != -1 || right_start_offset != -1);
|
int special_chars = (left_end_offset != -1 || right_start_offset != -1);
|
||||||
const char *str;
|
const char *str;
|
||||||
while (++offset <= end_offset) {
|
while (++offset <= end_offset) {
|
||||||
if (offset <= start_offset || offset > end_offset) {
|
if (offset <= start_offset) {
|
||||||
str = " ";
|
str = " ";
|
||||||
} else if (special_chars && left_end_offset < offset && offset <= right_start_offset) {
|
} else if (special_chars && left_end_offset < offset && offset <= right_start_offset) {
|
||||||
str = secondary;
|
str = secondary;
|
||||||
|
@ -792,6 +791,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
|
||||||
|
|
||||||
int code_offset = tb->tb_lasti;
|
int code_offset = tb->tb_lasti;
|
||||||
PyCodeObject* code = frame->f_frame->f_code;
|
PyCodeObject* code = frame->f_frame->f_code;
|
||||||
|
const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line);
|
||||||
|
|
||||||
int start_line;
|
int start_line;
|
||||||
int end_line;
|
int end_line;
|
||||||
|
@ -813,7 +813,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
|
||||||
//
|
//
|
||||||
// ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE
|
// ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE
|
||||||
// ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~
|
||||||
// | |-> left_end_offset | |-> left_offset
|
// | |-> left_end_offset | |-> end_offset
|
||||||
// |-> start_offset |-> right_start_offset
|
// |-> start_offset |-> right_start_offset
|
||||||
//
|
//
|
||||||
// In general we will only have (start_offset, end_offset) but we can gather more information
|
// In general we will only have (start_offset, end_offset) but we can gather more information
|
||||||
|
@ -822,6 +822,9 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
|
||||||
// the different ranges (primary_error_char and secondary_error_char). If we cannot obtain the
|
// the different ranges (primary_error_char and secondary_error_char). If we cannot obtain the
|
||||||
// AST information or we cannot identify special ranges within it, then left_end_offset and
|
// AST information or we cannot identify special ranges within it, then left_end_offset and
|
||||||
// right_end_offset will be set to -1.
|
// right_end_offset will be set to -1.
|
||||||
|
//
|
||||||
|
// To keep the column indicators pertinent, they are not shown when the primary character
|
||||||
|
// spans the whole line.
|
||||||
|
|
||||||
// Convert the utf-8 byte offset to the actual character offset so we print the right number of carets.
|
// Convert the utf-8 byte offset to the actual character offset so we print the right number of carets.
|
||||||
assert(source_line);
|
assert(source_line);
|
||||||
|
@ -859,7 +862,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_ssize_t i = PyUnicode_GET_LENGTH(source_line);
|
Py_ssize_t i = source_line_len;
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
if (!IS_WHITESPACE(source_line_str[i])) {
|
if (!IS_WHITESPACE(source_line_str[i])) {
|
||||||
break;
|
break;
|
||||||
|
@ -869,6 +872,13 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
|
||||||
end_offset = i + 1;
|
end_offset = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Elide indicators if primary char spans the frame line
|
||||||
|
Py_ssize_t stripped_line_len = source_line_len - truncation - _TRACEBACK_SOURCE_LINE_INDENT;
|
||||||
|
bool has_secondary_ranges = (left_end_offset != -1 || right_start_offset != -1);
|
||||||
|
if (end_offset - start_offset == stripped_line_len && !has_secondary_ranges) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
|
if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
|
||||||
err = -1;
|
err = -1;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue