Drop support for positional arguments in @pytest.fixture

This commit is contained in:
Bruno Oliveira 2020-08-17 17:26:06 -03:00
parent 98530184a5
commit c747dc5248
5 changed files with 10 additions and 122 deletions

View File

@ -2,3 +2,5 @@ As per our policy, the following features have been deprecated in the 5.X series
removed: removed:
* The ``funcargnames`` read-only property of ``FixtureRequest``, ``Metafunc``, and ``Function`` classes. Use ``fixturenames`` attribute. * 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.

View File

@ -162,6 +162,13 @@ Removed Features
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed. 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`` ``funcargnames`` alias for ``fixturenames``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -30,11 +30,6 @@ RESULT_LOG = PytestDeprecationWarning(
"See https://docs.pytest.org/en/stable/deprecations.html#result-log-result-log for more information." "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( NODE_USE_FROM_PARENT = UnformattedWarning(
PytestDeprecationWarning, PytestDeprecationWarning,
"Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n"

View File

@ -2,7 +2,6 @@ import functools
import inspect import inspect
import os import os
import sys import sys
import warnings
from collections import defaultdict from collections import defaultdict
from collections import deque from collections import deque
from types import TracebackType from types import TracebackType
@ -46,7 +45,6 @@ from _pytest.compat import TYPE_CHECKING
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin
from _pytest.config import Config from _pytest.config import Config
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
from _pytest.mark import ParameterSet from _pytest.mark import ParameterSet
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME from _pytest.outcomes import TEST_OUTCOME
@ -1246,7 +1244,7 @@ def fixture( # noqa: F811
def fixture( # noqa: F811 def fixture( # noqa: F811
fixture_function: Optional[_FixtureFunction] = None, fixture_function: Optional[_FixtureFunction] = None,
*args: Any, *,
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
params: Optional[Iterable[object]] = None, params: Optional[Iterable[object]] = None,
autouse: bool = False, autouse: bool = False,
@ -1308,53 +1306,6 @@ def fixture( # noqa: F811
name the decorated function ``fixture_<fixturename>`` and then use name the decorated function ``fixture_<fixturename>`` and then use
``@pytest.fixture(name='<fixturename>')``. ``@pytest.fixture(name='<fixturename>')``.
""" """
# 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( fixture_marker = FixtureFunctionMarker(
scope=scope, params=params, autouse=autouse, ids=ids, name=name, scope=scope, params=params, autouse=autouse, ids=ids, name=name,
) )

View File

@ -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): def test_indirect_fixture_does_not_break_scope(testdir):
"""Ensure that fixture scope is respected when using indirect fixtures (#570)""" """Ensure that fixture scope is respected when using indirect fixtures (#570)"""
testdir.makepyfile( testdir.makepyfile(