diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 77d8b52ca46..8570c447359 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -446,6 +446,9 @@ def pytest_collection_modifyitems(items: list[nodes.Item], config: Config) -> No deselected = [] for colitem in items: if colitem.nodeid.startswith(deselect_prefixes): + colitem._deselected_reason = ( + f"Deselected by --deselect option: {colitem.nodeid}" + ) deselected.append(colitem) else: remaining.append(colitem) diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 068c7410a46..0e14096f0e6 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -217,6 +217,9 @@ def deselect_by_keyword(items: list[Item], config: Config) -> None: deselected = [] for colitem in items: if not expr.evaluate(KeywordMatcher.from_item(colitem)): + colitem._deselected_reason = ( + f"Keyword expression '{keywordexpr}' did not match." + ) deselected.append(colitem) else: remaining.append(colitem) @@ -266,9 +269,9 @@ def deselect_by_mark(items: list[Item], config: Config) -> None: if expr.evaluate(MarkMatcher.from_markers(item.iter_markers())): remaining.append(item) else: + item._deselected_reason = f"Mark expression '{matchexpr}' did not match." deselected.append(item) if deselected: - config.hook.pytest_deselected(items=deselected) items[:] = remaining diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 6690f6ab1f8..ac49f47a9b3 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -144,6 +144,7 @@ class Node(abc.ABC, metaclass=NodeMeta): # Note that __dict__ is still available. __slots__ = ( "__dict__", + "_deselected_reason", "_nodeid", "_store", "config", @@ -214,6 +215,9 @@ def __init__( # Deprecated alias. Was never public. Can be removed in a few releases. self._store = self.stash + #: A reason for deselection. + self._deselected_reason = "" + @classmethod def from_parent(cls, parent: Node, **kw) -> Self: """Public constructor for Nodes. diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index a95f79ba6b6..4959b281d1e 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -1452,7 +1452,12 @@ def _build_collect_only_summary_stats_line( if errors: main_color = _color_for_type["error"] parts += [("%d %s" % pluralize(errors, "error"), {main_color: True})] # noqa: UP031 - + if self.verbosity >= 2: + for item in self.stats["deselected"]: + self.write_line( + f"Deselected: {item.nodeid}: {item._deselected_reason}", + cyan=True, + ) return parts, main_color