A few improvements

This commit is contained in:
Sadra Barikbin 2023-08-01 13:41:55 +03:30
parent 96be57a3d0
commit 40b2c09ecf
2 changed files with 24 additions and 62 deletions

View File

@ -14,7 +14,6 @@ from functools import partial
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import cast
from typing import Dict from typing import Dict
from typing import final from typing import final
from typing import Generator from typing import Generator
@ -498,8 +497,11 @@ class PyCollector(PyobjMixin, nodes.Collector):
if not metafunc._calls: if not metafunc._calls:
yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo)
else: else:
# Dynamic direct parametrization may have shadowed some fixtures, # Direct parametrizations taking place in module/class-specific
# so make sure we update what the function really needs. # `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() fixtureinfo.prune_dependency_tree()
for callspec in metafunc._calls: for callspec in metafunc._calls:
@ -1170,7 +1172,7 @@ def get_direct_param_fixture_func(request: FixtureRequest) -> Any:
return request.param 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]]]() name2pseudofixturedef_key = StashKey[Dict[str, FixtureDef[Any]]]()
@ -1330,8 +1332,8 @@ class Metafunc:
object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids)
# Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering # Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering
# artificial FixtureDef's so that later at test execution time we can rely # artificial "pseudo" FixtureDef's so that later at test execution time we can
# on a proper FixtureDef to exist for fixture setup. # rely on a proper FixtureDef to exist for fixture setup.
arg2fixturedefs = self._arg2fixturedefs arg2fixturedefs = self._arg2fixturedefs
node = None node = None
# If we have a scope that is higher than function, we need # If we have a scope that is higher than function, we need
@ -1339,7 +1341,8 @@ class Metafunc:
# a per-scope basis. We thus store and cache the fixturedef on the # a per-scope basis. We thus store and cache the fixturedef on the
# node related to the scope. # node related to the scope.
if scope_ is not Scope.Function: if scope_ is not Scope.Function:
collector = cast(nodes.Node, self.definition.parent) collector = self.definition.parent
assert collector is not None
node = get_scope_node(collector, scope_) node = get_scope_node(collector, scope_)
if node is None: if node is None:
# If used class scope and there is no class, use module-level # If used class scope and there is no class, use module-level

View File

@ -23,6 +23,7 @@ from _pytest.compat import getfuncargnames
from _pytest.compat import NOTSET from _pytest.compat import NOTSET
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
from _pytest.python import Function
from _pytest.python import IdMaker from _pytest.python import IdMaker
from _pytest.scope import Scope from _pytest.scope import Scope
@ -974,16 +975,6 @@ class TestMetafunc:
assert metafunc._calls[1].params == dict(x=3, y=4) assert metafunc._calls[1].params == dict(x=3, y=4)
assert metafunc._calls[1].id == "3-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: def test_high_scoped_parametrize_reordering(self, pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(
""" """
@ -1018,36 +1009,6 @@ class TestMetafunc:
] ]
) )
@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" <Function test\[a-0\]>",
r" <Function test\[a-2\]>",
r" <Function test\[b-1\]>",
]
)
def test_parametrize_multiple_times(self, pytester: Pytester) -> None: def test_parametrize_multiple_times(self, pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(
""" """
@ -1590,29 +1551,27 @@ class TestMetafuncFunctional:
def test_parametrize_module_level_test_with_class_scope( def test_parametrize_module_level_test_with_class_scope(
self, pytester: Pytester self, pytester: Pytester
) -> None: ) -> None:
pytester.makepyfile( module = pytester.makepyfile(
""" """
import pytest import pytest
@pytest.fixture
def item(request):
return request._pyfuncitem
fixturedef = None
@pytest.mark.parametrize("x", [0, 1], scope="class") @pytest.mark.parametrize("x", [0, 1], scope="class")
def test_1(item, x): def test_1(x):
global fixturedef pass
fixturedef = item._fixtureinfo.name2fixturedefs['x'][-1]
@pytest.mark.parametrize("x", [1, 2], scope="module") @pytest.mark.parametrize("x", [1, 2], scope="module")
def test_2(item, x): def test_2(x):
global fixturedef pass
assert fixturedef == item._fixtureinfo.name2fixturedefs['x'][-1]
""" """
) )
result = pytester.runpytest() test_1_0, _, test_2_0, _ = pytester.genitems((pytester.getmodulecol(module),))
assert result.ret == 0 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: class TestMetafuncFunctionalAuto: