Skip to content

Commit

Permalink
Fixing missing marks when inheritance from multiple classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Oren Efraimov committed Sep 19, 2021
1 parent 60e995d commit 8f0afe5
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Edison Gustavo Muenz
Edoardo Batini
Edson Tadeu M. Manoel
Eduardo Schettino
Efraimov Oren
Eli Boyarski
Elizaveta Shashkova
Éloi Rivard
Expand Down
5 changes: 5 additions & 0 deletions doc/en/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ with advance notice in the **Deprecations** section of releases.
pytest 6.2.5 (2021-08-29)
=========================

Bug Fixes
---------

- `#7792 <https://github.com/pytest-dev/pytest/issues/7792>`_: Fixing missing marks when inheritance from multiple classes.


Trivial/Internal Changes
------------------------
Expand Down
14 changes: 14 additions & 0 deletions src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@


EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
MARKERS_BY_CLASS_NAME = {}


def istestfunc(func) -> bool:
Expand Down Expand Up @@ -368,6 +369,19 @@ def get_unpacked_marks(obj) -> List[Mark]:
return normalize_mark_list(mark_list)


def get_all_mro_marks(cls, module_name):
all_markers = getattr(cls, "pytestmark", [])
if cls is None:
return all_markers
for parent_obj in cls.__mro__[::-1]:
full_class_name = f"{module_name}_{parent_obj.__name__}"
if not MARKERS_BY_CLASS_NAME.get(full_class_name):
MARKERS_BY_CLASS_NAME[full_class_name] = get_unpacked_marks(parent_obj)
all_markers.extend(MARKERS_BY_CLASS_NAME[full_class_name])
setattr(cls, "pytestmark", list({str(mark): mark for mark in all_markers}.values()))
return cls.pytestmark


def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List[Mark]:
"""
Normalize an iterable of Mark or MarkDecorator objects into a list of marks
Expand Down
2 changes: 2 additions & 0 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from _pytest.main import Session
from _pytest.mark import MARK_GEN
from _pytest.mark import ParameterSet
from _pytest.mark.structures import get_all_mro_marks
from _pytest.mark.structures import get_unpacked_marks
from _pytest.mark.structures import Mark
from _pytest.mark.structures import MarkDecorator
Expand Down Expand Up @@ -1586,6 +1587,7 @@ def __init__(
# to a readonly property that returns FunctionDefinition.name.

self.keywords.update(self.obj.__dict__)
self.own_markers.extend(get_all_mro_marks(self.cls, self.module.__name__))
self.own_markers.extend(get_unpacked_marks(self.obj))
if callspec:
self.callspec = callspec
Expand Down
31 changes: 31 additions & 0 deletions testing/test_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,3 +1127,34 @@ def test_foo():
result = pytester.runpytest(foo, "-m", expr)
result.stderr.fnmatch_lines([expected])
assert result.ret == ExitCode.USAGE_ERROR

@pytest.mark.parametrize(
"markers",
(
"mark1",
"mark1 and mark2",
"mark1 and mark2 and mark3",
"mark1 and mark2 and mark3 and mark4",
),
)
def test_markers_from_multiple_inheritances(pytester: Pytester, markers) -> None:
py_file = pytester.makepyfile(
"""
import pytest
@pytest.mark.mark1
class Base1:
pass
@pytest.mark.mark2
class Base2:
pass
@pytest.mark.mark3
class TestMultipleInheritances(Base1, Base2):
@pytest.mark.mark4
def test_multiple_inheritances(self):
pass
"""
)
result = pytester.inline_run(py_file, "-m", markers)
result.assertoutcome(passed=1)

0 comments on commit 8f0afe5

Please sign in to comment.