Skip to content

Commit 719f1ec

Browse files
Anton3bluetech
authored andcommitted
fixtures: improve handling of pytest_fixture_post_finalizer raising
Previously, if `pytest_fixture_post_finalizer` raised an error, the fixture was not reset properly, because the `FixtureDef.finish()` was disrupted. Fix this by making `pytest_fixture_post_finalizer` itself part of the teardown as a finalizer, which already has proper error handling. If it raises, it's handled (and shown to the user) the same as a teardown failure. Fix #14114.
1 parent 96728d5 commit 719f1ec

3 files changed

Lines changed: 64 additions & 1 deletion

File tree

changelog/14114.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
An exception from :hook:`pytest_fixture_post_finalizer` no longer prevents fixtures from being torn down, causing additional errors in the following tests.

src/_pytest/fixtures.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,6 @@ def finish(self, request: SubRequest) -> None:
10411041
except BaseException as e:
10421042
exceptions.append(e)
10431043
node = request.node
1044-
node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
10451044
# Even if finalization fails, we invalidate the cached fixture
10461045
# value and remove all finalizers because they may be bound methods
10471046
# which will keep instances alive.
@@ -1101,6 +1100,15 @@ def execute(self, request: SubRequest) -> FixtureValue:
11011100
for parent_fixture in requested_fixtures_that_should_finalize_us:
11021101
parent_fixture.addfinalizer(finalizer)
11031102

1103+
# Register the pytest_fixture_post_finalizer as the first finalizer,
1104+
# which is executed last.
1105+
assert not self._finalizers
1106+
self.addfinalizer(
1107+
lambda: request.node.ihook.pytest_fixture_post_finalizer(
1108+
fixturedef=self, request=request
1109+
)
1110+
)
1111+
11041112
ihook = request.node.ihook
11051113
try:
11061114
# Setup the fixture, run the code in it, and cache the value

testing/python/fixtures.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4281,6 +4281,60 @@ def test_second(foo, bar, baz):
42814281
result.assert_outcomes(passed=2)
42824282

42834283

4284+
def test_fixture_post_finalizer_hook_exception(pytester: Pytester) -> None:
4285+
"""Test that exceptions in pytest_fixture_post_finalizer hook are caught.
4286+
4287+
Also verifies that the fixture cache is properly reset even when the
4288+
post_finalizer hook raises an exception, so the fixture can be rebuilt
4289+
in subsequent tests.
4290+
"""
4291+
pytester.makeconftest(
4292+
"""
4293+
import pytest
4294+
4295+
def pytest_fixture_post_finalizer(fixturedef, request):
4296+
if "test_first" in request.node.nodeid:
4297+
raise RuntimeError("Error in post finalizer hook")
4298+
4299+
@pytest.fixture
4300+
def my_fixture(request):
4301+
yield request.node.nodeid
4302+
"""
4303+
)
4304+
pytester.makepyfile(
4305+
test_fixtures="""
4306+
def test_first(my_fixture):
4307+
assert "test_first" in my_fixture
4308+
4309+
def test_second(my_fixture):
4310+
assert "test_second" in my_fixture
4311+
"""
4312+
)
4313+
result = pytester.runpytest("-v", "--setup-show")
4314+
result.assert_outcomes(passed=2, errors=1)
4315+
result.stdout.fnmatch_lines(
4316+
[
4317+
"*test_first*PASSED",
4318+
"*test_first*ERROR",
4319+
"*RuntimeError: Error in post finalizer hook*",
4320+
]
4321+
)
4322+
# Verify fixture is setup twice (rebuilt for test_second despite error).
4323+
result.stdout.fnmatch_lines(
4324+
[
4325+
"test_fixtures.py::test_first ",
4326+
" SETUP F my_fixture",
4327+
" test_fixtures.py::test_first (fixtures used: my_fixture, request)PASSED",
4328+
"test_fixtures.py::test_first ERROR",
4329+
"test_fixtures.py::test_second ",
4330+
" SETUP F my_fixture",
4331+
" test_fixtures.py::test_second (fixtures used: my_fixture, request)PASSED",
4332+
" TEARDOWN F my_fixture",
4333+
],
4334+
consecutive=True,
4335+
)
4336+
4337+
42844338
class TestScopeOrdering:
42854339
"""Class of tests that ensure fixtures are ordered based on their scopes (#2405)"""
42864340

0 commit comments

Comments
 (0)