8000 [3.13] gh-132308: prevent `TracebackException` swallowing attributes … · python/cpython@dfaa384 · GitHub
[go: up one dir, main page]

Skip to content

Commit dfaa384

Browse files
[3.13] gh-132308: prevent TracebackException swallowing attributes of a falsey Exception or ExceptionGroup (GH-132363) (#132725)
gh-132308: prevent `TracebackException` swallowing attributes of a falsey `Exception` or `ExceptionGroup` (GH-132363) (cherry picked from commit 69cda31) Co-authored-by: Duprat <yduprat@gmail.com>
1 parent 7998f99 commit dfaa384

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

Lib/test/test_traceback.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3408,6 +3408,19 @@ class Unrepresentable:
34083408
def __repr__(self) -> str:
34093409
raise Exception("Unrepresentable")
34103410

3411+
3412+
# Used in test_dont_swallow_cause_or_context_of_falsey_exception and
3413+
# test_dont_swallow_subexceptions_of_falsey_exceptiongroup.
3414+
class FalseyException(Exception):
3415+
def __bool__(self):
3416+
return False
3417+
3418+
3419+
class FalseyExceptionGroup(ExceptionGroup):
3420+
def __bool__(self):
3421+
return False
3422+
3423+
34113424
class TestTracebackException(unittest.TestCase):
34123425
def do_test_smoke(self, exc, expected_type_str):
34133426
try:
@@ -3753,6 +3766,24 @@ def f():
37533766
'ZeroDivisionError: division by zero',
37543767
''])
37553768

3769+
def test_dont_swallow_cause_or_context_of_falsey_exception(self):
3770+
# see gh-132308: Ensure that __cause__ or __context__ attributes of exceptions
3771+
# that evaluate as falsey are included in the output. For falsey term,
3772+
# see https://docs.python.org/3/library/stdtypes.html#truth-value-testing.
3773+
3774+
try:
3775+
raise FalseyException from KeyError
3776+
except FalseyException as e:
3777+
self.assertIn(cause_message, traceback.format_exception(e))
3778+
3779+
try:
3780+
try:
3781+
1/0
3782+
except ZeroDivisionError:
3783+
raise FalseyException
3784+
except FalseyException as e:
3785+
self.assertIn(context_message, traceback.format_exception(e))
3786+
37563787

37573788
class TestTracebackException_ExceptionGroups(unittest.TestCase):
37583789
def setUp(self):
@@ -3954,6 +3985,26 @@ def test_comparison(self):
39543985
self.assertNotEqual(exc, object())
39553986
self.assertEqual(exc, ALWAYS_EQ)
39563987

3988+
def test_dont_swallow_subexceptions_of_falsey_exceptiongroup(self):
3989+
# see gh-132308: Ensure that subexceptions of exception groups
3990+
# that evaluate as falsey are displayed in the output. For falsey term,
3991+
# see https://docs.python.org/3/library/stdtypes.html#truth-value-testing.
3992+
3993+
try:
3994+
raise FalseyExceptionGroup("Gih", (KeyError(), NameError()))
3995+
except Exception as ee:
3996+
str_exc = ''.join(traceback.format_exception(ee))
3997+
self.assertIn('+---------------- 1 ----------------', str_exc)
3998+
self.assertIn('+---------------- 2 ----------------', str_exc)
3999+
4000+
# Test with a falsey exception, in last position, as sub-exceptions.
4001+
msg = 'bool'
4002+
try:
4003+
raise FalseyExceptionGroup("Gah", (KeyError(), FalseyException(msg)))
4004+
except Exception as ee:
4005+
str_exc = traceback.format_exception(ee)
4006+
self.assertIn(f'{FalseyException.__name__}: {msg}', str_exc[-2])
4007+
39574008

39584009
global_for_suggestions = None
39594010

Lib/traceback.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
11161116
queue = [(self, exc_value)]
11171117
while queue:
11181118
te, e = queue.pop()
1119-
if (e and e.__cause__ is not None
1119+
if (e is not None and e.__cause__ is not None
11201120
and id(e.__cause__) not in _seen):
11211121
cause = TracebackException(
11221122
type(e.__cause__),
@@ -1137,7 +1137,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
11371137
not e.__suppress_context__)
11381138
else:
11391139
need_context = True
1140-
if (e and e.__context__ is not None
1140+
if (e is not None and e.__context__ is not None
11411141
and need_context and id(e.__context__) not in _seen):
11421142
context = TracebackException(
11431143
type(e.__context__),
@@ -1152,7 +1152,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
11521152
else:
11531153
context = None
11541154

1155-
if e and isinstance(e, BaseExceptionGroup):
1155+
if e is not None and isinstance(e, BaseExceptionGroup):
11561156
exceptions = []
11571157
for exc in e.exceptions:
11581158
texc = TracebackException(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
A :class:`traceback.TracebackException` now correctly renders the ``__context__``
2+
and ``__cause__`` attributes from :ref:`falsey <truth>` :class:`Exception`,
3+
and the ``exceptions`` attribute from falsey :class:`ExceptionGroup`.

0 commit comments

Comments
 (0)
0