From 1acf56d033e801f34ddc78d1124d818d96ba31b7 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 15 May 2024 17:11:26 +0300 Subject: [PATCH] fixtures: fix non-working package-scope parametrization reordering The `.parent` was incorrectly removed in a21fb87a90974189c1b8b26189959507189bb3a1, but it was actually correct (well, it assumes that Module.path.parent == Package.path, which I'm not sure is always correct, but that's a different matter). Restore it. Fix #12328. --- changelog/12328.bugfix.rst | 1 + src/_pytest/fixtures.py | 3 ++- testing/python/fixtures.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 changelog/12328.bugfix.rst diff --git a/changelog/12328.bugfix.rst b/changelog/12328.bugfix.rst new file mode 100644 index 000000000..f33442585 --- /dev/null +++ b/changelog/12328.bugfix.rst @@ -0,0 +1 @@ +Fix a regression in pytest 8.0.0 where package-scoped parameterized items were not correctly reordered to minimize setups/teardowns in some cases. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5a290718f..a271f947d 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -187,7 +187,8 @@ def get_parametrized_fixture_keys( if scope is Scope.Session: scoped_item_path = None elif scope is Scope.Package: - scoped_item_path = item.path + # Package key = module's directory. + scoped_item_path = item.path.parent elif scope is Scope.Module: scoped_item_path = item.path elif scope is Scope.Class: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index aec0deb99..13ab4904c 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4274,6 +4274,39 @@ class TestScopeOrdering: request = TopRequest(items[0], _ispytest=True) assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split() + def test_parametrized_package_scope_reordering(self, pytester: Pytester) -> None: + """A paramaterized package-scoped fixture correctly reorders items to + minimize setups & teardowns. + + Regression test for #12328. + """ + pytester.makepyfile( + __init__="", + conftest=""" + import pytest + @pytest.fixture(scope="package", params=["a", "b"]) + def fix(request): + return request.param + """, + test_1="def test1(fix): pass", + test_2="def test2(fix): pass", + ) + + result = pytester.runpytest("--setup-plan") + assert result.ret == ExitCode.OK + result.stdout.fnmatch_lines( + [ + " SETUP P fix['a']", + " test_1.py::test1[a] (fixtures used: fix, request)", + " test_2.py::test2[a] (fixtures used: fix, request)", + " TEARDOWN P fix['a']", + " SETUP P fix['b']", + " test_1.py::test1[b] (fixtures used: fix, request)", + " test_2.py::test2[b] (fixtures used: fix, request)", + " TEARDOWN P fix['b']", + ], + ) + def test_multiple_packages(self, pytester: Pytester) -> None: """Complex test involving multiple package fixtures. Make sure teardowns are executed in order.