Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ Eric Yuan
Erik Aronesty
Erik Hasse
Erik M. Bray
Ethan Wass
Evan Kepner
Evgeny Seliverstov
Fabian Sturm
Expand Down
1 change: 1 addition & 0 deletions changelog/12863.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix applying markers, including :ref:`pytest.mark.parametrize <pytest.mark.parametrize ref>` when placed above `@staticmethod` or `@classmethod`.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ disable = [

[tool.codespell]
ignore-words-list = "afile,asend,asser,assertio,feld,hove,ned,noes,notin,paramete,parth,socio-economic,tesults,varius,wil"
skip = "*/plugin_list.rst"
skip = "AUTHORS,*/plugin_list.rst"
write-changes = true

[tool.check-wheel-contents]
Expand Down
9 changes: 7 additions & 2 deletions src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,13 @@ def __call__(self, *args: object, **kwargs: object):
if args and not kwargs:
func = args[0]
is_class = inspect.isclass(func)
if len(args) == 1 and (istestfunc(func) or is_class):
store_mark(func, self.mark, stacklevel=3)
# For staticmethods/classmethods, the marks are eventually fetched from the
# function object, not the descriptor, so unwrap.
unwrapped_func = func
if isinstance(func, (staticmethod, classmethod)):
unwrapped_func = func.__func__
if len(args) == 1 and (istestfunc(unwrapped_func) or is_class):
store_mark(unwrapped_func, self.mark, stacklevel=3)
return func
return self.with_args(*args, **kwargs)

Expand Down
35 changes: 35 additions & 0 deletions testing/test_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -1226,3 +1226,38 @@ def test_attrs(self):
)
result = pytester.runpytest(foo)
result.assert_outcomes(passed=1)


def test_mark_parametrize_over_staticmethod(pytester: Pytester) -> None:
"""Check that applying marks works as intended on classmethods and staticmethods.

Regression test for #12863.
"""
pytester.makepyfile(
"""
import pytest

class TestClass:
@pytest.mark.parametrize("value", [1, 2])
@classmethod
def test_classmethod_wrapper(cls, value: int):
assert value in [1, 2]

@classmethod
@pytest.mark.parametrize("value", [1, 2])
def test_classmethod_wrapper_on_top(cls, value: int):
assert value in [1, 2]

@pytest.mark.parametrize("value", [1, 2])
@staticmethod
def test_staticmethod_wrapper(value: int):
assert value in [1, 2]

@staticmethod
@pytest.mark.parametrize("value", [1, 2])
def test_staticmethod_wrapper_on_top(value: int):
assert value in [1, 2]
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=8)