From 97ecd17ab5089c1cfe80916ec426d5a409917808 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 13 Mar 2020 10:18:06 -0300 Subject: [PATCH] Revert "[parametrize] enforce explicit argnames declaration (#6330)" (cherry picked from commit 010e711971bdc5aee24ce1dc5de70ca4db62b3d9) Conflicts: testing/python/metafunc.py --- changelog/6909.bugfix.rst | 3 +++ doc/en/example/parametrize.rst | 3 --- src/_pytest/fixtures.py | 16 ++++-------- src/_pytest/python.py | 35 ------------------------- testing/python/collect.py | 2 +- testing/python/metafunc.py | 48 ---------------------------------- 6 files changed, 9 insertions(+), 98 deletions(-) create mode 100644 changelog/6909.bugfix.rst diff --git a/changelog/6909.bugfix.rst b/changelog/6909.bugfix.rst new file mode 100644 index 00000000000..32edc4974c2 --- /dev/null +++ b/changelog/6909.bugfix.rst @@ -0,0 +1,3 @@ +Revert the change introduced by `#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. diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index f1425342bb6..15593b28a02 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -398,9 +398,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 -------------------------------------------------------------- diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 30d55d37c49..e0605992080 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1,5 +1,6 @@ import functools import inspect +import itertools import sys import warnings from collections import defaultdict @@ -1305,8 +1306,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) @@ -1503,12 +1506,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 - ) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index ccccf5ee4a6..01a16e15312 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -947,8 +947,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 @@ -1121,39 +1119,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. diff --git a/testing/python/collect.py b/testing/python/collect.py index 3c28682d077..5269d9c2c03 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -495,7 +495,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 """ diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 3bda378c155..6ba661aecc8 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1925,51 +1925,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*', - ] - )