diff --git a/changelog/5585.breaking.rst b/changelog/5585.breaking.rst index 12b9a52dd..655c4a42e 100644 --- a/changelog/5585.breaking.rst +++ b/changelog/5585.breaking.rst @@ -2,3 +2,5 @@ As per our policy, the following features have been deprecated in the 5.X series removed: * The ``funcargnames`` read-only property of ``FixtureRequest``, ``Metafunc``, and ``Function`` classes. Use ``fixturenames`` attribute. + +* ``@pytest.fixture`` no longer supports positional arguments, pass all arguments by keyword instead. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index d5c7540ed..886bfd7fc 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -162,6 +162,13 @@ Removed Features As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after an appropriate period of deprecation has passed. +``pytest.fixture`` arguments are keyword only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionremoved:: 6.0 + +Passing arguments to pytest.fixture() as positional arguments has been removed - pass them by keyword instead. + ``funcargnames`` alias for ``fixturenames`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index d76144d60..2f3c80251 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -30,11 +30,6 @@ RESULT_LOG = PytestDeprecationWarning( "See https://docs.pytest.org/en/stable/deprecations.html#result-log-result-log for more information." ) -FIXTURE_POSITIONAL_ARGUMENTS = PytestDeprecationWarning( - "Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them " - "as a keyword argument instead." -) - NODE_USE_FROM_PARENT = UnformattedWarning( PytestDeprecationWarning, "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c0fdc587b..03d8f5394 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -2,7 +2,6 @@ import functools import inspect import os import sys -import warnings from collections import defaultdict from collections import deque from types import TracebackType @@ -46,7 +45,6 @@ from _pytest.compat import TYPE_CHECKING from _pytest.config import _PluggyPlugin from _pytest.config import Config from _pytest.config.argparsing import Parser -from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS from _pytest.mark import ParameterSet from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME @@ -1246,7 +1244,7 @@ def fixture( # noqa: F811 def fixture( # noqa: F811 fixture_function: Optional[_FixtureFunction] = None, - *args: Any, + *, scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", params: Optional[Iterable[object]] = None, autouse: bool = False, @@ -1308,53 +1306,6 @@ def fixture( # noqa: F811 name the decorated function ``fixture_`` and then use ``@pytest.fixture(name='')``. """ - # Positional arguments backward compatibility. - # If a kwarg is equal to its default, assume it was not explicitly - # passed, i.e. not duplicated. The more correct way is to use a - # **kwargs and check `in`, but that obfuscates the function signature. - if isinstance(fixture_function, str): - # It's actually the first positional argument, scope. - args = (fixture_function, *args) # type: ignore[unreachable] - fixture_function = None - duplicated_args = [] - if len(args) > 0: - if scope == "function": - scope = args[0] - else: - duplicated_args.append("scope") - if len(args) > 1: - if params is None: - params = args[1] - else: - duplicated_args.append("params") - if len(args) > 2: - if autouse is False: - autouse = args[2] - else: - duplicated_args.append("autouse") - if len(args) > 3: - if ids is None: - ids = args[3] - else: - duplicated_args.append("ids") - if len(args) > 4: - if name is None: - name = args[4] - else: - duplicated_args.append("name") - if len(args) > 5: - raise TypeError( - "fixture() takes 5 positional arguments but {} were given".format(len(args)) - ) - if duplicated_args: - raise TypeError( - "The fixture arguments are defined as positional and keyword: {}. " - "Use only keyword arguments.".format(", ".join(duplicated_args)) - ) - if args: - warnings.warn(FIXTURE_POSITIONAL_ARGUMENTS, stacklevel=2) - # End backward compatiblity. - fixture_marker = FixtureFunctionMarker( scope=scope, params=params, autouse=autouse, ids=ids, name=name, ) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index f00741772..d54583858 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4130,73 +4130,6 @@ def test_fixture_named_request(testdir): ) -def test_fixture_duplicated_arguments() -> None: - """Raise error if there are positional and keyword arguments for the same parameter (#1682).""" - with pytest.raises(TypeError) as excinfo: - - @pytest.fixture("session", scope="session") # type: ignore[call-overload] - def arg(arg): - pass - - assert ( - str(excinfo.value) - == "The fixture arguments are defined as positional and keyword: scope. " - "Use only keyword arguments." - ) - - with pytest.raises(TypeError) as excinfo: - - @pytest.fixture( # type: ignore[call-overload] - "function", - ["p1"], - True, - ["id1"], - "name", - scope="session", - params=["p1"], - autouse=True, - ids=["id1"], - name="name", - ) - def arg2(request): - pass - - assert ( - str(excinfo.value) - == "The fixture arguments are defined as positional and keyword: scope, params, autouse, ids, name. " - "Use only keyword arguments." - ) - - -def test_fixture_with_positionals() -> None: - """Raise warning, but the positionals should still works (#1682).""" - from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS - - with pytest.warns(pytest.PytestDeprecationWarning) as warnings: - - @pytest.fixture("function", [0], True) # type: ignore[call-overload] - def fixture_with_positionals(): - pass - - assert str(warnings[0].message) == str(FIXTURE_POSITIONAL_ARGUMENTS) - - assert fixture_with_positionals._pytestfixturefunction.scope == "function" - assert fixture_with_positionals._pytestfixturefunction.params == (0,) - assert fixture_with_positionals._pytestfixturefunction.autouse - - -def test_fixture_with_too_many_positionals() -> None: - with pytest.raises(TypeError) as excinfo: - - @pytest.fixture("function", [0], True, ["id"], "name", "extra") # type: ignore[call-overload] - def fixture_with_positionals(): - pass - - assert ( - str(excinfo.value) == "fixture() takes 5 positional arguments but 6 were given" - ) - - def test_indirect_fixture_does_not_break_scope(testdir): """Ensure that fixture scope is respected when using indirect fixtures (#570)""" testdir.makepyfile(