From 561f1a993bcef74b626a43ec16c79a57b6aa99d9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 1 Jul 2023 22:08:21 +0200 Subject: [PATCH] fix #10447 - consider marks in reverse mro order to give base classes priority --- changelog/10447.bugfix.rst | 2 ++ src/_pytest/mark/structures.py | 4 +++- testing/test_mark.py | 37 +++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 changelog/10447.bugfix.rst diff --git a/changelog/10447.bugfix.rst b/changelog/10447.bugfix.rst new file mode 100644 index 000000000..fff94b28f --- /dev/null +++ b/changelog/10447.bugfix.rst @@ -0,0 +1,2 @@ +markers are now considered in the reverse mro order to ensure base class markers are considered first +this resolves a regression. diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index bc10d3b90..d6e426567 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -374,7 +374,9 @@ def get_unpacked_marks( if not consider_mro: mark_lists = [obj.__dict__.get("pytestmark", [])] else: - mark_lists = [x.__dict__.get("pytestmark", []) for x in obj.__mro__] + mark_lists = [ + x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__) + ] mark_list = [] for item in mark_lists: if isinstance(item, list): diff --git a/testing/test_mark.py b/testing/test_mark.py index e2d1a40c3..2767260df 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1130,6 +1130,41 @@ def test_mark_mro() -> None: all_marks = get_unpacked_marks(C) - assert all_marks == [xfail("c").mark, xfail("a").mark, xfail("b").mark] + assert all_marks == [xfail("b").mark, xfail("a").mark, xfail("c").mark] assert get_unpacked_marks(C, consider_mro=False) == [xfail("c").mark] + + +# @pytest.mark.issue("https://github.com/pytest-dev/pytest/issues/10447") +def test_mark_fixture_order_mro(pytester: Pytester): + """This ensures we walk marks of the mro starting with the base classes + the action at a distance fixtures are taken as minimal example from a real project + + """ + foo = pytester.makepyfile( + """ + import pytest + + @pytest.fixture + def add_attr1(request): + request.instance.attr1 = object() + + + @pytest.fixture + def add_attr2(request): + request.instance.attr2 = request.instance.attr1 + + + @pytest.mark.usefixtures('add_attr1') + class Parent: + pass + + + @pytest.mark.usefixtures('add_attr2') + class TestThings(Parent): + def test_attrs(self): + assert self.attr1 == self.attr2 + """ + ) + result = pytester.runpytest(foo) + result.assert_outcomes(passed=1)