Fix traceback reporting for exceptions with `__cause__` cycles.

This commit is contained in:
Anthony Sottile 2018-08-11 08:54:48 -07:00
parent 64faa41d06
commit 17644ff285
3 changed files with 49 additions and 1 deletions

View File

@ -0,0 +1 @@
Fix traceback reporting for exceptions with ``__cause__`` cycles.

View File

@ -719,7 +719,9 @@ class FormattedExcinfo(object):
repr_chain = [] repr_chain = []
e = excinfo.value e = excinfo.value
descr = None descr = None
while e is not None: seen = set()
while e is not None and id(e) not in seen:
seen.add(id(e))
if excinfo: if excinfo:
reprtraceback = self.repr_traceback(excinfo) reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash() reprcrash = excinfo._getreprcrash()

View File

@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function
import operator import operator
import os import os
import sys import sys
import textwrap
import _pytest import _pytest
import py import py
import pytest import pytest
@ -1265,6 +1266,50 @@ raise ValueError()
] ]
) )
@pytest.mark.skipif("sys.version_info[0] < 3")
def test_exc_chain_repr_cycle(self, importasmod):
mod = importasmod(
"""
class Err(Exception):
pass
def fail():
return 0 / 0
def reraise():
try:
fail()
except ZeroDivisionError as e:
raise Err() from e
def unreraise():
try:
reraise()
except Err as e:
raise e.__cause__
"""
)
excinfo = pytest.raises(ZeroDivisionError, mod.unreraise)
r = excinfo.getrepr(style="short")
tw = TWMock()
r.toterminal(tw)
out = "\n".join(line for line in tw.lines if isinstance(line, str))
expected_out = textwrap.dedent(
"""\
:13: in unreraise
reraise()
:10: in reraise
raise Err() from e
E test_exc_chain_repr_cycle0.mod.Err
During handling of the above exception, another exception occurred:
:15: in unreraise
raise e.__cause__
:8: in reraise
fail()
:5: in fail
return 0 / 0
E ZeroDivisionError: division by zero"""
)
assert out == expected_out
@pytest.mark.parametrize("style", ["short", "long"]) @pytest.mark.parametrize("style", ["short", "long"])
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"]) @pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])