Ensure fixtures obtained with getfixturevalue() are finalized in the correct order

Fix #1895
This commit is contained in:
Bruno Oliveira 2019-02-27 21:10:37 -03:00
parent e1f97e41e3
commit 7dceabfcb2
3 changed files with 20 additions and 6 deletions

View File

@ -585,11 +585,13 @@ class FixtureRequest(FuncargnamesCompatAttr):
# call the fixture function # call the fixture function
fixturedef.execute(request=subrequest) fixturedef.execute(request=subrequest)
finally: finally:
# if fixture function failed it might have registered finalizers self._schedule_finalizers(fixturedef, subrequest)
self.session._setupstate.addfinalizer(
functools.partial(fixturedef.finish, request=subrequest), def _schedule_finalizers(self, fixturedef, subrequest):
subrequest.node, # if fixture function failed it might have registered finalizers
) self.session._setupstate.addfinalizer(
functools.partial(fixturedef.finish, request=subrequest), subrequest.node
)
def _check_scope(self, argname, invoking_scope, requested_scope): def _check_scope(self, argname, invoking_scope, requested_scope):
if argname == "request": if argname == "request":
@ -659,6 +661,16 @@ class SubRequest(FixtureRequest):
def addfinalizer(self, finalizer): def addfinalizer(self, finalizer):
self._fixturedef.addfinalizer(finalizer) self._fixturedef.addfinalizer(finalizer)
def _schedule_finalizers(self, fixturedef, subrequest):
# if the executing fixturedef was not explicitly requested in the argument list (via
# getfixturevalue inside the fixture call) then ensure this fixture def will be finished
# first
if fixturedef.argname not in self.funcargnames:
fixturedef.addfinalizer(
functools.partial(self._fixturedef.finish, request=self)
)
super(SubRequest, self)._schedule_finalizers(fixturedef, subrequest)
scopes = "session package module class function".split() scopes = "session package module class function".split()
scopenum_function = scopes.index("function") scopenum_function = scopes.index("function")
@ -858,6 +870,7 @@ class FixtureDef(object):
def execute(self, request): def execute(self, request):
# get required arguments and register our own finish() # get required arguments and register our own finish()
# with their finalization # with their finalization
# TODO CHECK HOW TO AVOID EXPLICITLY FINALIZING AGAINST ARGNAMES
for argname in self.argnames: for argname in self.argnames:
fixturedef = request._get_active_fixturedef(argname) fixturedef = request._get_active_fixturedef(argname)
if argname != "request": if argname != "request":

View File

@ -327,6 +327,7 @@ class SetupState(object):
assert callable(finalizer) assert callable(finalizer)
# assert colitem in self.stack # some unit tests don't setup stack :/ # assert colitem in self.stack # some unit tests don't setup stack :/
self._finalizers.setdefault(colitem, []).append(finalizer) self._finalizers.setdefault(colitem, []).append(finalizer)
pass
def _pop_and_teardown(self): def _pop_and_teardown(self):
colitem = self.stack.pop() colitem = self.stack.pop()

View File

@ -1866,7 +1866,7 @@ class TestAutouseManagement(object):
"setup-2", "step1-2", "step2-2", "teardown-2",] "setup-2", "step1-2", "step2-2", "teardown-2",]
""" """
) )
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-v")
reprec.assertoutcome(passed=5) reprec.assertoutcome(passed=5)
def test_ordering_autouse_before_explicit(self, testdir): def test_ordering_autouse_before_explicit(self, testdir):