diff --git a/changelog/3501.bugfix.rst b/changelog/3501.bugfix.rst new file mode 100644 index 00000000000..babf9973b61 --- /dev/null +++ b/changelog/3501.bugfix.rst @@ -0,0 +1 @@ +Allow to insert ``pytest.mark.NAME`` (``MarkDecorator``) objects at the start of an item's marks. diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 7e86aee448b..f5248a4e0bd 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -4,6 +4,7 @@ from operator import attrgetter import attr +import six from ..deprecated import MARK_PARAMETERSET_UNPACKING, MARK_INFO_ATTRIBUTE from ..compat import NOTSET, getfslineno, MappingMixin @@ -282,12 +283,17 @@ class MarkInfo(object): """ Marking object created by :class:`MarkDecorator` instances. """ _marks = attr.ib() - combined = attr.ib( - repr=False, - default=attr.Factory( - lambda self: reduce(Mark.combined_with, self._marks), takes_self=True - ), - ) + combined = attr.ib(repr=False) + + @combined.default + def _combined_default(self): + if six.PY2: + marks = [ + (x.mark if isinstance(x, MarkDecorator) else x) for x in self._marks + ] + else: + marks = self._marks + return reduce(Mark.combined_with, marks) name = alias("combined.name", warning=MARK_INFO_ATTRIBUTE) args = alias("combined.args", warning=MARK_INFO_ATTRIBUTE) diff --git a/testing/test_mark.py b/testing/test_mark.py index e96af888a07..3478cf18788 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -136,6 +136,16 @@ def g(): assert "reason" not in g.some.kwargs assert g.some.kwargs["reason2"] == "456" + @ignore_markinfo + def test_pytest_mark_decorator_first_item(self): + """Check that inserting a MarkDecorator as the first element of MarkInfo works (#3501)""" + from _pytest.mark.structures import MarkInfo, Mark + + mark_info = MarkInfo( + [pytest.mark.slow(3), Mark(name="slow", args=(1,), kwargs={})] + ) + assert mark_info.args == (3, 1) + def test_marked_class_run_twice(testdir, request): """Test fails file is run twice that contains marked class.