Skip to content

Commit 94be186

Browse files
committed
traceback.c implementation
1 parent 6a8f56d commit 94be186

File tree

2 files changed

+74
-64
lines changed

2 files changed

+74
-64
lines changed

Lib/test/test_traceback.py

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def do_test(firstlines, message, charset, lineno):
252252
self.assertTrue(stdout[2].endswith(err_line),
253253
"Invalid traceback line: {0!r} instead of {1!r}".format(
254254
stdout[2], err_line))
255-
actual_err_msg = stdout[3 if has_no_debug_ranges() else 4]
255+
actual_err_msg = stdout[3]
256256
self.assertTrue(actual_err_msg == err_msg,
257257
"Invalid error message: {0!r} instead of {1!r}".format(
258258
actual_err_msg, err_msg))
@@ -704,23 +704,23 @@ class A: pass
704704
)
705705
self.assertEqual(result_lines, expected_error.splitlines())
706706

707-
# @cpython_only
708-
# @requires_debug_ranges()
709-
# class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests):
710-
# """
711-
# Same set of tests as above but with Python's internal traceback printing.
712-
# """
713-
# def get_exception(self, callable):
714-
# from _testcapi import exception_print
715-
# try:
716-
# callable()
717-
# self.fail("No exception thrown.")
718-
# except Exception as e:
719-
# with captured_output("stderr") as tbstderr:
720-
# exception_print(e)
721-
# return tbstderr.getvalue().splitlines()[:-1]
722-
#
723-
# callable_line = get_exception.__code__.co_firstlineno + 3
707+
@cpython_only
708+
@requires_debug_ranges()
709+
class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests):
710+
"""
711+
Same set of tests as above but with Python's internal traceback printing.
712+
"""
713+
def get_exception(self, callable):
714+
from _testcapi import exception_print
715+
try:
716+
callable()
717+
self.fail("No exception thrown.")
718+
except Exception as e:
719+
with captured_output("stderr") as tbstderr:
720+
exception_print(e)
721+
return tbstderr.getvalue().splitlines()[:-1]
722+
723+
callable_line = get_exception.__code__.co_firstlineno + 3
724724

725725

726726
class TracebackFormatTests(unittest.TestCase):
@@ -1015,14 +1015,14 @@ def h(count=10):
10151015
def test_recursive_traceback_python(self):
10161016
self._check_recursive_traceback_display(traceback.print_exc)
10171017

1018-
# @cpython_only
1019-
# @requires_debug_ranges()
1020-
# def test_recursive_traceback_cpython_internal(self):
1021-
# from _testcapi import exception_print
1022-
# def render_exc():
1023-
# exc_type, exc_value, exc_tb = sys.exc_info()
1024-
# exception_print(exc_value)
1025-
# self._check_recursive_traceback_display(render_exc)
1018+
@cpython_only
1019+
@requires_debug_ranges()
1020+
def test_recursive_traceback_cpython_internal(self):
1021+
from _testcapi import exception_print
1022+
def render_exc():
1023+
exc_type, exc_value, exc_tb = sys.exc_info()
1024+
exception_print(exc_value)
1025+
self._check_recursive_traceback_display(render_exc)
10261026

10271027
def test_format_stack(self):
10281028
def fmt():
@@ -1058,16 +1058,10 @@ def __eq__(self, other):
10581058
exception_print(exc_val)
10591059

10601060
tb = stderr_f.getvalue().strip().splitlines()
1061-
if has_no_debug_ranges():
1062-
self.assertEqual(11, len(tb))
1063-
self.assertEqual(context_message.strip(), tb[5])
1064-
self.assertIn('UnhashableException: ex2', tb[3])
1065-
self.assertIn('UnhashableException: ex1', tb[10])
1066-
else:
1067-
self.assertEqual(13, len(tb))
1068-
self.assertEqual(context_message.strip(), tb[6])
1069-
self.assertIn('UnhashableException: ex2', tb[4])
1070-
self.assertIn('UnhashableException: ex1', tb[12])
1061+
self.assertEqual(11, len(tb))
1062+
self.assertEqual(context_message.strip(), tb[5])
1063+
self.assertIn('UnhashableException: ex2', tb[3])
1064+
self.assertIn('UnhashableException: ex1', tb[10])
10711065

10721066
def deep_eg(self):
10731067
e = TypeError(1)
@@ -1451,15 +1445,17 @@ def __str__(self):
14511445
# #### Exception Groups ####
14521446

14531447
def test_exception_group_basic(self):
1448+
self.maxDiff = None
14541449
def exc():
1455-
raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])
1450+
if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])
14561451

14571452
expected = (
14581453
f' + Exception Group Traceback (most recent call last):\n'
14591454
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
14601455
f' | exception_or_callable()\n'
14611456
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n'
1462-
f' | raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
1457+
f' | if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
1458+
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
14631459
f' | ExceptionGroup: eg (2 sub-exceptions)\n'
14641460
f' +-+---------------- 1 ----------------\n'
14651461
f' | ValueError: 1\n'
@@ -1474,13 +1470,14 @@ def test_exception_group_cause(self):
14741470
def exc():
14751471
EG = ExceptionGroup
14761472
try:
1477-
raise EG("eg1", [ValueError(1), TypeError(2)])
1473+
if True: raise EG("eg1", [ValueError(1), TypeError(2)])
14781474
except Exception as e:
14791475
raise EG("eg2", [ValueError(3), TypeError(4)]) from e
14801476

14811477
expected = (f' + Exception Group Traceback (most recent call last):\n'
14821478
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
1483-
f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n'
1479+
f' | if True: raise EG("eg1", [ValueError(1), TypeError(2)])\n'
1480+
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
14841481
f' | ExceptionGroup: eg1 (2 sub-exceptions)\n'
14851482
f' +-+---------------- 1 ----------------\n'
14861483
f' | ValueError: 1\n'
@@ -1510,7 +1507,7 @@ def exc():
15101507
EG = ExceptionGroup
15111508
try:
15121509
try:
1513-
raise EG("eg1", [ValueError(1), TypeError(2)])
1510+
if True: raise EG("eg1", [ValueError(1), TypeError(2)])
15141511
except:
15151512
raise EG("eg2", [ValueError(3), TypeError(4)])
15161513
except:
@@ -1519,7 +1516,8 @@ def exc():
15191516
expected = (
15201517
f' + Exception Group Traceback (most recent call last):\n'
15211518
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 4}, in exc\n'
1522-
f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n'
1519+
f' | if True: raise EG("eg1", [ValueError(1), TypeError(2)])\n'
1520+
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
15231521
f' | ExceptionGroup: eg1 (2 sub-exceptions)\n'
15241522
f' +-+---------------- 1 ----------------\n'
15251523
f' | ValueError: 1\n'
@@ -1558,7 +1556,7 @@ def exc():
15581556
TE = TypeError
15591557
try:
15601558
try:
1561-
raise EG("nested", [TE(2), TE(3)])
1559+
if True: raise EG("nested", [TE(2), TE(3)])
15621560
except Exception as e:
15631561
exc = e
15641562
raise EG("eg", [VE(1), exc, VE(4)])
@@ -1574,7 +1572,8 @@ def exc():
15741572
f' +---------------- 2 ----------------\n'
15751573
f' | Exception Group Traceback (most recent call last):\n'
15761574
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
1577-
f' | raise EG("nested", [TE(2), TE(3)])\n'
1575+
f' | if True: raise EG("nested", [TE(2), TE(3)])\n'
1576+
f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
15781577
f' | ExceptionGroup: nested (2 sub-exceptions)\n'
15791578
f' +-+---------------- 1 ----------------\n'
15801579
f' | TypeError: 2\n'
@@ -1732,7 +1731,7 @@ def exc():
17321731
excs = []
17331732
for msg in ['bad value', 'terrible value']:
17341733
try:
1735-
raise ValueError(msg)
1734+
if True: raise ValueError(msg)
17361735
except ValueError as e:
17371736
e.add_note(f'the {msg}')
17381737
excs.append(e)
@@ -1761,13 +1760,15 @@ def exc():
17611760
f' +-+---------------- 1 ----------------\n'
17621761
f' | Traceback (most recent call last):\n'
17631762
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
1764-
f' | raise ValueError(msg)\n'
1763+
f' | if True: raise ValueError(msg)\n'
1764+
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
17651765
f' | ValueError: bad value\n'
17661766
f' | the bad value\n'
17671767
f' +---------------- 2 ----------------\n'
17681768
f' | Traceback (most recent call last):\n'
17691769
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
1770-
f' | raise ValueError(msg)\n'
1770+
f' | if True: raise ValueError(msg)\n'
1771+
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
17711772
f' | ValueError: terrible value\n'
17721773
f' | the terrible value\n'
17731774
f' +------------------------------------\n')
@@ -1781,7 +1782,7 @@ def exc():
17811782
excs = []
17821783
for msg in ['bad value', 'terrible value']:
17831784
try:
1784-
raise ValueError(msg)
1785+
if True: raise ValueError(msg)
17851786
except ValueError as e:
17861787
e.add_note(f'the {msg}')
17871788
e.add_note(f'Goodbye {msg}')
@@ -1813,14 +1814,16 @@ def exc():
18131814
f' +-+---------------- 1 ----------------\n'
18141815
f' | Traceback (most recent call last):\n'
18151816
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
1816-
f' | raise ValueError(msg)\n'
1817+
f' | if True: raise ValueError(msg)\n'
1818+
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
18171819
f' | ValueError: bad value\n'
18181820
f' | the bad value\n'
18191821
f' | Goodbye bad value\n'
18201822
f' +---------------- 2 ----------------\n'
18211823
f' | Traceback (most recent call last):\n'
18221824
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
1823-
f' | raise ValueError(msg)\n'
1825+
f' | if True: raise ValueError(msg)\n'
1826+
f' | ^^^^^^^^^^^^^^^^^^^^^\n'
18241827
f' | ValueError: terrible value\n'
18251828
f' | the terrible value\n'
18261829
f' | Goodbye terrible value\n'
@@ -1871,18 +1874,18 @@ def get_report(self, e):
18711874
return s
18721875

18731876

1874-
# class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
1875-
# #
1876-
# # This checks built-in reporting by the interpreter.
1877-
# #
1878-
#
1879-
# @cpython_only
1880-
# def get_report(self, e):
1881-
# from _testcapi import exception_print
1882-
# e = self.get_exception(e)
1883-
# with captured_output("stderr") as s:
1884-
# exception_print(e)
1885-
# return s.getvalue()
1877+
class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
1878+
#
1879+
# This checks built-in reporting by the interpreter.
1880+
#
1881+
1882+
@cpython_only
1883+
def get_report(self, e):
1884+
from _testcapi import exception_print
1885+
e = self.get_exception(e)
1886+
with captured_output("stderr") as s:
1887+
exception_print(e)
1888+
return s.getvalue()
18861889

18871890

18881891
class LimitTests(unittest.TestCase):

Python/traceback.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,6 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent,
592592
* Traceback (most recent call last):
593593
* File "/home/isidentical/cpython/cpython/t.py", line 10, in <module>
594594
* add_values(1, 2, 'x', 3, 4)
595-
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
596595
* File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values
597596
* return a + b + c + d + e
598597
* ~~~~~~^~~
@@ -792,6 +791,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
792791

793792
int code_offset = tb->tb_lasti;
794793
PyCodeObject* code = frame->f_frame->f_code;
794+
const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line);
795795

796796
int start_line;
797797
int end_line;
@@ -859,7 +859,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
859859
goto done;
860860
}
861861

862-
Py_ssize_t i = PyUnicode_GET_LENGTH(source_line);
862+
Py_ssize_t i = source_line_len;
863863
while (--i >= 0) {
864864
if (!IS_WHITESPACE(source_line_str[i])) {
865865
break;
@@ -869,6 +869,13 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
869869
end_offset = i + 1;
870870
}
871871

872+
// elide indicators if primary char spans the frame line
873+
Py_ssize_t stripped_line_len = source_line_len - truncation - _TRACEBACK_SOURCE_LINE_INDENT;
874+
if (end_offset - start_offset == stripped_line_len &&
875+
left_end_offset == -1 && right_start_offset == -1) {
876+
goto done;
877+
}
878+
872879
if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
873880
err = -1;
874881
goto done;

0 commit comments

Comments
 (0)