Skip to content

Commit

Permalink
A few improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
sadra-barikbin committed Aug 1, 2023
1 parent 96be57a commit 40b2c09
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 62 deletions.
17 changes: 10 additions & 7 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
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
Expand Down Expand Up @@ -498,8 +497,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:
Expand Down Expand Up @@ -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]]]()


Expand Down Expand Up @@ -1330,16 +1332,17 @@ 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
# to make sure we only ever create an according fixturedef on
# 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
Expand Down
69 changes: 14 additions & 55 deletions testing/python/metafunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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(
"""
Expand Down Expand Up @@ -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" <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(
"""
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 40b2c09

Please sign in to comment.