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 typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import final
from typing import Generator
@ -498,8 +497,11 @@ class PyCollector(PyobjMixin, nodes.Collector):
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 +1172,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 +1332,8 @@ class Metafunc:
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
@ -1339,7 +1341,8 @@ class Metafunc:
# a per-scope basis. We thus store and cache the fixturedef on the
# node related to the scope.
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_)
if node is None:
# 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.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 @@ class TestMetafunc:
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 @@ 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:
pytester.makepyfile(
"""
@ -1590,29 +1551,27 @@ class TestMetafuncFunctional:
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: