Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.2k
gh-98458: unittest: bugfix for infinite loop while handling chained exceptions that contain cycles#98459
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
gh-98458: unittest: bugfix for infinite loop while handling chained exceptions that contain cycles#98459
Changes from1 commit
2defc0e
bfe8a4a
c100409
fa84a5b
8388d82
08fd984
6b868df
d7aa4e7
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
…cks. The "seen" set follows the approach used in the TracebackException class (thank you@iritkatriel for pointing it out)
- Loading branch information
Uh oh!
There was an error while loading.Please reload this page.
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -275,6 +275,39 @@ def get_exc_info(): | ||
self.assertEqual(len(dropped), 1) | ||
self.assertIn("raise self.failureException(msg)", dropped[0]) | ||
def test_addFailure_filter_traceback_frames_chained_exception_cycle(self): | ||
class Foo(unittest.TestCase): | ||
def test_1(self): | ||
pass | ||
def get_exc_info(): | ||
AlexTate marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
try: | ||
# Create two directionally opposed cycles | ||
# __cause__ in one direction, __context__ in the other | ||
# Only one is needed since they serve the same role in _clean_tracebacks() | ||
# But this way we cover both possibilities if the code changes | ||
AlexTate marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
A, B, C = Exception("A"), Exception("B"), Exception("C") | ||
edges = [(C, B), (B, A), (A, C)] | ||
for ex1, ex2 in edges: | ||
ex1.__cause__ = ex2 | ||
ex2.__context__ = ex1 | ||
raise C | ||
except: | ||
return sys.exc_info() | ||
exc_info_tuple = get_exc_info() | ||
test = Foo('test_1') | ||
result = unittest.TestResult() | ||
result.startTest(test) | ||
result.addFailure(test, exc_info_tuple) | ||
result.stopTest(test) | ||
formatted_exc = result.failures[0][1] | ||
self.assertEqual(formatted_exc.count("Exception: A\n"), 1) | ||
self.assertEqual(formatted_exc.count("Exception: B\n"), 1) | ||
self.assertEqual(formatted_exc.count("Exception: C\n"), 1) | ||
# "addError(test, err)" | ||
# ... | ||
# "Called when the test case test raises an unexpected exception err | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -196,6 +196,7 @@ def _clean_tracebacks(self, exctype, value, tb, test): | ||
ret = None | ||
first = True | ||
excs = [(exctype, value, tb)] | ||
seen = {id(value)} # Handle loops in __cause__ or __context__. | ||
AlexTate marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
while excs: | ||
(exctype, value, tb) = excs.pop() | ||
# Skip test runner traceback levels | ||
@@ -214,8 +215,9 @@ def _clean_tracebacks(self, exctype, value, tb, test): | ||
if value is not None: | ||
for c in (value.__cause__, value.__context__): | ||
if c is not None andid(c)notin seen: | ||
excs.append((type(c), c, c.__traceback__)) | ||
seen.add(id(c)) | ||
return ret | ||
def _is_relevant_tb_level(self, tb): | ||