fixtures: fix tracebacks for higher-scoped failed fixtures getting longer and longer
Fix #12204.
This commit is contained in:
parent
127a372928
commit
0b91d5e3e8
|
@ -0,0 +1,4 @@
|
||||||
|
Fix a regression in pytest 8.0 where tracebacks get longer and longer when multiple tests fail due to a shared higher-scope fixture which raised.
|
||||||
|
|
||||||
|
The fix necessitated internal changes which may affect some plugins:
|
||||||
|
- ``FixtureDef.cached_result[2]`` is now a tuple ``(exc, tb)`` instead of ``exc``.
|
|
@ -8,6 +8,7 @@ import inspect
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
import types
|
||||||
from typing import AbstractSet
|
from typing import AbstractSet
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
@ -104,8 +105,8 @@ _FixtureCachedResult = Union[
|
||||||
None,
|
None,
|
||||||
# Cache key.
|
# Cache key.
|
||||||
object,
|
object,
|
||||||
# Exception if raised.
|
# The exception and the original traceback.
|
||||||
BaseException,
|
Tuple[BaseException, Optional[types.TracebackType]],
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1049,8 +1050,8 @@ class FixtureDef(Generic[FixtureValue]):
|
||||||
# numpy arrays (#6497).
|
# numpy arrays (#6497).
|
||||||
if my_cache_key is cache_key:
|
if my_cache_key is cache_key:
|
||||||
if self.cached_result[2] is not None:
|
if self.cached_result[2] is not None:
|
||||||
exc = self.cached_result[2]
|
exc, exc_tb = self.cached_result[2]
|
||||||
raise exc
|
raise exc.with_traceback(exc_tb)
|
||||||
else:
|
else:
|
||||||
result = self.cached_result[0]
|
result = self.cached_result[0]
|
||||||
return result
|
return result
|
||||||
|
@ -1126,7 +1127,7 @@ def pytest_fixture_setup(
|
||||||
# Don't show the fixture as the skip location, as then the user
|
# Don't show the fixture as the skip location, as then the user
|
||||||
# wouldn't know which test skipped.
|
# wouldn't know which test skipped.
|
||||||
e._use_item_location = True
|
e._use_item_location = True
|
||||||
fixturedef.cached_result = (None, my_cache_key, e)
|
fixturedef.cached_result = (None, my_cache_key, (e, e.__traceback__))
|
||||||
raise
|
raise
|
||||||
fixturedef.cached_result = (result, my_cache_key, None)
|
fixturedef.cached_result = (result, my_cache_key, None)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -3397,6 +3397,28 @@ class TestErrors:
|
||||||
["*def gen(qwe123):*", "*fixture*qwe123*not found*", "*1 error*"]
|
["*def gen(qwe123):*", "*fixture*qwe123*not found*", "*1 error*"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_cached_exception_doesnt_get_longer(self, pytester: Pytester) -> None:
|
||||||
|
"""Regression test for #12204."""
|
||||||
|
pytester.makepyfile(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def bad(): 1 / 0
|
||||||
|
|
||||||
|
def test_1(bad): pass
|
||||||
|
def test_2(bad): pass
|
||||||
|
def test_3(bad): pass
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = pytester.runpytest_inprocess("--tb=native")
|
||||||
|
assert result.ret == ExitCode.TESTS_FAILED
|
||||||
|
failures = result.reprec.getfailures() # type: ignore[attr-defined]
|
||||||
|
assert len(failures) == 3
|
||||||
|
lines1 = failures[1].longrepr.reprtraceback.reprentries[0].lines
|
||||||
|
lines2 = failures[2].longrepr.reprtraceback.reprentries[0].lines
|
||||||
|
assert len(lines1) == len(lines2)
|
||||||
|
|
||||||
|
|
||||||
class TestShowFixtures:
|
class TestShowFixtures:
|
||||||
def test_funcarg_compat(self, pytester: Pytester) -> None:
|
def test_funcarg_compat(self, pytester: Pytester) -> None:
|
||||||
|
|
Loading…
Reference in New Issue