Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "[parametrize] enforce explicit argnames declaration (#6330)" #6914

Merged
merged 1 commit into from Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/6909.bugfix.rst
@@ -0,0 +1,3 @@
Revert the change introduced by `#6330 <https://github.com/pytest-dev/pytest/pull/6330>`_, which required all arguments to ``@pytest.mark.parametrize`` to be explicitly defined in the function signature.

The intention of the original change was to remove what was expected to be an unintended/surprising behavior, but it turns out many people relied on it, so the restriction has been reverted.
3 changes: 0 additions & 3 deletions doc/en/example/parametrize.rst
Expand Up @@ -402,9 +402,6 @@ The result of this test will be successful:

.. regendoc:wipe

Note, that each argument in `parametrize` list should be explicitly declared in corresponding
python test function or via `indirect`.

Parametrizing test methods through per-class configuration
--------------------------------------------------------------

Expand Down
16 changes: 5 additions & 11 deletions src/_pytest/fixtures.py
@@ -1,5 +1,6 @@
import functools
import inspect
import itertools
import sys
import warnings
from collections import defaultdict
Expand Down Expand Up @@ -1277,8 +1278,10 @@ def getfixtureinfo(self, node, func, cls, funcargs=True):
else:
argnames = ()

usefixtures = get_use_fixtures_for_node(node)
initialnames = usefixtures + argnames
usefixtures = itertools.chain.from_iterable(
mark.args for mark in node.iter_markers(name="usefixtures")
)
initialnames = tuple(usefixtures) + argnames
fm = node.session._fixturemanager
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
Expand Down Expand Up @@ -1475,12 +1478,3 @@ def _matchfactories(self, fixturedefs, nodeid):
for fixturedef in fixturedefs:
if nodes.ischildnode(fixturedef.baseid, nodeid):
yield fixturedef


def get_use_fixtures_for_node(node) -> Tuple[str, ...]:
"""Returns the names of all the usefixtures() marks on the given node"""
return tuple(
str(name)
for mark in node.iter_markers(name="usefixtures")
for name in mark.args
)
35 changes: 0 additions & 35 deletions src/_pytest/python.py
Expand Up @@ -936,8 +936,6 @@ def parametrize(

arg_values_types = self._resolve_arg_value_types(argnames, indirect)

self._validate_explicit_parameters(argnames, indirect)

# Use any already (possibly) generated ids with parametrize Marks.
if _param_mark and _param_mark._param_ids_from:
generated_ids = _param_mark._param_ids_from._param_ids_generated
Expand Down Expand Up @@ -1110,39 +1108,6 @@ def _validate_if_using_arg_names(
pytrace=False,
)

def _validate_explicit_parameters(
self,
argnames: typing.Sequence[str],
indirect: Union[bool, typing.Sequence[str]],
) -> None:
"""
The argnames in *parametrize* should either be declared explicitly via
indirect list or in the function signature

:param List[str] argnames: list of argument names passed to ``parametrize()``.
:param indirect: same ``indirect`` parameter of ``parametrize()``.
:raise ValueError: if validation fails
"""
if isinstance(indirect, bool):
parametrized_argnames = [] if indirect else argnames
else:
parametrized_argnames = [arg for arg in argnames if arg not in indirect]

if not parametrized_argnames:
return

funcargnames = _pytest.compat.getfuncargnames(self.function)
usefixtures = fixtures.get_use_fixtures_for_node(self.definition)

for arg in parametrized_argnames:
if arg not in funcargnames and arg not in usefixtures:
func_name = self.function.__name__
msg = (
'In function "{func_name}":\n'
'Parameter "{arg}" should be declared explicitly via indirect or in function itself'
).format(func_name=func_name, arg=arg)
fail(msg, pytrace=False)


def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
"""Find the most appropriate scope for a parametrized call based on its arguments.
Expand Down
2 changes: 1 addition & 1 deletion testing/python/collect.py
Expand Up @@ -463,7 +463,7 @@ def fix3():
return '3'

@pytest.mark.parametrize('fix2', ['2'])
def test_it(fix1, fix2):
def test_it(fix1):
assert fix1 == '21'
assert not fix3_instantiated
"""
Expand Down
51 changes: 0 additions & 51 deletions testing/python/metafunc.py
Expand Up @@ -36,9 +36,6 @@ def __init__(self, names):
class DefinitionMock(python.FunctionDefinition):
obj = attr.ib()

def listchain(self):
return []

names = fixtures.getfuncargnames(func)
fixtureinfo = FuncFixtureInfoMock(names) # type: Any
definition = DefinitionMock._create(func) # type: Any
Expand Down Expand Up @@ -1902,51 +1899,3 @@ def test_converted_to_str(a, b):
"*= 6 passed in *",
]
)

def test_parametrize_explicit_parameters_func(self, testdir: Testdir) -> None:
testdir.makepyfile(
"""
import pytest


@pytest.fixture
def fixture(arg):
return arg

@pytest.mark.parametrize("arg", ["baz"])
def test_without_arg(fixture):
assert "baz" == fixture
"""
)
result = testdir.runpytest()
result.assert_outcomes(error=1)
result.stdout.fnmatch_lines(
[
'*In function "test_without_arg"*',
'*Parameter "arg" should be declared explicitly via indirect or in function itself*',
]
)

def test_parametrize_explicit_parameters_method(self, testdir: Testdir) -> None:
testdir.makepyfile(
"""
import pytest

class Test:
@pytest.fixture
def test_fixture(self, argument):
return argument

@pytest.mark.parametrize("argument", ["foobar"])
def test_without_argument(self, test_fixture):
assert "foobar" == test_fixture
"""
)
result = testdir.runpytest()
result.assert_outcomes(error=1)
result.stdout.fnmatch_lines(
[
'*In function "test_without_argument"*',
'*Parameter "argument" should be declared explicitly via indirect or in function itself*',
]
)