From ec2bcc51eedcfac30f42ed335c8689fb231e2f86 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 22 Jun 2024 12:21:00 +0200 Subject: [PATCH] expand marker test debugging and restore maker discovery when obj is passed trough --- src/_pytest/nodes.py | 3 ++- src/_pytest/python.py | 24 ++++++++++++++++-------- testing/test_mark.py | 8 ++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 816becbc9..208563c12 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -107,7 +107,7 @@ class NodeMeta(abc.ABCMeta): def _create(cls: type[_T], *k: Any, **kw: Any) -> _T: try: return super().__call__(*k, **kw) # type: ignore[no-any-return,misc] - except TypeError: + except TypeError as e: sig = signature(getattr(cls, "__init__")) known_kw = {k: v for k, v in kw.items() if k in sig.parameters} from .warning_types import PytestDeprecationWarning @@ -115,6 +115,7 @@ class NodeMeta(abc.ABCMeta): warnings.warn( PytestDeprecationWarning( f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n" + f"Exception: {e}\n" "See https://docs.pytest.org/en/stable/deprecations.html" "#constructors-of-custom-pytest-node-subclasses-should-take-kwargs " "for more details." diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 256f32aa4..62f605360 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -255,6 +255,10 @@ class PyobjMixin(nodes.Node): as its intended to always mix in before a node its position in the mro is unaffected""" + def __init__(self, *k: Any, obj: Any | None = None, **kw: Any) -> None: + super().__init__(*k, **kw) + self._assign_obj_with_markers(obj) + _ALLOW_MARKERS = True @property @@ -279,19 +283,23 @@ class PyobjMixin(nodes.Node): # Overridden by Function. return None + def _assign_obj_with_markers(self, obj: Any | None) -> None: + self._obj = obj + # XXX evil hack + # used to avoid Function marker duplication + if self._ALLOW_MARKERS and obj is not None: + self.own_markers.extend(get_unpacked_marks(self.obj)) + # This assumes that `obj` is called before there is a chance + # to add custom keys to `self.keywords`, so no fear of overriding. + self.keywords.update((mark.name, mark) for mark in self.own_markers) + @property def obj(self) -> Any: """Underlying Python object.""" obj = getattr(self, "_obj", None) if obj is None: - self._obj = obj = self._getobj() - # XXX evil hack - # used to avoid Function marker duplication - if self._ALLOW_MARKERS: - self.own_markers.extend(get_unpacked_marks(self.obj)) - # This assumes that `obj` is called before there is a chance - # to add custom keys to `self.keywords`, so no fear of overriding. - self.keywords.update((mark.name, mark) for mark in self.own_markers) + obj = self._getobj() + self._assign_obj_with_markers(obj) return obj @obj.setter diff --git a/testing/test_mark.py b/testing/test_mark.py index 090e10ee9..8f08ea57c 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -582,6 +582,14 @@ class TestFunctional: has_own, has_inherited = items has_own_marker = has_own.get_closest_marker("c") has_inherited_marker = has_inherited.get_closest_marker("c") + + for item in items: + print(item) + for node in item.iter_parents(): + print(" ", node) + for marker in node.own_markers: + print(" ", marker) + assert has_own_marker is not None assert has_inherited_marker is not None assert has_own_marker.kwargs == {"location": "function"}