Merge pull request #4571 from nicoddemus/remove-parametrize-marks
Remove support for applying marks to values in parametrize
This commit is contained in:
commit
58fc918d0a
|
@ -0,0 +1,3 @@
|
|||
Removed support for applying marks directly to values in ``@pytest.mark.parametrize``. Use ``pytest.param`` instead.
|
||||
|
||||
See our `docs <https://docs.pytest.org/en/latest/deprecations.html#marks-in-pytest-mark-parametrize>`__ on information on how to update your code.
|
|
@ -7,6 +7,11 @@ This page lists all pytest features that are currently deprecated or have been r
|
|||
The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives
|
||||
should be used instead.
|
||||
|
||||
.. contents::
|
||||
:depth: 3
|
||||
:local:
|
||||
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
|
@ -81,40 +86,6 @@ As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` i
|
|||
:ref:`the documentation <update marker code>` on tips on how to update your code.
|
||||
|
||||
|
||||
marks in ``pytest.mark.parametrize``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.2
|
||||
|
||||
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b", [(3, 9), pytest.mark.xfail(reason="flaky")(6, 36), (10, 100)]
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
||||
call.
|
||||
|
||||
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
|
||||
further internal improvements in the marks architecture.
|
||||
|
||||
To update the code, use ``pytest.param``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[(3, 9), pytest.param((6, 36), marks=pytest.mark.xfail(reason="flaky")), (10, 100)],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
|
||||
|
||||
Result log (``--result-log``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -145,6 +116,55 @@ collection.
|
|||
This issue should affect only advanced plugins who create new collection types, so if you see this warning
|
||||
message please contact the authors so they can change the code.
|
||||
|
||||
|
||||
marks in ``pytest.mark.parametrize``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 4.0.*
|
||||
|
||||
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[
|
||||
(3, 9),
|
||||
pytest.mark.xfail(reason="flaky")(6, 36),
|
||||
(10, 100),
|
||||
(20, 200),
|
||||
(40, 400),
|
||||
(50, 500),
|
||||
],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
||||
call.
|
||||
|
||||
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
|
||||
further internal improvements in the marks architecture.
|
||||
|
||||
To update the code, use ``pytest.param``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[
|
||||
(3, 9),
|
||||
pytest.param(6, 36, marks=pytest.mark.xfail(reason="flaky")),
|
||||
(10, 100),
|
||||
(20, 200),
|
||||
(40, 400),
|
||||
(50, 500),
|
||||
],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
|
||||
``pytest_funcarg__`` prefix
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -54,12 +54,6 @@ MARK_INFO_ATTRIBUTE = RemovedInPytest4Warning(
|
|||
"Docs: https://docs.pytest.org/en/latest/mark.html#updating-code"
|
||||
)
|
||||
|
||||
MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
|
||||
"Applying marks directly to parameters is deprecated,"
|
||||
" please use pytest.param(..., marks=...) instead.\n"
|
||||
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
|
||||
)
|
||||
|
||||
RAISES_EXEC = PytestDeprecationWarning(
|
||||
"raises(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly\n\n"
|
||||
"See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec"
|
||||
|
|
|
@ -13,7 +13,6 @@ from ..compat import getfslineno
|
|||
from ..compat import MappingMixin
|
||||
from ..compat import NOTSET
|
||||
from ..deprecated import MARK_INFO_ATTRIBUTE
|
||||
from ..deprecated import MARK_PARAMETERSET_UNPACKING
|
||||
from _pytest.outcomes import fail
|
||||
|
||||
|
||||
|
@ -82,39 +81,23 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
|
|||
return cls(values, marks, id_)
|
||||
|
||||
@classmethod
|
||||
def extract_from(cls, parameterset, belonging_definition, legacy_force_tuple=False):
|
||||
def extract_from(cls, parameterset, force_tuple=False):
|
||||
"""
|
||||
:param parameterset:
|
||||
a legacy style parameterset that may or may not be a tuple,
|
||||
and may or may not be wrapped into a mess of mark objects
|
||||
|
||||
:param legacy_force_tuple:
|
||||
:param force_tuple:
|
||||
enforce tuple wrapping so single argument tuple values
|
||||
don't get decomposed and break tests
|
||||
|
||||
:param belonging_definition: the item that we will be extracting the parameters from.
|
||||
"""
|
||||
|
||||
if isinstance(parameterset, cls):
|
||||
return parameterset
|
||||
if not isinstance(parameterset, MarkDecorator) and legacy_force_tuple:
|
||||
if force_tuple:
|
||||
return cls.param(parameterset)
|
||||
|
||||
newmarks = []
|
||||
argval = parameterset
|
||||
while isinstance(argval, MarkDecorator):
|
||||
newmarks.append(
|
||||
MarkDecorator(Mark(argval.markname, argval.args[:-1], argval.kwargs))
|
||||
)
|
||||
argval = argval.args[-1]
|
||||
assert not isinstance(argval, ParameterSet)
|
||||
if legacy_force_tuple:
|
||||
argval = (argval,)
|
||||
|
||||
if newmarks and belonging_definition is not None:
|
||||
belonging_definition.warn(MARK_PARAMETERSET_UNPACKING)
|
||||
|
||||
return cls(argval, marks=newmarks, id=None)
|
||||
else:
|
||||
return cls(parameterset, marks=[], id=None)
|
||||
|
||||
@classmethod
|
||||
def _for_parametrize(cls, argnames, argvalues, func, config, function_definition):
|
||||
|
@ -124,12 +107,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
|
|||
else:
|
||||
force_tuple = False
|
||||
parameters = [
|
||||
ParameterSet.extract_from(
|
||||
x,
|
||||
legacy_force_tuple=force_tuple,
|
||||
belonging_definition=function_definition,
|
||||
)
|
||||
for x in argvalues
|
||||
ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues
|
||||
]
|
||||
del argvalues
|
||||
|
||||
|
@ -137,11 +115,21 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
|
|||
# check all parameter sets have the correct number of values
|
||||
for param in parameters:
|
||||
if len(param.values) != len(argnames):
|
||||
raise ValueError(
|
||||
'In "parametrize" the number of values ({}) must be '
|
||||
"equal to the number of names ({})".format(
|
||||
param.values, argnames
|
||||
)
|
||||
msg = (
|
||||
'{nodeid}: in "parametrize" the number of names ({names_len}):\n'
|
||||
" {names}\n"
|
||||
"must be equal to the number of values ({values_len}):\n"
|
||||
" {values}"
|
||||
)
|
||||
fail(
|
||||
msg.format(
|
||||
nodeid=function_definition.nodeid,
|
||||
values=param.values,
|
||||
names=argnames,
|
||||
names_len=len(argnames),
|
||||
values_len=len(param.values),
|
||||
),
|
||||
pytrace=False,
|
||||
)
|
||||
else:
|
||||
# empty parameter set (likely computed at runtime): create a single
|
||||
|
|
|
@ -244,13 +244,6 @@ class TestClass(object):
|
|||
"ignore:usage of Generator.Function is deprecated, please use pytest.Function instead"
|
||||
)
|
||||
class TestFunction(object):
|
||||
@pytest.fixture
|
||||
def ignore_parametrized_marks_args(self):
|
||||
"""Provides arguments to pytester.runpytest() to ignore the warning about marks being applied directly
|
||||
to parameters.
|
||||
"""
|
||||
return ("-W", "ignore:Applying marks directly to parameters")
|
||||
|
||||
def test_getmodulecollector(self, testdir):
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
modcol = item.getparent(pytest.Module)
|
||||
|
@ -472,7 +465,6 @@ class TestFunction(object):
|
|||
rec = testdir.inline_run()
|
||||
rec.assertoutcome(passed=1)
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:Applying marks directly to parameters")
|
||||
def test_parametrize_with_mark(self, testdir):
|
||||
items = testdir.getitems(
|
||||
"""
|
||||
|
@ -480,7 +472,7 @@ class TestFunction(object):
|
|||
@pytest.mark.foo
|
||||
@pytest.mark.parametrize('arg', [
|
||||
1,
|
||||
pytest.mark.bar(pytest.mark.baz(2))
|
||||
pytest.param(2, marks=[pytest.mark.baz, pytest.mark.bar])
|
||||
])
|
||||
def test_function(arg):
|
||||
pass
|
||||
|
@ -558,37 +550,37 @@ class TestFunction(object):
|
|||
assert colitems[2].name == "test2[a-c]"
|
||||
assert colitems[3].name == "test2[b-c]"
|
||||
|
||||
def test_parametrize_skipif(self, testdir, ignore_parametrized_marks_args):
|
||||
def test_parametrize_skipif(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
m = pytest.mark.skipif('True')
|
||||
|
||||
@pytest.mark.parametrize('x', [0, 1, m(2)])
|
||||
@pytest.mark.parametrize('x', [0, 1, pytest.param(2, marks=m)])
|
||||
def test_skip_if(x):
|
||||
assert x < 2
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(*ignore_parametrized_marks_args)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 2 passed, 1 skipped in *")
|
||||
|
||||
def test_parametrize_skip(self, testdir, ignore_parametrized_marks_args):
|
||||
def test_parametrize_skip(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
m = pytest.mark.skip('')
|
||||
|
||||
@pytest.mark.parametrize('x', [0, 1, m(2)])
|
||||
@pytest.mark.parametrize('x', [0, 1, pytest.param(2, marks=m)])
|
||||
def test_skip(x):
|
||||
assert x < 2
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(*ignore_parametrized_marks_args)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 2 passed, 1 skipped in *")
|
||||
|
||||
def test_parametrize_skipif_no_skip(self, testdir, ignore_parametrized_marks_args):
|
||||
def test_parametrize_skipif_no_skip(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
@ -600,40 +592,40 @@ class TestFunction(object):
|
|||
assert x < 2
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(*ignore_parametrized_marks_args)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 1 failed, 2 passed in *")
|
||||
|
||||
def test_parametrize_xfail(self, testdir, ignore_parametrized_marks_args):
|
||||
def test_parametrize_xfail(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
m = pytest.mark.xfail('True')
|
||||
|
||||
@pytest.mark.parametrize('x', [0, 1, m(2)])
|
||||
@pytest.mark.parametrize('x', [0, 1, pytest.param(2, marks=m)])
|
||||
def test_xfail(x):
|
||||
assert x < 2
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(*ignore_parametrized_marks_args)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 2 passed, 1 xfailed in *")
|
||||
|
||||
def test_parametrize_passed(self, testdir, ignore_parametrized_marks_args):
|
||||
def test_parametrize_passed(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
m = pytest.mark.xfail('True')
|
||||
|
||||
@pytest.mark.parametrize('x', [0, 1, m(2)])
|
||||
@pytest.mark.parametrize('x', [0, 1, pytest.param(2, marks=m)])
|
||||
def test_xfail(x):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(*ignore_parametrized_marks_args)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 2 passed, 1 xpassed in *")
|
||||
|
||||
def test_parametrize_xfail_passed(self, testdir, ignore_parametrized_marks_args):
|
||||
def test_parametrize_xfail_passed(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
@ -645,7 +637,7 @@ class TestFunction(object):
|
|||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(*ignore_parametrized_marks_args)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 3 passed in *")
|
||||
|
||||
def test_function_original_name(self, testdir):
|
||||
|
|
|
@ -1372,7 +1372,6 @@ class TestMetafuncFunctionalAuto(object):
|
|||
assert output.count("preparing foo-3") == 1
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:Applying marks directly to parameters")
|
||||
@pytest.mark.issue308
|
||||
class TestMarkersWithParametrization(object):
|
||||
def test_simple_mark(self, testdir):
|
||||
|
@ -1382,7 +1381,7 @@ class TestMarkersWithParametrization(object):
|
|||
@pytest.mark.foo
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.bar((1, 3)),
|
||||
pytest.param(1, 3, marks=pytest.mark.bar),
|
||||
(2, 3),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1402,7 +1401,7 @@ class TestMarkersWithParametrization(object):
|
|||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.foo((2, 3)),
|
||||
pytest.param(2, 3, marks=pytest.mark.foo),
|
||||
(3, 4),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1442,7 +1441,7 @@ class TestMarkersWithParametrization(object):
|
|||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.xfail((1, 3)),
|
||||
pytest.param(1, 3, marks=pytest.mark.xfail),
|
||||
(2, 3),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1459,7 +1458,7 @@ class TestMarkersWithParametrization(object):
|
|||
|
||||
@pytest.mark.parametrize("n", [
|
||||
2,
|
||||
pytest.mark.xfail(3),
|
||||
pytest.param(3, marks=pytest.mark.xfail),
|
||||
4,
|
||||
])
|
||||
def test_isEven(n):
|
||||
|
@ -1475,7 +1474,7 @@ class TestMarkersWithParametrization(object):
|
|||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.xfail("True")((1, 3)),
|
||||
pytest.param(1, 3, marks=pytest.mark.xfail("True")),
|
||||
(2, 3),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1491,7 +1490,7 @@ class TestMarkersWithParametrization(object):
|
|||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.xfail(reason="some bug")((1, 3)),
|
||||
pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
|
||||
(2, 3),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1507,7 +1506,7 @@ class TestMarkersWithParametrization(object):
|
|||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.xfail("True", reason="some bug")((1, 3)),
|
||||
pytest.param(1, 3, marks=pytest.mark.xfail("True", reason="some bug")),
|
||||
(2, 3),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1522,9 +1521,11 @@ class TestMarkersWithParametrization(object):
|
|||
s = """
|
||||
import pytest
|
||||
|
||||
m = pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict})
|
||||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict})((2, 3)),
|
||||
pytest.param(2, 3, marks=m),
|
||||
(3, 4),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
|
@ -1548,7 +1549,7 @@ class TestMarkersWithParametrization(object):
|
|||
failingTestData = [(1, 3),
|
||||
(2, 2)]
|
||||
|
||||
testData = passingTestData + [pytest.mark.xfail(d)
|
||||
testData = passingTestData + [pytest.param(*d, marks=pytest.mark.xfail)
|
||||
for d in failingTestData]
|
||||
metafunc.parametrize(("n", "expected"), testData)
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import six
|
|||
import pytest
|
||||
from _pytest.mark import EMPTY_PARAMETERSET_OPTION
|
||||
from _pytest.mark import MarkGenerator as Mark
|
||||
from _pytest.mark import ParameterSet
|
||||
from _pytest.mark import transfer_markers
|
||||
from _pytest.nodes import Collector
|
||||
from _pytest.nodes import Node
|
||||
|
@ -477,8 +476,10 @@ def test_parametrized_collect_with_wrong_args(testdir):
|
|||
result = testdir.runpytest(py_file)
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
'E ValueError: In "parametrize" the number of values ((1, 2, 3)) '
|
||||
"must be equal to the number of names (['foo', 'bar'])"
|
||||
'test_parametrized_collect_with_wrong_args.py::test_func: in "parametrize" the number of names (2):',
|
||||
" ['foo', 'bar']",
|
||||
"must be equal to the number of values (3):",
|
||||
" (1, 2, 3)",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -1042,36 +1043,6 @@ class TestKeywordSelection(object):
|
|||
assert_test_is_not_selected("()")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"argval, expected",
|
||||
[
|
||||
(
|
||||
pytest.mark.skip()((1, 2)),
|
||||
ParameterSet(values=(1, 2), marks=[pytest.mark.skip], id=None),
|
||||
),
|
||||
(
|
||||
pytest.mark.xfail(pytest.mark.skip()((1, 2))),
|
||||
ParameterSet(
|
||||
values=(1, 2), marks=[pytest.mark.xfail, pytest.mark.skip], id=None
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.filterwarnings("default")
|
||||
def test_parameterset_extractfrom(argval, expected):
|
||||
from _pytest.deprecated import MARK_PARAMETERSET_UNPACKING
|
||||
|
||||
warn_called = []
|
||||
|
||||
class DummyItem:
|
||||
def warn(self, warning):
|
||||
warn_called.append(warning)
|
||||
|
||||
extracted = ParameterSet.extract_from(argval, belonging_definition=DummyItem())
|
||||
assert extracted == expected
|
||||
assert warn_called == [MARK_PARAMETERSET_UNPACKING]
|
||||
|
||||
|
||||
def test_legacy_transfer():
|
||||
class FakeModule(object):
|
||||
pytestmark = []
|
||||
|
|
Loading…
Reference in New Issue