From 7dceabfcb2a8d178761bd862d37e01cbcc23f6d1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 27 Feb 2019 21:10:37 -0300 Subject: [PATCH] Ensure fixtures obtained with getfixturevalue() are finalized in the correct order Fix #1895 --- src/_pytest/fixtures.py | 23 ++++++++++++++++++----- src/_pytest/runner.py | 1 + testing/python/fixture.py | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index b2ad9aae3..fa45dffc1 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -585,11 +585,13 @@ class FixtureRequest(FuncargnamesCompatAttr): # call the fixture function fixturedef.execute(request=subrequest) finally: - # if fixture function failed it might have registered finalizers - self.session._setupstate.addfinalizer( - functools.partial(fixturedef.finish, request=subrequest), - subrequest.node, - ) + self._schedule_finalizers(fixturedef, subrequest) + + def _schedule_finalizers(self, fixturedef, subrequest): + # 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): if argname == "request": @@ -659,6 +661,16 @@ class SubRequest(FixtureRequest): def addfinalizer(self, 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() scopenum_function = scopes.index("function") @@ -858,6 +870,7 @@ class FixtureDef(object): def execute(self, request): # get required arguments and register our own finish() # with their finalization + # TODO CHECK HOW TO AVOID EXPLICITLY FINALIZING AGAINST ARGNAMES for argname in self.argnames: fixturedef = request._get_active_fixturedef(argname) if argname != "request": diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 8357991fe..55dcd8054 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -327,6 +327,7 @@ class SetupState(object): assert callable(finalizer) # assert colitem in self.stack # some unit tests don't setup stack :/ self._finalizers.setdefault(colitem, []).append(finalizer) + pass def _pop_and_teardown(self): colitem = self.stack.pop() diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 3d557cec8..d9a512d70 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1866,7 +1866,7 @@ class TestAutouseManagement(object): "setup-2", "step1-2", "step2-2", "teardown-2",] """ ) - reprec = testdir.inline_run("-s") + reprec = testdir.inline_run("-v") reprec.assertoutcome(passed=5) def test_ordering_autouse_before_explicit(self, testdir):