Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9bcc68b

Browse files
gh-98458: unittest: bugfix for infinite loop while handling chained exceptions that contain cycles (GH-98459)
* Bugfix addressing infinite loop while handling self-referencing chained exception in TestResult._clean_tracebacks()* Bugfix extended to properly handle exception cycles in _clean_tracebacks. The "seen" set follows the approach used in the TracebackException class (thank you@iritkatriel for pointing it out)* adds a test for a single chained exception that holds a self-loop in its __cause__ and __context__ attributes(cherry picked from commit72ec518)Co-authored-by: AlexTate <0xalextate@gmail.com>
1 parent7aa87bb commit9bcc68b

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

‎Lib/unittest/result.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def _clean_tracebacks(self, exctype, value, tb, test):
196196
ret=None
197197
first=True
198198
excs= [(exctype,value,tb)]
199+
seen= {id(value)}# Detect loops in chained exceptions.
199200
whileexcs:
200201
(exctype,value,tb)=excs.pop()
201202
# Skip test runner traceback levels
@@ -214,8 +215,9 @@ def _clean_tracebacks(self, exctype, value, tb, test):
214215

215216
ifvalueisnotNone:
216217
forcin (value.__cause__,value.__context__):
217-
ifcisnotNone:
218+
ifcisnotNoneandid(c)notinseen:
218219
excs.append((type(c),c,c.__traceback__))
220+
seen.add(id(c))
219221
returnret
220222

221223
def_is_relevant_tb_level(self,tb):

‎Lib/unittest/test/test_result.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,62 @@ def get_exc_info():
275275
self.assertEqual(len(dropped),1)
276276
self.assertIn("raise self.failureException(msg)",dropped[0])
277277

278+
deftest_addFailure_filter_traceback_frames_chained_exception_self_loop(self):
279+
classFoo(unittest.TestCase):
280+
deftest_1(self):
281+
pass
282+
283+
defget_exc_info():
284+
try:
285+
loop=Exception("Loop")
286+
loop.__cause__=loop
287+
loop.__context__=loop
288+
raiseloop
289+
except:
290+
returnsys.exc_info()
291+
292+
exc_info_tuple=get_exc_info()
293+
294+
test=Foo('test_1')
295+
result=unittest.TestResult()
296+
result.startTest(test)
297+
result.addFailure(test,exc_info_tuple)
298+
result.stopTest(test)
299+
300+
formatted_exc=result.failures[0][1]
301+
self.assertEqual(formatted_exc.count("Exception: Loop\n"),1)
302+
303+
deftest_addFailure_filter_traceback_frames_chained_exception_cycle(self):
304+
classFoo(unittest.TestCase):
305+
deftest_1(self):
306+
pass
307+
308+
defget_exc_info():
309+
try:
310+
# Create two directionally opposed cycles
311+
# __cause__ in one direction, __context__ in the other
312+
A,B,C=Exception("A"),Exception("B"),Exception("C")
313+
edges= [(C,B), (B,A), (A,C)]
314+
forex1,ex2inedges:
315+
ex1.__cause__=ex2
316+
ex2.__context__=ex1
317+
raiseC
318+
except:
319+
returnsys.exc_info()
320+
321+
exc_info_tuple=get_exc_info()
322+
323+
test=Foo('test_1')
324+
result=unittest.TestResult()
325+
result.startTest(test)
326+
result.addFailure(test,exc_info_tuple)
327+
result.stopTest(test)
328+
329+
formatted_exc=result.failures[0][1]
330+
self.assertEqual(formatted_exc.count("Exception: A\n"),1)
331+
self.assertEqual(formatted_exc.count("Exception: B\n"),1)
332+
self.assertEqual(formatted_exc.count("Exception: C\n"),1)
333+
278334
# "addError(test, err)"
279335
# ...
280336
# "Called when the test case test raises an unexpected exception err
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix infinite loop in unittest when a self-referencing chained exception is raised

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp