Skip to content

Commit 6db4724

Browse files
SalmonModeblueyed
authored andcommitted
cleaned up, DAMPified, and added tests to ensure fixtures only add ther finalizer to a dependee fixture once
1 parent ff05460 commit 6db4724

File tree

2 files changed

+45
-9
lines changed

2 files changed

+45
-9
lines changed

src/_pytest/fixtures.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -888,16 +888,13 @@ def finish(self, request):
888888

889889
def execute(self, request):
890890
for argname in self._dependee_fixture_argnames(request):
891+
if argname == "request":
892+
continue
891893
fixturedef = request._get_active_fixturedef(argname)
892-
if argname != "request":
893-
for fin in fixturedef._finalizers:
894-
if "request" in getattr(fin, "keywords", {}):
895-
if self == fin.keywords["request"]._fixturedef:
896-
break
897-
else:
898-
fixturedef.addfinalizer(
899-
functools.partial(self.finish, request=request)
900-
)
894+
if not self._will_be_finalized_by_fixture(fixturedef):
895+
fixturedef.addfinalizer(
896+
functools.partial(self.finish, request=request)
897+
)
901898

902899
my_cache_key = self.cache_key(request)
903900
cached_result = getattr(self, "cached_result", None)
@@ -917,6 +914,25 @@ def execute(self, request):
917914
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
918915
return hook.pytest_fixture_setup(fixturedef=self, request=request)
919916

917+
def _will_be_finalized_by_fixture(self, fixturedef):
918+
"""Whether or not this fixture be finalized by the passed fixture.
919+
920+
Every ``:class:FixtureDef`` keeps a list of all the finishers (tear downs) of
921+
other ``:class:FixtureDef`` instances that it should run before running its own.
922+
Finishers are added to this list not by this ``:class:FixtureDef``, but by the
923+
other ``:class:FixtureDef`` instances. They tell this instance that it's
924+
responsible for tearing them down before it tears itself down.
925+
926+
This method allows a ``:class:FixtureDef`` to check if it has already told
927+
another ``:class:FixtureDef`` that the latter ``:class:FixtureDef`` is
928+
responsible for tearing down this ``:class:FixtureDef``.
929+
"""
930+
for finalizer in fixturedef._finalizers:
931+
if "request" in getattr(finalizer, "keywords", {}):
932+
if self == finalizer.keywords["request"]._fixturedef:
933+
return True
934+
return False
935+
920936
def _dependee_fixture_argnames(self, request):
921937
"""A list of argnames for fixtures that this fixture depends on.
922938

testing/python/fixtures.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4374,3 +4374,23 @@ def test_suffix(fix_combined):
43744374
)
43754375
result = testdir.runpytest("-vv", str(p1))
43764376
assert result.ret == 0
4377+
4378+
4379+
class TestFinalizerOnlyAddedOnce:
4380+
4381+
@pytest.fixture(scope="class", autouse=True)
4382+
def a(self):
4383+
pass
4384+
4385+
@pytest.fixture(scope="class", autouse=True)
4386+
def b(self, a):
4387+
pass
4388+
4389+
def test_a_will_finalize_b(self, request):
4390+
a = request._get_active_fixturedef("a")
4391+
b = request._get_active_fixturedef("b")
4392+
assert b._will_be_finalized_by_fixture(a)
4393+
4394+
def test_a_only_finishes_one(self, request):
4395+
a = request._get_active_fixturedef("a")
4396+
assert len(a._finalizers)

0 commit comments

Comments
 (0)