From 67d93d52759f9d4f78cb0eed3d810b087deaed00 Mon Sep 17 00:00:00 2001 From: Sandeep1519-droid Date: Thu, 15 Oct 2026 15:44:57 +0530 Subject: [PATCH 1/3] tracing: avoid treating specced mocks as their spec types Use the actual object type when selecting trace formatting so mocked strings and dictionaries do not interrupt hook execution. Co-authored-by: Codex --- src/pluggy/_tracing.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pluggy/_tracing.py b/src/pluggy/_tracing.py index 9211c0b7..e8776f76 100644 --- a/src/pluggy/_tracing.py +++ b/src/pluggy/_tracing.py @@ -9,6 +9,7 @@ import enum import os from typing import Any +from typing import cast _Writer = Callable[[str], object] @@ -60,10 +61,12 @@ def _format_block(indent: str, lines: list[str]) -> list[str]: def _render_value(obj: object) -> str: """Render a traced value, adding detail only where ``str`` is ambiguous.""" + obj_type = type(obj) # Before the str check: a StrEnum member is a str whose str() drops the name. - if isinstance(obj, (enum.Enum, os.PathLike)): + if issubclass(obj_type, (enum.Enum, os.PathLike)): return _safe_repr(obj) - if isinstance(obj, str): + if issubclass(obj_type, str): + obj = cast(str, obj) if len(obj.splitlines()) > 1: return _safe_str(obj) if _is_plain_token(obj): @@ -94,8 +97,8 @@ def get(self, name: str) -> TagTracerSub: return TagTracerSub(self, (name,)) def _format_message(self, tags: Sequence[str], args: Sequence[object]) -> str: - if isinstance(args[-1], dict): - extra = args[-1] + if issubclass(type(args[-1]), dict): + extra = cast(dict[object, object], args[-1]) args = args[:-1] else: extra = {} From f8d5bf8404a0a6f4deebf684f723904d403c37de Mon Sep 17 00:00:00 2001 From: Sandeep1519-droid Date: Thu, 15 Oct 2026 15:45:37 +0530 Subject: [PATCH 2/3] test tracing with specced mock arguments and results Both cases fail on upstream main and pass with the tracing fix. Co-authored-by: Codex --- testing/test_tracer.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/testing/test_tracer.py b/testing/test_tracer.py index 4fc16292..467a8248 100644 --- a/testing/test_tracer.py +++ b/testing/test_tracer.py @@ -1,6 +1,7 @@ import enum import os import pathlib +from unittest.mock import Mock import pytest @@ -164,6 +165,32 @@ def hello_again(self, arg): ] +@pytest.mark.parametrize("spec", [str, dict]) +def test_plugin_tracing_with_mock(pm: PluginManager, spec: type) -> None: + class Api: + @hookspec(firstresult=True) + def hello(self, arg: object) -> object: + "Return the argument." + + class Plugin: + @hookimpl + def hello(self, arg: object) -> object: + return arg + + pm.add_hookspecs(Api) + pm.register(Plugin()) + trace_out: list[str] = [] + pm.trace.root.setwriter(trace_out.append) + pm.enable_tracing() + + value = Mock(spec=spec) + assert pm.hook.hello(arg=value) is value + assert trace_out == [ + f" hello [hook]\n arg: {value}\n", + f" finish hello --> {value} [hook]\n", + ] + + class BrokenRepr: def __repr__(self) -> str: raise RuntimeError("repr is broken") From eb0f67d7cf110aadd3ebb50eab990f98f2ec8ea6 Mon Sep 17 00:00:00 2001 From: Sandeep1519-droid Date: Thu, 15 Oct 2026 15:46:38 +0530 Subject: [PATCH 3/3] document the tracing fix for specced mocks Fixes pytest-dev/pluggy#792. Co-authored-by: Codex --- changelog/792.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/792.bugfix.rst diff --git a/changelog/792.bugfix.rst b/changelog/792.bugfix.rst new file mode 100644 index 00000000..0f738d1c --- /dev/null +++ b/changelog/792.bugfix.rst @@ -0,0 +1 @@ +Fix hook tracing raising ``TypeError`` for specced mocks passed as hook arguments or returned from first-result hooks.