diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 9182ce7df..64c43bdd0 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -464,6 +464,7 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): if not metafunc._calls: yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) else: + metafunc._recompute_direct_params_indices() # Direct parametrizations taking place in module/class-specific # `metafunc.parametrize` calls may have shadowed some fixtures, so make sure # we update what the function really needs a.k.a its fixture closure. Note that @@ -1131,6 +1132,8 @@ class Metafunc: # Result of parametrize(). self._calls: list[CallSpec2] = [] + self._params_directness: dict[str, Literal["indirect", "direct"]] = {} + def parametrize( self, argnames: str | Sequence[str], @@ -1273,6 +1276,7 @@ class Metafunc: name2pseudofixturedef_key, default ) arg_directness = self._resolve_args_directness(argnames, indirect) + self._params_directness.update(arg_directness) for argname in argnames: if arg_directness[argname] == "indirect": continue @@ -1445,6 +1449,12 @@ class Metafunc: pytrace=False, ) + def _recompute_direct_params_indices(self): + for argname, param_type in self._params_directness.items(): + if param_type == "direct": + for i, callspec in enumerate(self._calls): + callspec.indices[argname] = i + def _find_parametrized_scope( argnames: Sequence[str], diff --git a/testing/example_scripts/issue_519.py b/testing/example_scripts/issue_519.py index 138c07e95..da5f5ad6a 100644 --- a/testing/example_scripts/issue_519.py +++ b/testing/example_scripts/issue_519.py @@ -23,13 +23,13 @@ def checked_order(): assert order == [ ("issue_519.py", "fix1", "arg1v1"), ("test_one[arg1v1-arg2v1]", "fix2", "arg2v1"), - ("test_one[arg1v1-arg2v2]", "fix2", "arg2v2"), ("test_two[arg1v1-arg2v1]", "fix2", "arg2v1"), + ("test_one[arg1v1-arg2v2]", "fix2", "arg2v2"), ("test_two[arg1v1-arg2v2]", "fix2", "arg2v2"), ("issue_519.py", "fix1", "arg1v2"), ("test_one[arg1v2-arg2v1]", "fix2", "arg2v1"), - ("test_one[arg1v2-arg2v2]", "fix2", "arg2v2"), ("test_two[arg1v2-arg2v1]", "fix2", "arg2v1"), + ("test_one[arg1v2-arg2v2]", "fix2", "arg2v2"), ("test_two[arg1v2-arg2v2]", "fix2", "arg2v2"), ] diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index bc091bb1f..d05fd2096 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4878,3 +4878,37 @@ def test_subfixture_teardown_order(pytester: Pytester) -> None: ) result = pytester.runpytest() assert result.ret == 0 + + +def test_reordering_in_multiple_parametrization(pytester: Pytester) -> None: + pytester.makepyfile( + """ + import pytest + @pytest.mark.parametrize("arg2", [3, 4]) + @pytest.mark.parametrize("arg1", [0, 1, 2], scope='module') + def test1(arg1, arg2): + pass + + def test2(): + pass + + @pytest.mark.parametrize("arg1", [0, 1, 2], scope='module') + def test3(arg1): + pass + """ + ) + result = pytester.runpytest("--collect-only") + result.stdout.re_match_lines( + [ + r" ", + r" ", + r" ", + r" ", + r" ", + r" ", + r" ", + r" ", + r" ", + r" ", + ] + )