Skip to content

Commit 7dceabf

Browse files
committed
Ensure fixtures obtained with getfixturevalue() are finalized in the correct order
Fix #1895
1 parent e1f97e4 commit 7dceabf

File tree

3 files changed

+20
-6
lines changed

3 files changed

+20
-6
lines changed

src/_pytest/fixtures.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -585,11 +585,13 @@ def _compute_fixture_value(self, fixturedef):
585585
# call the fixture function
586586
fixturedef.execute(request=subrequest)
587587
finally:
588-
# if fixture function failed it might have registered finalizers
589-
self.session._setupstate.addfinalizer(
590-
functools.partial(fixturedef.finish, request=subrequest),
591-
subrequest.node,
592-
)
588+
self._schedule_finalizers(fixturedef, subrequest)
589+
590+
def _schedule_finalizers(self, fixturedef, subrequest):
591+
# if fixture function failed it might have registered finalizers
592+
self.session._setupstate.addfinalizer(
593+
functools.partial(fixturedef.finish, request=subrequest), subrequest.node
594+
)
593595

594596
def _check_scope(self, argname, invoking_scope, requested_scope):
595597
if argname == "request":
@@ -659,6 +661,16 @@ def __repr__(self):
659661
def addfinalizer(self, finalizer):
660662
self._fixturedef.addfinalizer(finalizer)
661663

664+
def _schedule_finalizers(self, fixturedef, subrequest):
665+
# if the executing fixturedef was not explicitly requested in the argument list (via
666+
# getfixturevalue inside the fixture call) then ensure this fixture def will be finished
667+
# first
668+
if fixturedef.argname not in self.funcargnames:
669+
fixturedef.addfinalizer(
670+
functools.partial(self._fixturedef.finish, request=self)
671+
)
672+
super(SubRequest, self)._schedule_finalizers(fixturedef, subrequest)
673+
662674

663675
scopes = "session package module class function".split()
664676
scopenum_function = scopes.index("function")
@@ -858,6 +870,7 @@ def finish(self, request):
858870
def execute(self, request):
859871
# get required arguments and register our own finish()
860872
# with their finalization
873+
# TODO CHECK HOW TO AVOID EXPLICITLY FINALIZING AGAINST ARGNAMES
861874
for argname in self.argnames:
862875
fixturedef = request._get_active_fixturedef(argname)
863876
if argname != "request":

src/_pytest/runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def addfinalizer(self, finalizer, colitem):
327327
assert callable(finalizer)
328328
# assert colitem in self.stack # some unit tests don't setup stack :/
329329
self._finalizers.setdefault(colitem, []).append(finalizer)
330+
pass
330331

331332
def _pop_and_teardown(self):
332333
colitem = self.stack.pop()

testing/python/fixture.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1866,7 +1866,7 @@ def test_finish():
18661866
"setup-2", "step1-2", "step2-2", "teardown-2",]
18671867
"""
18681868
)
1869-
reprec = testdir.inline_run("-s")
1869+
reprec = testdir.inline_run("-v")
18701870
reprec.assertoutcome(passed=5)
18711871

18721872
def test_ordering_autouse_before_explicit(self, testdir):

0 commit comments

Comments
 (0)