Skip to content

Commit

Permalink
python: fix scope assignment for indirect parameter sets (#11277)
Browse files Browse the repository at this point in the history
Previously, when assigning a scope for a fully-indirect parameter set,
when there are multiple fixturedefs for a param (i.e. same-name fixture
chain), the highest scope was used, but it should be the lowest scope,
since that's the effective scope of the fixture.
  • Loading branch information
sadra-barikbin authored Aug 6, 2023
1 parent 1c04a92 commit e8a8a5f
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 3 deletions.
2 changes: 2 additions & 0 deletions changelog/11277.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed a bug that when there are multiple fixtures for an indirect parameter,
the scope of the highest-scope fixture is picked for the parameter set, instead of that of the one with the narrowest scope.
2 changes: 1 addition & 1 deletion src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ def node(self):
node: Optional[Union[nodes.Item, nodes.Collector]] = self._pyfuncitem
elif scope is Scope.Package:
# FIXME: _fixturedef is not defined on FixtureRequest (this class),
# but on FixtureRequest (a subclass).
# but on SubRequest (a subclass).
node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined]
else:
node = get_scope_node(self._pyfuncitem, scope)
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -1516,7 +1516,7 @@ def _find_parametrized_scope(
if all_arguments_are_fixtures:
fixturedefs = arg2fixturedefs or {}
used_scopes = [
fixturedef[0]._scope
fixturedef[-1]._scope
for name, fixturedef in fixturedefs.items()
if name in argnames
]
Expand Down Expand Up @@ -1682,7 +1682,7 @@ class Function(PyobjMixin, nodes.Item):
:param config:
The pytest Config object.
:param callspec:
If given, this is function has been parametrized and the callspec contains
If given, this function has been parametrized and the callspec contains
meta information about the parametrization.
:param callobj:
If given, the object which will be called when the Function is invoked,
Expand Down
62 changes: 62 additions & 0 deletions testing/python/metafunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class DummyFixtureDef:
module_fix=[DummyFixtureDef(Scope.Module)],
class_fix=[DummyFixtureDef(Scope.Class)],
func_fix=[DummyFixtureDef(Scope.Function)],
mixed_fix=[DummyFixtureDef(Scope.Module), DummyFixtureDef(Scope.Class)],
),
)

Expand Down Expand Up @@ -187,6 +188,7 @@ def find_scope(argnames, indirect):
)
== Scope.Module
)
assert find_scope(["mixed_fix"], indirect=True) == Scope.Class

def test_parametrize_and_id(self) -> None:
def func(x, y):
Expand Down Expand Up @@ -1503,6 +1505,66 @@ def test_it(x): pass
result = pytester.runpytest()
assert result.ret == 0

def test_reordering_with_scopeless_and_just_indirect_parametrization(
self, pytester: Pytester
) -> None:
pytester.makeconftest(
"""
import pytest
@pytest.fixture(scope="package")
def fixture1():
pass
"""
)
pytester.makepyfile(
"""
import pytest
@pytest.fixture(scope="module")
def fixture0():
pass
@pytest.fixture(scope="module")
def fixture1(fixture0):
pass
@pytest.mark.parametrize("fixture1", [0], indirect=True)
def test_0(fixture1):
pass
@pytest.fixture(scope="module")
def fixture():
pass
@pytest.mark.parametrize("fixture", [0], indirect=True)
def test_1(fixture):
pass
def test_2():
pass
class Test:
@pytest.fixture(scope="class")
def fixture(self, fixture):
pass
@pytest.mark.parametrize("fixture", [0], indirect=True)
def test_3(self, fixture):
pass
"""
)
result = pytester.runpytest("-v")
assert result.ret == 0
result.stdout.fnmatch_lines(
[
"*test_0*",
"*test_1*",
"*test_2*",
"*test_3*",
]
)


class TestMetafuncFunctionalAuto:
"""Tests related to automatically find out the correct scope for
Expand Down

0 comments on commit e8a8a5f

Please sign in to comment.