Correctly handle tracebackhide for chained exceptions
This commit is contained in:
parent
572b5657d7
commit
a583c68e15
|
@ -411,13 +411,13 @@ class Traceback(List[TracebackEntry]):
|
||||||
"""
|
"""
|
||||||
return Traceback(filter(fn, self), self._excinfo)
|
return Traceback(filter(fn, self), self._excinfo)
|
||||||
|
|
||||||
def getcrashentry(self) -> TracebackEntry:
|
def getcrashentry(self) -> Optional[TracebackEntry]:
|
||||||
"""Return last non-hidden traceback entry that lead to the exception of a traceback."""
|
"""Return last non-hidden traceback entry that lead to the exception of a traceback."""
|
||||||
for i in range(-1, -len(self) - 1, -1):
|
for i in range(-1, -len(self) - 1, -1):
|
||||||
entry = self[i]
|
entry = self[i]
|
||||||
if not entry.ishidden():
|
if not entry.ishidden():
|
||||||
return entry
|
return entry
|
||||||
return self[-1]
|
return None
|
||||||
|
|
||||||
def recursionindex(self) -> Optional[int]:
|
def recursionindex(self) -> Optional[int]:
|
||||||
"""Return the index of the frame/TracebackEntry where recursion originates if
|
"""Return the index of the frame/TracebackEntry where recursion originates if
|
||||||
|
@ -602,11 +602,13 @@ class ExceptionInfo(Generic[E]):
|
||||||
"""
|
"""
|
||||||
return isinstance(self.value, exc)
|
return isinstance(self.value, exc)
|
||||||
|
|
||||||
def _getreprcrash(self) -> "ReprFileLocation":
|
def _getreprcrash(self) -> Optional["ReprFileLocation"]:
|
||||||
exconly = self.exconly(tryshort=True)
|
exconly = self.exconly(tryshort=True)
|
||||||
entry = self.traceback.getcrashentry()
|
entry = self.traceback.getcrashentry()
|
||||||
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
|
if entry:
|
||||||
return ReprFileLocation(path, lineno + 1, exconly)
|
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
|
||||||
|
return ReprFileLocation(path, lineno + 1, exconly)
|
||||||
|
return None
|
||||||
|
|
||||||
def getrepr(
|
def getrepr(
|
||||||
self,
|
self,
|
||||||
|
@ -942,9 +944,14 @@ class FormattedExcinfo:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
reprtraceback = self.repr_traceback(excinfo_)
|
reprtraceback = self.repr_traceback(excinfo_)
|
||||||
reprcrash: Optional[ReprFileLocation] = (
|
|
||||||
excinfo_._getreprcrash() if self.style != "value" else None
|
# will be None if all traceback entries are hidden
|
||||||
)
|
reprcrash: Optional[ReprFileLocation] = excinfo_._getreprcrash()
|
||||||
|
if reprcrash:
|
||||||
|
if self.style == "value":
|
||||||
|
repr_chain += [(reprtraceback, None, descr)]
|
||||||
|
else:
|
||||||
|
repr_chain += [(reprtraceback, reprcrash, descr)]
|
||||||
else:
|
else:
|
||||||
# Fallback to native repr if the exception doesn't have a traceback:
|
# Fallback to native repr if the exception doesn't have a traceback:
|
||||||
# ExceptionInfo objects require a full traceback to work.
|
# ExceptionInfo objects require a full traceback to work.
|
||||||
|
@ -952,8 +959,8 @@ class FormattedExcinfo:
|
||||||
traceback.format_exception(type(e), e, None)
|
traceback.format_exception(type(e), e, None)
|
||||||
)
|
)
|
||||||
reprcrash = None
|
reprcrash = None
|
||||||
|
repr_chain += [(reprtraceback, reprcrash, descr)]
|
||||||
|
|
||||||
repr_chain += [(reprtraceback, reprcrash, descr)]
|
|
||||||
if e.__cause__ is not None and self.chain:
|
if e.__cause__ is not None and self.chain:
|
||||||
e = e.__cause__
|
e = e.__cause__
|
||||||
excinfo_ = (
|
excinfo_ = (
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
def test_tbh_chained(testdir):
|
||||||
|
p = testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
def f1():
|
||||||
|
__tracebackhide__ = True
|
||||||
|
try:
|
||||||
|
return f1.meh
|
||||||
|
except AttributeError:
|
||||||
|
pytest.fail("fail")
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fix():
|
||||||
|
f1()
|
||||||
|
|
||||||
|
|
||||||
|
def test(fix):
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest(str(p))
|
||||||
|
assert "'function' object has no attribute 'meh'" not in result.stdout.str()
|
||||||
|
assert result.ret == 1
|
Loading…
Reference in New Issue