Skip to content

Commit

Permalink
Correctly handle tracebackhide for chained exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Felhof committed Feb 28, 2023
1 parent 572b565 commit a583c68
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 9 deletions.
25 changes: 16 additions & 9 deletions src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,13 +411,13 @@ def filter(
"""
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."""
for i in range(-1, -len(self) - 1, -1):
entry = self[i]
if not entry.ishidden():
return entry
return self[-1]
return None

def recursionindex(self) -> Optional[int]:
"""Return the index of the frame/TracebackEntry where recursion originates if
Expand Down Expand Up @@ -602,11 +602,13 @@ def errisinstance(
"""
return isinstance(self.value, exc)

def _getreprcrash(self) -> "ReprFileLocation":
def _getreprcrash(self) -> Optional["ReprFileLocation"]:
exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
return ReprFileLocation(path, lineno + 1, exconly)
if entry:
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
return ReprFileLocation(path, lineno + 1, exconly)
return None

def getrepr(
self,
Expand Down Expand Up @@ -942,18 +944,23 @@ def repr_excinfo(
)
else:
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:
# Fallback to native repr if the exception doesn't have a traceback:
# ExceptionInfo objects require a full traceback to work.
reprtraceback = ReprTracebackNative(
traceback.format_exception(type(e), e, None)
)
reprcrash = None
repr_chain += [(reprtraceback, reprcrash, descr)]

repr_chain += [(reprtraceback, reprcrash, descr)]
if e.__cause__ is not None and self.chain:
e = e.__cause__
excinfo_ = (
Expand Down
24 changes: 24 additions & 0 deletions testing/test_tracebackhide.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit a583c68

Please sign in to comment.