8000 bpo-24959: fix unittest.assertRaises bug where traceback entries are dropped from chained exceptions by iritkatriel · Pull Request #23688 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-24959: fix unittest.assertRaises bug where traceback entries are dropped from chained exceptions #23688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 8, 2022
Next Next commit
bpo-42247: fix unittest.assertRaises bug where traceback entries were…
… dropped from the chained exception
  • Loading branch information
iritkatriel committed Sep 5, 2021
commit f642752b725ea1867f22be6bfa2e3f951070d707
22 changes: 14 additions & 8 deletions Lib/unittest/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,10 @@ def _exc_info_to_string(self, err, test):

if exctype is test.failureException:
# Skip assert*() traceback levels
length = self._count_relevant_tb_levels(tb)
else:
length = None
self._remove_unittest_tb_frames(tb)
tb_e = traceback.TracebackException(
exctype, value, tb,
limit=length, capture_locals=self.tb_locals, compact=True)
capture_locals=self.tb_locals, compact=True)
msgLines = list(tb_e.format())

if self.buffer:
Expand All @@ -204,12 +202,20 @@ def _exc_info_to_string(self, err, test):
def _is_relevant_tb_level(self, tb):
return '__unittest' in tb.tb_frame.f_globals

def _count_relevant_tb_levels(self, tb):
length = 0
def _remove_unittest_tb_frames(self, tb):
'''Truncates usercode tb at the first unittest frame.

If the first frame of the traceback is in user code,
the prefix up to the first unittest frame is returned.
If the first frame is already in the unittest module,
the traceback is not modified.
'''
prev = None
while tb and not self._is_relevant_tb_level(tb):
length += 1
prev = tb
tb = tb.tb_next
return length
if prev is not None:
prev.tb_next = None

def __repr__(self):
return ("<%s run=%i errors=%i failures=%i>" %
Expand Down
41 changes: 41 additions & 0 deletions Lib/unittest/test/test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,47 @@ class tb_frame(object):
Frame.tb_frame.f_globals['__unittest'] = True
self.assertTrue(result._is_relevant_tb_level(Frame))

def testRemoveUnittestTbFrames(self):
try:
self.fail('too bad')
except:
exc_info = sys.exc_info()
tb = exc_info[2]
self.assertEqual(len(list(traceback.walk_tb(tb))), 2)
result = unittest.TestResult()
result._remove_unittest_tb_frames(tb)
self.assertEqual(len(list(traceback.walk_tb(tb))), 1)

def testExcInfoStringChained(self): # bpo 49563
def raiseAnException():
raise ValueError(42)
try:
try:
raiseAnException()
except:
self.fail('too bad')
except:
exc_info = sys.exc_info()

class ResultWithoutFilter(unittest.TestResult):
def _remove_unittest_tb_frames(self, tb):
pass

result = ResultWithoutFilter()
exc_str_no_filter = result._exc_info_to_string(exc_info, self)

result = unittest.TestResult()
exc_str_filtered = result._exc_info_to_string(exc_info, self)
for exc_str in [exc_str_no_filter, exc_str_filtered]:
self.assertIn('raiseAnException()', exc_str)
self.assertIn('raise ValueError(42)', exc_str)
self.assertIn("self.fail('too bad')", exc_str)
self.assertIn('AssertionError: too bad', exc_str)

# The unittest call:
self.assertNotIn('raise self.failureException(msg)', exc_str_filtered)
self.assertIn('raise self.failureException(msg)', exc_str_no_filter)

def testFailFast(self):
result = unittest.TestResult()
result._exc_info_to_string = lambda *_: ''
Expand Down
0