[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 69cda31261)

Co-authored-by: Duprat <yduprat@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-04-30 09:19:53 +02:00 committed by GitHub
parent 7998f998b2
commit dfaa384991
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 57 additions and 3 deletions

View file

@ -3408,6 +3408,19 @@ class Unrepresentable:
def __repr__(self) -> str:
raise Exception("Unrepresentable")
# Used in test_dont_swallow_cause_or_context_of_falsey_exception and
# test_dont_swallow_subexceptions_of_falsey_exceptiongroup.
class FalseyException(Exception):
def __bool__(self):
return False
class FalseyExceptionGroup(ExceptionGroup):
def __bool__(self):
return False
class TestTracebackException(unittest.TestCase):
def do_test_smoke(self, exc, expected_type_str):
try:
@ -3753,6 +3766,24 @@ class TestTracebackException(unittest.TestCase):
'ZeroDivisionError: division by zero',
''])
def test_dont_swallow_cause_or_context_of_falsey_exception(self):
# see gh-132308: Ensure that __cause__ or __context__ attributes of exceptions
# that evaluate as falsey are included in the output. For falsey term,
# see https://docs.python.org/3/library/stdtypes.html#truth-value-testing.
try:
raise FalseyException from KeyError
except FalseyException as e:
self.assertIn(cause_message, traceback.format_exception(e))
try:
try:
1/0
except ZeroDivisionError:
raise FalseyException
except FalseyException as e:
self.assertIn(context_message, traceback.format_exception(e))
class TestTracebackException_ExceptionGroups(unittest.TestCase):
def setUp(self):
@ -3954,6 +3985,26 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
self.assertNotEqual(exc, object())
self.assertEqual(exc, ALWAYS_EQ)
def test_dont_swallow_subexceptions_of_falsey_exceptiongroup(self):
# see gh-132308: Ensure that subexceptions of exception groups
# that evaluate as falsey are displayed in the output. For falsey term,
# see https://docs.python.org/3/library/stdtypes.html#truth-value-testing.
try:
raise FalseyExceptionGroup("Gih", (KeyError(), NameError()))
except Exception as ee:
str_exc = ''.join(traceback.format_exception(ee))
self.assertIn('+---------------- 1 ----------------', str_exc)
self.assertIn('+---------------- 2 ----------------', str_exc)
# Test with a falsey exception, in last position, as sub-exceptions.
msg = 'bool'
try:
raise FalseyExceptionGroup("Gah", (KeyError(), FalseyException(msg)))
except Exception as ee:
str_exc = traceback.format_exception(ee)
self.assertIn(f'{FalseyException.__name__}: {msg}', str_exc[-2])
global_for_suggestions = None

View file

@ -1116,7 +1116,7 @@ class TracebackException:
queue = [(self, exc_value)]
while queue:
te, e = queue.pop()
if (e and e.__cause__ is not None
if (e is not None and e.__cause__ is not None
and id(e.__cause__) not in _seen):
cause = TracebackException(
type(e.__cause__),
@ -1137,7 +1137,7 @@ class TracebackException:
not e.__suppress_context__)
else:
need_context = True
if (e and e.__context__ is not None
if (e is not None and e.__context__ is not None
and need_context and id(e.__context__) not in _seen):
context = TracebackException(
type(e.__context__),
@ -1152,7 +1152,7 @@ class TracebackException:
else:
context = None
if e and isinstance(e, BaseExceptionGroup):
if e is not None and isinstance(e, BaseExceptionGroup):
exceptions = []
for exc in e.exceptions:
texc = TracebackException(

View file

@ -0,0 +1,3 @@
A :class:`traceback.TracebackException` now correctly renders the ``__context__``
and ``__cause__`` attributes from :ref:`falsey <truth>` :class:`Exception`,
and the ``exceptions`` attribute from falsey :class:`ExceptionGroup`.