From 9ac1f5c1a31cbb7274e4962bcf74efd804a781b7 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Tue, 1 Aug 2023 13:41:55 +0330 Subject: [PATCH] A few improvements --- src/_pytest/python.py | 13 ++++--- testing/python/metafunc.py | 69 ++++++++------------------------------ 2 files changed, 22 insertions(+), 60 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 0218c8ccdd9..82003749a30 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -498,8 +498,11 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]: if not metafunc._calls: yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) else: - # Dynamic direct parametrization may have shadowed some fixtures, - # so make sure we update what the function really needs. + # 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 + # direct parametrizations using `@pytest.mark.parametrize` have already been considered + # into making the closure using `ignore_args` arg to `getfixtureclosure`. fixtureinfo.prune_dependency_tree() for callspec in metafunc._calls: @@ -1170,7 +1173,7 @@ def get_direct_param_fixture_func(request: FixtureRequest) -> Any: return request.param -# Used for storing artificial fixturedefs for direct parametrization. +# Used for storing pseudo fixturedefs for direct parametrization. name2pseudofixturedef_key = StashKey[Dict[str, FixtureDef[Any]]]() @@ -1330,8 +1333,8 @@ def parametrize( object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) # Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering - # artificial FixtureDef's so that later at test execution time we can rely - # on a proper FixtureDef to exist for fixture setup. + # artificial "pseudo" FixtureDef's so that later at test execution time we can + # rely on a proper FixtureDef to exist for fixture setup. arg2fixturedefs = self._arg2fixturedefs node = None # If we have a scope that is higher than function, we need diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 40169aee04d..837883f0cb0 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -23,6 +23,7 @@ from _pytest.compat import NOTSET from _pytest.outcomes import fail from _pytest.pytester import Pytester +from _pytest.python import Function from _pytest.python import IdMaker from _pytest.scope import Scope @@ -974,16 +975,6 @@ def test_parametrize_twoargs(self) -> None: assert metafunc._calls[1].params == dict(x=3, y=4) assert metafunc._calls[1].id == "3-4" - @pytest.mark.xfail(reason="Will pass upon merging PR#11257") - def test_parametrize_with_duplicate_values(self) -> None: - metafunc = self.Metafunc(lambda x, y: None) - metafunc.parametrize(("x", "y"), [(1, 2), (3, 4), (1, 5), (2, 2)]) - assert len(metafunc._calls) == 4 - assert metafunc._calls[0].indices == dict(x=0, y=0) - assert metafunc._calls[1].indices == dict(x=1, y=1) - assert metafunc._calls[2].indices == dict(x=0, y=2) - assert metafunc._calls[3].indices == dict(x=2, y=0) - def test_high_scoped_parametrize_reordering(self, pytester: Pytester) -> None: pytester.makepyfile( """ @@ -1018,36 +1009,6 @@ def test3(arg1): ] ) - @pytest.mark.xfail(reason="Will pass upon merging PR#11257") - def test_high_scoped_parametrize_with_duplicate_values_reordering( - self, pytester: Pytester - ) -> None: - pytester.makepyfile( - """ - import pytest - - @pytest.fixture(scope='module') - def fixture1(request): - pass - - @pytest.fixture(scope='module') - def fixture2(request): - pass - - @pytest.mark.parametrize("fixture1, fixture2", [("a", 0), ("b", 1), ("a", 2)], indirect=True) - def test(fixture1, fixture2): - pass - """ - ) - result = pytester.runpytest("--collect-only") - result.stdout.re_match_lines( - [ - r" ", - r" ", - r" ", - ] - ) - def test_parametrize_multiple_times(self, pytester: Pytester) -> None: pytester.makepyfile( """ @@ -1590,29 +1551,27 @@ def test_it(x): pass def test_parametrize_module_level_test_with_class_scope( self, pytester: Pytester ) -> None: - pytester.makepyfile( + module = pytester.makepyfile( """ import pytest - @pytest.fixture - def item(request): - return request._pyfuncitem - - fixturedef = None - @pytest.mark.parametrize("x", [0, 1], scope="class") - def test_1(item, x): - global fixturedef - fixturedef = item._fixtureinfo.name2fixturedefs['x'][-1] + def test_1(x): + pass @pytest.mark.parametrize("x", [1, 2], scope="module") - def test_2(item, x): - global fixturedef - assert fixturedef == item._fixtureinfo.name2fixturedefs['x'][-1] + def test_2(x): + pass """ ) - result = pytester.runpytest() - assert result.ret == 0 + test_1_0, _, test_2_0, _ = pytester.genitems((pytester.getmodulecol(module),)) + test_1_fixture_x = cast(Function, test_1_0)._fixtureinfo.name2fixturedefs["x"][ + -1 + ] + test_2_fixture_x = cast(Function, test_2_0)._fixtureinfo.name2fixturedefs["x"][ + -1 + ] + assert test_1_fixture_x == test_2_fixture_x class TestMetafuncFunctionalAuto: