Revert "Correctly handle tracebackhide for chained exceptions (#10772)"
This reverts commit 431ec6d34e
.
Fix #10903.
Reopen #1904.
This commit is contained in:
parent
61f7c27ec0
commit
90412827c3
1
AUTHORS
1
AUTHORS
|
@ -128,7 +128,6 @@ Erik M. Bray
|
||||||
Evan Kepner
|
Evan Kepner
|
||||||
Fabien Zarifian
|
Fabien Zarifian
|
||||||
Fabio Zadrozny
|
Fabio Zadrozny
|
||||||
Felix Hofstätter
|
|
||||||
Felix Nieuwenhuizen
|
Felix Nieuwenhuizen
|
||||||
Feng Ma
|
Feng Ma
|
||||||
Florian Bruhin
|
Florian Bruhin
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix crash ``INTERNALERROR IndexError: list index out of range`` which happens when displaying an exception where all entries are hidden.
|
||||||
|
This reverts the change "Correctly handle ``__tracebackhide__`` for chained exceptions." introduced in version 7.3.0.
|
|
@ -82,6 +82,7 @@ Bug Fixes
|
||||||
|
|
||||||
|
|
||||||
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Correctly handle ``__tracebackhide__`` for chained exceptions.
|
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Correctly handle ``__tracebackhide__`` for chained exceptions.
|
||||||
|
NOTE: This change was reverted in version 7.3.1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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) -> Optional[TracebackEntry]:
|
def getcrashentry(self) -> 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 None
|
return self[-1]
|
||||||
|
|
||||||
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,13 +602,11 @@ class ExceptionInfo(Generic[E]):
|
||||||
"""
|
"""
|
||||||
return isinstance(self.value, exc)
|
return isinstance(self.value, exc)
|
||||||
|
|
||||||
def _getreprcrash(self) -> Optional["ReprFileLocation"]:
|
def _getreprcrash(self) -> "ReprFileLocation":
|
||||||
exconly = self.exconly(tryshort=True)
|
exconly = self.exconly(tryshort=True)
|
||||||
entry = self.traceback.getcrashentry()
|
entry = self.traceback.getcrashentry()
|
||||||
if entry:
|
|
||||||
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
|
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
|
||||||
return ReprFileLocation(path, lineno + 1, exconly)
|
return ReprFileLocation(path, lineno + 1, exconly)
|
||||||
return None
|
|
||||||
|
|
||||||
def getrepr(
|
def getrepr(
|
||||||
self,
|
self,
|
||||||
|
@ -946,14 +944,9 @@ class FormattedExcinfo:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
reprtraceback = self.repr_traceback(excinfo_)
|
reprtraceback = self.repr_traceback(excinfo_)
|
||||||
|
reprcrash: Optional[ReprFileLocation] = (
|
||||||
# will be None if all traceback entries are hidden
|
excinfo_._getreprcrash() if self.style != "value" else None
|
||||||
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.
|
||||||
|
@ -961,8 +954,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_ = (
|
||||||
|
@ -1053,7 +1046,7 @@ class ExceptionChainRepr(ExceptionRepr):
|
||||||
@dataclasses.dataclass(eq=False)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprExceptionInfo(ExceptionRepr):
|
class ReprExceptionInfo(ExceptionRepr):
|
||||||
reprtraceback: "ReprTraceback"
|
reprtraceback: "ReprTraceback"
|
||||||
reprcrash: Optional["ReprFileLocation"]
|
reprcrash: "ReprFileLocation"
|
||||||
|
|
||||||
def toterminal(self, tw: TerminalWriter) -> None:
|
def toterminal(self, tw: TerminalWriter) -> None:
|
||||||
self.reprtraceback.toterminal(tw)
|
self.reprtraceback.toterminal(tw)
|
||||||
|
|
|
@ -347,10 +347,6 @@ class TestReport(BaseReport):
|
||||||
elif isinstance(excinfo.value, skip.Exception):
|
elif isinstance(excinfo.value, skip.Exception):
|
||||||
outcome = "skipped"
|
outcome = "skipped"
|
||||||
r = excinfo._getreprcrash()
|
r = excinfo._getreprcrash()
|
||||||
if r is None:
|
|
||||||
raise ValueError(
|
|
||||||
"There should always be a traceback entry for skipping a test."
|
|
||||||
)
|
|
||||||
if excinfo.value._use_item_location:
|
if excinfo.value._use_item_location:
|
||||||
path, line = item.reportinfo()[:2]
|
path, line = item.reportinfo()[:2]
|
||||||
assert line is not None
|
assert line is not None
|
||||||
|
|
|
@ -294,7 +294,6 @@ class TestTraceback_f_g_h:
|
||||||
excinfo = pytest.raises(ValueError, f)
|
excinfo = pytest.raises(ValueError, f)
|
||||||
tb = excinfo.traceback
|
tb = excinfo.traceback
|
||||||
entry = tb.getcrashentry()
|
entry = tb.getcrashentry()
|
||||||
assert entry is not None
|
|
||||||
co = _pytest._code.Code.from_function(h)
|
co = _pytest._code.Code.from_function(h)
|
||||||
assert entry.frame.code.path == co.path
|
assert entry.frame.code.path == co.path
|
||||||
assert entry.lineno == co.firstlineno + 1
|
assert entry.lineno == co.firstlineno + 1
|
||||||
|
@ -312,7 +311,10 @@ class TestTraceback_f_g_h:
|
||||||
excinfo = pytest.raises(ValueError, f)
|
excinfo = pytest.raises(ValueError, f)
|
||||||
tb = excinfo.traceback
|
tb = excinfo.traceback
|
||||||
entry = tb.getcrashentry()
|
entry = tb.getcrashentry()
|
||||||
assert entry is None
|
co = _pytest._code.Code.from_function(g)
|
||||||
|
assert entry.frame.code.path == co.path
|
||||||
|
assert entry.lineno == co.firstlineno + 2
|
||||||
|
assert entry.frame.code.name == "g"
|
||||||
|
|
||||||
|
|
||||||
def test_excinfo_exconly():
|
def test_excinfo_exconly():
|
||||||
|
@ -1573,3 +1575,20 @@ def test_exceptiongroup(pytester: Pytester, outer_chain, inner_chain) -> None:
|
||||||
# with py>=3.11 does not depend on exceptiongroup, though there is a toxenv for it
|
# with py>=3.11 does not depend on exceptiongroup, though there is a toxenv for it
|
||||||
pytest.importorskip("exceptiongroup")
|
pytest.importorskip("exceptiongroup")
|
||||||
_exceptiongroup_common(pytester, outer_chain, inner_chain, native=False)
|
_exceptiongroup_common(pytester, outer_chain, inner_chain, native=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_all_entries_hidden_doesnt_crash(pytester: Pytester) -> None:
|
||||||
|
"""Regression test for #10903.
|
||||||
|
|
||||||
|
We're not really sure what should be *displayed* here, so this test
|
||||||
|
just verified that at least it doesn't crash.
|
||||||
|
"""
|
||||||
|
pytester.makepyfile(
|
||||||
|
"""
|
||||||
|
def test():
|
||||||
|
__tracebackhide__ = True
|
||||||
|
1 / 0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = pytester.runpytest()
|
||||||
|
assert result.ret == 1
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
def test_tbh_chained(testdir):
|
|
||||||
"""Ensure chained exceptions whose frames contain "__tracebackhide__" are not shown (#1904)."""
|
|
||||||
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