Skip to content

Commit 7d4c4c6

Browse files
Merge pull request #3805 from asottile/cause_cycles
Fix traceback reporting for exceptions with `__cause__` cycles.
2 parents 939a792 + 17644ff commit 7d4c4c6

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

changelog/3804.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix traceback reporting for exceptions with ``__cause__`` cycles.

src/_pytest/_code/code.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,9 @@ def repr_excinfo(self, excinfo):
719719
repr_chain = []
720720
e = excinfo.value
721721
descr = None
722-
while e is not None:
722+
seen = set()
723+
while e is not None and id(e) not in seen:
724+
seen.add(id(e))
723725
if excinfo:
724726
reprtraceback = self.repr_traceback(excinfo)
725727
reprcrash = excinfo._getreprcrash()

testing/code/test_excinfo.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import operator
55
import os
66
import sys
7+
import textwrap
78
import _pytest
89
import py
910
import pytest
@@ -1265,6 +1266,50 @@ def g():
12651266
]
12661267
)
12671268

1269+
@pytest.mark.skipif("sys.version_info[0] < 3")
1270+
def test_exc_chain_repr_cycle(self, importasmod):
1271+
mod = importasmod(
1272+
"""
1273+
class Err(Exception):
1274+
pass
1275+
def fail():
1276+
return 0 / 0
1277+
def reraise():
1278+
try:
1279+
fail()
1280+
except ZeroDivisionError as e:
1281+
raise Err() from e
1282+
def unreraise():
1283+
try:
1284+
reraise()
1285+
except Err as e:
1286+
raise e.__cause__
1287+
"""
1288+
)
1289+
excinfo = pytest.raises(ZeroDivisionError, mod.unreraise)
1290+
r = excinfo.getrepr(style="short")
1291+
tw = TWMock()
1292+
r.toterminal(tw)
1293+
out = "\n".join(line for line in tw.lines if isinstance(line, str))
1294+
expected_out = textwrap.dedent(
1295+
"""\
1296+
:13: in unreraise
1297+
reraise()
1298+
:10: in reraise
1299+
raise Err() from e
1300+
E test_exc_chain_repr_cycle0.mod.Err
1301+
1302+
During handling of the above exception, another exception occurred:
1303+
:15: in unreraise
1304+
raise e.__cause__
1305+
:8: in reraise
1306+
fail()
1307+
:5: in fail
1308+
return 0 / 0
1309+
E ZeroDivisionError: division by zero"""
1310+
)
1311+
assert out == expected_out
1312+
12681313

12691314
@pytest.mark.parametrize("style", ["short", "long"])
12701315
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])

0 commit comments

Comments
 (0)