From baed905c0b1a8381bd97167a691096e206f1a168 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 17 Jan 2024 15:44:38 +0100 Subject: [PATCH 01/10] Move scheduling of fixture finalization so it isn't rescheduled if the fixture value is cached. --- src/_pytest/fixtures.py | 29 ++++++++++------ .../test_scope_fixture_teardown_order.py | 33 +++++++++++++++++++ 2 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 testing/python/test_scope_fixture_teardown_order.py diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c294ec586b5..5edb7c4a38b 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -538,6 +538,8 @@ def getfixturevalue(self, argname: str) -> Any: :raises pytest.FixtureLookupError: If the given fixture could not be found. """ + # Note: This is called during setup for evaluating fixtures defined via + # function arguments as well. fixturedef = self._get_active_fixturedef(argname) assert fixturedef.cached_result is not None, ( f'The fixture value for "{argname}" is not available. ' @@ -574,9 +576,8 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: """Create a SubRequest based on "self" and call the execute method of the given FixtureDef object. - This will force the FixtureDef object to throw away any previous - results and compute a new fixture value, which will be stored into - the FixtureDef object itself. + If the FixtureDef has cached the result it will do nothing, otherwise it will + setup and run the fixture, cache the value, and schedule a finalizer for it. """ # prepare a subrequest object before calling fixture function # (latter managed by fixturedef) @@ -641,11 +642,8 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: # Check if a higher-level scoped fixture accesses a lower level one. subrequest._check_scope(argname, self._scope, scope) - try: - # Call the fixture function. - fixturedef.execute(request=subrequest) - finally: - self._schedule_finalizers(fixturedef, subrequest) + # Make sure the fixture value is cached, running it if it isn't + fixturedef.execute(request=subrequest) def _schedule_finalizers( self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" @@ -1055,7 +1053,9 @@ def finish(self, request: SubRequest) -> None: self.cached_result = None self._finalizers.clear() + # Note: the return value is entirely unused, no tests depend on it def execute(self, request: SubRequest) -> FixtureValue: + finalizer = functools.partial(self.finish, request=request) # Get required arguments and register our own finish() # with their finalization. for argname in self.argnames: @@ -1063,7 +1063,7 @@ def execute(self, request: SubRequest) -> FixtureValue: if argname != "request": # PseudoFixtureDef is only for "request". assert isinstance(fixturedef, FixtureDef) - fixturedef.addfinalizer(functools.partial(self.finish, request=request)) + fixturedef.addfinalizer(finalizer) my_cache_key = self.cache_key(request) if self.cached_result is not None: @@ -1073,6 +1073,7 @@ def execute(self, request: SubRequest) -> FixtureValue: if my_cache_key is cache_key: if self.cached_result[2] is not None: exc = self.cached_result[2] + # this would previously trigger adding a finalizer. Should it? raise exc else: result = self.cached_result[0] @@ -1083,7 +1084,15 @@ def execute(self, request: SubRequest) -> FixtureValue: assert self.cached_result is None ihook = request.node.ihook - result = ihook.pytest_fixture_setup(fixturedef=self, request=request) + try: + # Setup the fixture, run the code in it, and cache the value + # in self.cached_result + result = ihook.pytest_fixture_setup(fixturedef=self, request=request) + finally: + # schedule our finalizer, even if the setup failed + request.node.addfinalizer(finalizer) + + # note: unused return result def cache_key(self, request: SubRequest) -> object: diff --git a/testing/python/test_scope_fixture_teardown_order.py b/testing/python/test_scope_fixture_teardown_order.py new file mode 100644 index 00000000000..fdff19af2a1 --- /dev/null +++ b/testing/python/test_scope_fixture_teardown_order.py @@ -0,0 +1,33 @@ +import pytest + +last_executed = "" + + +@pytest.fixture(scope="module") +def fixture_1(): + global last_executed + assert last_executed == "" + last_executed = "autouse_setup" + yield + assert last_executed == "noautouse_teardown" + last_executed = "autouse_teardown" + + +@pytest.fixture(scope="module") +def fixture_2(): + global last_executed + assert last_executed == "autouse_setup" + last_executed = "noautouse_setup" + yield + assert last_executed == "run_test" + last_executed = "noautouse_teardown" + + +def test_autouse_fixture_teardown_order(fixture_1, fixture_2): + global last_executed + assert last_executed == "noautouse_setup" + last_executed = "run_test" + + +def test_2(fixture_1): + pass From 3356fa77226757f75e12223034cd4173d0d108dd Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 17 Jan 2024 16:31:43 +0100 Subject: [PATCH 02/10] add changelog --- changelog/1489.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/1489.bugfix.rst diff --git a/changelog/1489.bugfix.rst b/changelog/1489.bugfix.rst new file mode 100644 index 00000000000..70c5dd1252e --- /dev/null +++ b/changelog/1489.bugfix.rst @@ -0,0 +1 @@ +Fix some instances where teardown of higher-scoped fixtures was not happening in the reverse order they were initialized in. From f76a77b06831de9732ed045f32fe94edaec5d189 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 13 Feb 2024 13:41:21 +0100 Subject: [PATCH 03/10] Add test for finalizer in a fixture with a cached exception --- testing/python/test_scope_fixture_caching.py | 63 ++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 testing/python/test_scope_fixture_caching.py diff --git a/testing/python/test_scope_fixture_caching.py b/testing/python/test_scope_fixture_caching.py new file mode 100644 index 00000000000..21594fb1f30 --- /dev/null +++ b/testing/python/test_scope_fixture_caching.py @@ -0,0 +1,63 @@ +from typing import Generator + +import pytest + + +# These tests will fail if run out of order, or selectively ... so they should probably be defined in a different way + +executed: list[str] = [] + + +######### +# Make sure setup and finalization is only run once when using fixture multiple times +# I'm pretty sure there's other tests that checks this already though, though idr fully where +######### +@pytest.fixture(scope="class") +def fixture_1() -> Generator[None, None, None]: + executed.append("fix setup") + yield + executed.append("fix teardown") + + +class TestFixtureCaching: + def test_1(self, fixture_1: None) -> None: + assert executed == ["fix setup"] + + def test_2(self, fixture_1: None) -> None: + assert executed == ["fix setup"] + + +def test_expected_setup_and_teardown() -> None: + assert executed == ["fix setup", "fix teardown"] + + +###### +# Make sure setup & finalization is only run once, with a cached exception +###### +executed_crash: list[str] = [] + + +@pytest.fixture(scope="class") +def fixture_crash(request: pytest.FixtureRequest) -> None: + executed_crash.append("fix_crash setup") + + def my_finalizer() -> None: + executed_crash.append("fix_crash teardown") + + request.addfinalizer(my_finalizer) + + raise Exception("foo") + + +class TestFixtureCachingException: + @pytest.mark.xfail + def test_crash_1(self, fixture_crash: None) -> None: + assert False + + @pytest.mark.xfail + def test_crash_2(self, fixture_crash: None) -> None: + assert False + + +def test_crash_expected_setup_and_teardown() -> None: + assert executed_crash == ["fix_crash setup", "fix_crash teardown"] From 537a831a4f99f852e99cd2c88ccea50016891d73 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 13 Feb 2024 13:43:12 +0100 Subject: [PATCH 04/10] add typing to teardown_order test --- testing/python/test_scope_fixture_teardown_order.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/testing/python/test_scope_fixture_teardown_order.py b/testing/python/test_scope_fixture_teardown_order.py index fdff19af2a1..8ce97cd3262 100644 --- a/testing/python/test_scope_fixture_teardown_order.py +++ b/testing/python/test_scope_fixture_teardown_order.py @@ -1,10 +1,13 @@ +from typing import Generator + import pytest + last_executed = "" @pytest.fixture(scope="module") -def fixture_1(): +def fixture_1() -> Generator[None, None, None]: global last_executed assert last_executed == "" last_executed = "autouse_setup" @@ -14,7 +17,7 @@ def fixture_1(): @pytest.fixture(scope="module") -def fixture_2(): +def fixture_2() -> Generator[None, None, None]: global last_executed assert last_executed == "autouse_setup" last_executed = "noautouse_setup" @@ -23,11 +26,11 @@ def fixture_2(): last_executed = "noautouse_teardown" -def test_autouse_fixture_teardown_order(fixture_1, fixture_2): +def test_autouse_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None: global last_executed assert last_executed == "noautouse_setup" last_executed = "run_test" -def test_2(fixture_1): +def test_2(fixture_1: None) -> None: pass From d168ea41f4465208a993982913dc0e58c79146aa Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 13 Feb 2024 13:46:12 +0100 Subject: [PATCH 05/10] fix typing in test to work on runtime in py38 --- testing/python/test_scope_fixture_caching.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/python/test_scope_fixture_caching.py b/testing/python/test_scope_fixture_caching.py index 21594fb1f30..2a24a2f53a8 100644 --- a/testing/python/test_scope_fixture_caching.py +++ b/testing/python/test_scope_fixture_caching.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Generator import pytest From 3fc5c5530e9cb95904e578c1c4a9746abe26c964 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sun, 3 Mar 2024 11:25:52 +0100 Subject: [PATCH 06/10] remove some comments, remove now unused _schedule_finalizers(), move tests to a pytester test in testing/python/fixtures.py --- src/_pytest/fixtures.py | 24 ------ testing/python/fixtures.py | 77 ++++++++++++++++++++ testing/python/test_scope_fixture_caching.py | 65 ----------------- 3 files changed, 77 insertions(+), 89 deletions(-) delete mode 100644 testing/python/test_scope_fixture_caching.py diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index f724af6bae7..532330fa0fa 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -633,13 +633,6 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: # Make sure the fixture value is cached, running it if it isn't fixturedef.execute(request=subrequest) - def _schedule_finalizers( - self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" - ) -> None: - # If fixture function failed it might have registered finalizers. - finalizer = functools.partial(fixturedef.finish, request=subrequest) - subrequest.node.addfinalizer(finalizer) - @final class TopRequest(FixtureRequest): @@ -766,21 +759,6 @@ def _factorytraceback(self) -> List[str]: def addfinalizer(self, finalizer: Callable[[], object]) -> None: self._fixturedef.addfinalizer(finalizer) - def _schedule_finalizers( - self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" - ) -> None: - # 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._fixture_defs - and fixturedef.argname not in self._pyfuncitem.fixturenames - ): - fixturedef.addfinalizer( - functools.partial(self._fixturedef.finish, request=self) - ) - super()._schedule_finalizers(fixturedef, subrequest) - @final class FixtureLookupError(LookupError): @@ -1037,7 +1015,6 @@ def finish(self, request: SubRequest) -> None: self.cached_result = None self._finalizers.clear() - # Note: the return value is entirely unused, no tests depend on it def execute(self, request: SubRequest) -> FixtureValue: finalizer = functools.partial(self.finish, request=request) # Get required arguments and register our own finish() @@ -1076,7 +1053,6 @@ def execute(self, request: SubRequest) -> FixtureValue: # schedule our finalizer, even if the setup failed request.node.addfinalizer(finalizer) - # note: unused return result def cache_key(self, request: SubRequest) -> object: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 299e411a695..1b7a0e4141a 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4559,3 +4559,80 @@ def test_deduplicate_names() -> None: assert items == ("a", "b", "c", "d") items = deduplicate_names((*items, "g", "f", "g", "e", "b")) assert items == ("a", "b", "c", "d", "g", "f", "e") + + +def test_scope_fixture_caching_1(pytester: Pytester) -> None: + """ + Make sure setup and finalization is only run once when using fixture + multiple times. This might be a duplicate of another test.""" + pytester.makepyfile( + """ + from __future__ import annotations + + from typing import Generator + + import pytest + executed: list[str] = [] + @pytest.fixture(scope="class") + def fixture_1() -> Generator[None, None, None]: + executed.append("fix setup") + yield + executed.append("fix teardown") + + + class TestFixtureCaching: + def test_1(self, fixture_1: None) -> None: + assert executed == ["fix setup"] + + def test_2(self, fixture_1: None) -> None: + assert executed == ["fix setup"] + + + def test_expected_setup_and_teardown() -> None: + assert executed == ["fix setup", "fix teardown"] + """ + ) + result = pytester.runpytest() + assert result.ret == 0 + + +def test_scope_fixture_caching_2(pytester: Pytester) -> None: + """Make sure setup & finalization is only run once, with a cached exception.""" + pytester.makepyfile( + """ + from __future__ import annotations + + from typing import Generator + + import pytest + executed_crash: list[str] = [] + + + @pytest.fixture(scope="class") + def fixture_crash(request: pytest.FixtureRequest) -> None: + executed_crash.append("fix_crash setup") + + def my_finalizer() -> None: + executed_crash.append("fix_crash teardown") + + request.addfinalizer(my_finalizer) + + raise Exception("foo") + + + class TestFixtureCachingException: + @pytest.mark.xfail + def test_crash_1(self, fixture_crash: None) -> None: + ... + + @pytest.mark.xfail + def test_crash_2(self, fixture_crash: None) -> None: + ... + + + def test_crash_expected_setup_and_teardown() -> None: + assert executed_crash == ["fix_crash setup", "fix_crash teardown"] + """ + ) + result = pytester.runpytest() + assert result.ret == 0 diff --git a/testing/python/test_scope_fixture_caching.py b/testing/python/test_scope_fixture_caching.py deleted file mode 100644 index 2a24a2f53a8..00000000000 --- a/testing/python/test_scope_fixture_caching.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -from typing import Generator - -import pytest - - -# These tests will fail if run out of order, or selectively ... so they should probably be defined in a different way - -executed: list[str] = [] - - -######### -# Make sure setup and finalization is only run once when using fixture multiple times -# I'm pretty sure there's other tests that checks this already though, though idr fully where -######### -@pytest.fixture(scope="class") -def fixture_1() -> Generator[None, None, None]: - executed.append("fix setup") - yield - executed.append("fix teardown") - - -class TestFixtureCaching: - def test_1(self, fixture_1: None) -> None: - assert executed == ["fix setup"] - - def test_2(self, fixture_1: None) -> None: - assert executed == ["fix setup"] - - -def test_expected_setup_and_teardown() -> None: - assert executed == ["fix setup", "fix teardown"] - - -###### -# Make sure setup & finalization is only run once, with a cached exception -###### -executed_crash: list[str] = [] - - -@pytest.fixture(scope="class") -def fixture_crash(request: pytest.FixtureRequest) -> None: - executed_crash.append("fix_crash setup") - - def my_finalizer() -> None: - executed_crash.append("fix_crash teardown") - - request.addfinalizer(my_finalizer) - - raise Exception("foo") - - -class TestFixtureCachingException: - @pytest.mark.xfail - def test_crash_1(self, fixture_crash: None) -> None: - assert False - - @pytest.mark.xfail - def test_crash_2(self, fixture_crash: None) -> None: - assert False - - -def test_crash_expected_setup_and_teardown() -> None: - assert executed_crash == ["fix_crash setup", "fix_crash teardown"] From 007d24a6faaa4e3eb8f4a7bc981093c2d7ce631a Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sun, 3 Mar 2024 13:22:23 +0100 Subject: [PATCH 07/10] improve comment getfixturevalue, move test_scoped_fixture_teardown_order to testing/python/fixtures.py --- src/_pytest/fixtures.py | 7 ++- testing/python/fixtures.py | 54 +++++++++++++++++++ .../test_scope_fixture_teardown_order.py | 36 ------------- 3 files changed, 59 insertions(+), 38 deletions(-) delete mode 100644 testing/python/test_scope_fixture_teardown_order.py diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 532330fa0fa..fa727513c13 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -531,8 +531,11 @@ def getfixturevalue(self, argname: str) -> Any: :raises pytest.FixtureLookupError: If the given fixture could not be found. """ - # Note: This is called during setup for evaluating fixtures defined via - # function arguments as well. + # Note that in addition to the use case described in the docstring, + # getfixturevalue() is also called by pytest itself during item setup to + # evaluate the fixtures that are requested statically + # (using function parameters, autouse, etc). + fixturedef = self._get_active_fixturedef(argname) assert fixturedef.cached_result is not None, ( f'The fixture value for "{argname}" is not available. ' diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 1b7a0e4141a..0758b590ca0 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4561,6 +4561,60 @@ def test_deduplicate_names() -> None: assert items == ("a", "b", "c", "d", "g", "f", "e") +def test_scoped_fixture_teardown_order(pytester: Pytester) -> None: + """ + Make sure teardowns happen in reverse order of setup with scoped fixtures, when + a later test only depends on a subset of scoped fixtures. + Regression test for https://github.com/pytest-dev/pytest/issues/1489 + """ + pytester.makepyfile( + """ + from typing import Generator + + import pytest + + + last_executed = "" + + + @pytest.fixture(scope="module") + def fixture_1() -> Generator[None, None, None]: + global last_executed + assert last_executed == "" + last_executed = "autouse_setup" + yield + assert last_executed == "noautouse_teardown" + last_executed = "autouse_teardown" + + + @pytest.fixture(scope="module") + def fixture_2() -> Generator[None, None, None]: + global last_executed + assert last_executed == "autouse_setup" + last_executed = "noautouse_setup" + yield + assert last_executed == "run_test" + last_executed = "noautouse_teardown" + + + def test_autouse_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None: + global last_executed + assert last_executed == "noautouse_setup" + last_executed = "run_test" + + + def test_2(fixture_1: None) -> None: + # this would previously queue an additional teardown of fixture_1, + # despite fixture_1's value being cached, which caused fixture_1 to be + # torn down before fixture_2 - violating the rule that teardowns should + # happen in reverse order of setup. + pass + """ + ) + result = pytester.runpytest() + assert result.ret == 0 + + def test_scope_fixture_caching_1(pytester: Pytester) -> None: """ Make sure setup and finalization is only run once when using fixture diff --git a/testing/python/test_scope_fixture_teardown_order.py b/testing/python/test_scope_fixture_teardown_order.py deleted file mode 100644 index 8ce97cd3262..00000000000 --- a/testing/python/test_scope_fixture_teardown_order.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import Generator - -import pytest - - -last_executed = "" - - -@pytest.fixture(scope="module") -def fixture_1() -> Generator[None, None, None]: - global last_executed - assert last_executed == "" - last_executed = "autouse_setup" - yield - assert last_executed == "noautouse_teardown" - last_executed = "autouse_teardown" - - -@pytest.fixture(scope="module") -def fixture_2() -> Generator[None, None, None]: - global last_executed - assert last_executed == "autouse_setup" - last_executed = "noautouse_setup" - yield - assert last_executed == "run_test" - last_executed = "noautouse_teardown" - - -def test_autouse_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None: - global last_executed - assert last_executed == "noautouse_setup" - last_executed = "run_test" - - -def test_2(fixture_1: None) -> None: - pass From 73ae9649188d312fef29316e0efce58453542b63 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Fri, 15 Mar 2024 15:54:17 +0100 Subject: [PATCH 08/10] remove test_scope_fixture_caching1/2, they're now in a separate PR --- testing/python/fixtures.py | 77 -------------------------------------- 1 file changed, 77 deletions(-) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 6f469542b80..d525cae878e 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4676,80 +4676,3 @@ def test_2(fixture_1: None) -> None: ) result = pytester.runpytest() assert result.ret == 0 - - -def test_scope_fixture_caching_1(pytester: Pytester) -> None: - """ - Make sure setup and finalization is only run once when using fixture - multiple times. This might be a duplicate of another test.""" - pytester.makepyfile( - """ - from __future__ import annotations - - from typing import Generator - - import pytest - executed: list[str] = [] - @pytest.fixture(scope="class") - def fixture_1() -> Generator[None, None, None]: - executed.append("fix setup") - yield - executed.append("fix teardown") - - - class TestFixtureCaching: - def test_1(self, fixture_1: None) -> None: - assert executed == ["fix setup"] - - def test_2(self, fixture_1: None) -> None: - assert executed == ["fix setup"] - - - def test_expected_setup_and_teardown() -> None: - assert executed == ["fix setup", "fix teardown"] - """ - ) - result = pytester.runpytest() - assert result.ret == 0 - - -def test_scope_fixture_caching_2(pytester: Pytester) -> None: - """Make sure setup & finalization is only run once, with a cached exception.""" - pytester.makepyfile( - """ - from __future__ import annotations - - from typing import Generator - - import pytest - executed_crash: list[str] = [] - - - @pytest.fixture(scope="class") - def fixture_crash(request: pytest.FixtureRequest) -> None: - executed_crash.append("fix_crash setup") - - def my_finalizer() -> None: - executed_crash.append("fix_crash teardown") - - request.addfinalizer(my_finalizer) - - raise Exception("foo") - - - class TestFixtureCachingException: - @pytest.mark.xfail - def test_crash_1(self, fixture_crash: None) -> None: - ... - - @pytest.mark.xfail - def test_crash_2(self, fixture_crash: None) -> None: - ... - - - def test_crash_expected_setup_and_teardown() -> None: - assert executed_crash == ["fix_crash setup", "fix_crash teardown"] - """ - ) - result = pytester.runpytest() - assert result.ret == 0 From 43f394f128e2acc44db53238093fddb8cf37f935 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 16 Mar 2024 23:18:32 +0200 Subject: [PATCH 09/10] Update src/_pytest/fixtures.py --- src/_pytest/fixtures.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 55dc71fee82..48429a023ac 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -544,10 +544,9 @@ def getfixturevalue(self, argname: str) -> Any: If the given fixture could not be found. """ # Note that in addition to the use case described in the docstring, - # getfixturevalue() is also called by pytest itself during item setup to - # evaluate the fixtures that are requested statically + # getfixturevalue() is also called by pytest itself during item and fixture + # setup to evaluate the fixtures that are requested statically # (using function parameters, autouse, etc). - # As well as called by `pytest_fixture_setup()` fixturedef = self._get_active_fixturedef(argname) assert fixturedef.cached_result is not None, ( From 13d104948ae2af443d1d705b22cd567f835a3feb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Mar 2024 21:23:34 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/python/fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 7984d45692d..1e22270e51b 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4697,12 +4697,12 @@ def test_crash_expected_setup_and_teardown() -> None: result = pytester.runpytest() assert result.ret == 0 - + def test_scoped_fixture_teardown_order(pytester: Pytester) -> None: """ Make sure teardowns happen in reverse order of setup with scoped fixtures, when a later test only depends on a subset of scoped fixtures. - + Regression test for https://github.com/pytest-dev/pytest/issues/1489 """ pytester.makepyfile( @@ -4750,4 +4750,4 @@ def test_2(fixture_1: None) -> None: """ ) result = pytester.runpytest() - assert result.ret == 0 \ No newline at end of file + assert result.ret == 0