diff --git a/changelog/3605.bugfix.rst b/changelog/3605.bugfix.rst new file mode 100644 index 000000000..58a294ecb --- /dev/null +++ b/changelog/3605.bugfix.rst @@ -0,0 +1 @@ +no longer ValueError when using the ``get_marker`` api. diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index eb0349c2f..d416e422b 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -225,9 +225,18 @@ def get_unpacked_marks(obj): obtain the unpacked marks that are stored on an object """ mark_list = getattr(obj, "pytestmark", []) - if not isinstance(mark_list, list): mark_list = [mark_list] + return normalize_mark_list(mark_list) + + +def normalize_mark_list(mark_list): + """ + normalizes marker decorating helpers to mark objects + + :type mark_list: List[Union[Mark, Markdecorator]] + :rtype: List[Mark] + """ return [getattr(mark, "mark", mark) for mark in mark_list] # unpack MarkDecorator diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 8a9c15dc2..724a6098a 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -39,7 +39,11 @@ from _pytest.compat import ( get_default_arg_names, ) from _pytest.outcomes import fail -from _pytest.mark.structures import transfer_markers, get_unpacked_marks +from _pytest.mark.structures import ( + transfer_markers, + get_unpacked_marks, + normalize_mark_list, +) # relative paths that we use to filter traceback entries from appearing to the user; @@ -773,7 +777,7 @@ class CallSpec2(object): self.indices[arg] = param_index self._arg2scopenum[arg] = scopenum self._idlist.append(id) - self.marks.extend(marks) + self.marks.extend(normalize_mark_list(marks)) def setall(self, funcargs, id, param): for x in funcargs: @@ -1254,7 +1258,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr): # feel free to cry, this was broken for years before # and keywords cant fix it per design self.keywords[mark.name] = mark - self.own_markers.extend(callspec.marks) + self.own_markers.extend(normalize_mark_list(callspec.marks)) if keywords: self.keywords.update(keywords) diff --git a/testing/test_mark.py b/testing/test_mark.py index acd140370..ddeff2789 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1139,3 +1139,42 @@ def test_addmarker_order(): node.add_marker("c", append=False) extracted = [x.name for x in node.iter_markers()] assert extracted == ["c", "a", "b"] + + +@pytest.mark.issue("https://github.com/pytest-dev/pytest/issues/3605") +@pytest.mark.filterwarnings("ignore") +def test_markers_from_parametrize(testdir): + testdir.makepyfile( + """ + from __future__ import print_function + import pytest + + first_custom_mark = pytest.mark.custom_marker + custom_mark = pytest.mark.custom_mark + @pytest.fixture(autouse=True) + def trigger(request): + custom_mark =request.node.get_marker('custom_mark') + print("Custom mark %s" % custom_mark) + + @custom_mark("custom mark non parametrized") + def test_custom_mark_non_parametrized(): + print("Hey from test") + + @pytest.mark.parametrize( + "obj_type", + [ + first_custom_mark("first custom mark")("template"), + pytest.param( # Think this should be recommended way? + "disk", + marks=custom_mark('custom mark1') + ), + custom_mark("custom mark2")("vm"), # Tried also this + ] + ) + def test_custom_mark_parametrized(obj_type): + print("obj_type is:", obj_type) + """ + ) + + result = testdir.runpytest() + result.assert_outcomes(passed=4)