diff --git a/changelog/11255.bugfix.rst b/changelog/11255.bugfix.rst new file mode 100644 index 000000000..2a2a42667 --- /dev/null +++ b/changelog/11255.bugfix.rst @@ -0,0 +1 @@ +Fixed crash on `parametrize(..., scope="package")` without a package present. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 516a595ef..c35f842db 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -155,6 +155,8 @@ name2pseudofixturedef_key = StashKey[Dict[str, "FixtureDef[Any]"]]() def add_funcarg_pseudo_fixture_def( collector: nodes.Collector, metafunc: "Metafunc", fixturemanager: "FixtureManager" ) -> None: + import _pytest.python + # This function will transform all collected calls to functions # if they use direct funcargs (i.e. direct parametrization) # because we want later test execution to be able to rely on @@ -192,11 +194,17 @@ def add_funcarg_pseudo_fixture_def( if scope is not Scope.Function: node = get_scope_node(collector, scope) if node is None: - assert scope is Scope.Class and isinstance( - collector, _pytest.python.Module - ) - # Use module-level collector for class-scope (for now). - node = collector + # If used class scope and there is no class, use module-level + # collector (for now). + if scope is Scope.Class: + assert isinstance(collector, _pytest.python.Module) + node = collector + # If used package scope and there is no package, use session + # (for now). + elif scope is Scope.Package: + node = collector.session + else: + assert False, f"Unhandled missing scope: {scope}" if node is None: name2pseudofixturedef = None else: diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 08ea8f910..bb4ae9d9a 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1486,6 +1486,23 @@ class TestMetafuncFunctional: ] ) + @pytest.mark.parametrize("scope", ["class", "package"]) + def test_parametrize_missing_scope_doesnt_crash( + self, pytester: Pytester, scope: str + ) -> None: + """Doesn't crash when parametrize(scope=) is used without a + corresponding node.""" + pytester.makepyfile( + f""" + import pytest + + @pytest.mark.parametrize("x", [0], scope="{scope}") + def test_it(x): pass + """ + ) + result = pytester.runpytest() + assert result.ret == 0 + class TestMetafuncFunctionalAuto: """Tests related to automatically find out the correct scope for