Repository: pytest-dev/pytest
File: src/_pytest/subtests.py (Python)

Code before:
if TYPE_CHECKING:
    from typing_extensions import Self


def pytest_addoption(parser: Parser) -> None:
    Config._add_verbosity_ini(
        parser,
        Config.VERBOSITY_SUBTESTS,
        help=(
            "Specify verbosity level for subtests. "
            "Higher levels will generate output for passed subtests. Failed subtests are always reported."
        ),
    )


@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
class SubtestContext:
    """The values passed to Subtests.test() that are included in the test report."""

    msg: str | None
    kwargs: Mapping[str, Any]

    def _to_json(self) -> dict[str, Any]:
        result = dataclasses.asdict(self)
        # Brute-force the returned kwargs dict to be JSON serializable (pytest-dev/pytest-xdist#1273).
        result["kwargs"] = {k: saferepr(v) for (k, v) in result["kwargs"].items()}
        return result

    @classmethod
    def _from_json(cls, d: dict[str, Any]) -> Self:
        return cls(msg=d["msg"], kwargs=d["kwargs"])


@dataclasses.dataclass(init=False)
class SubtestReport(TestReport):
    context: SubtestContext

    @property
    def head_line(self) -> str:
        _, _, domain = self.location
        return f"{domain} {self._sub_test_description()}"

    def _sub_test_description(self) -> str:
        parts = []
        if self.context.msg is not None:
            parts.append(f"[{self.context.msg}]")
        if self.context.kwargs:
            params_desc = ", ".join(
                f"{k}={saferepr(v)}" for (k, v) in self.context.kwargs.items()
            )
            parts.append(f"({params_desc})")

Review comment (suggestion):
IMO we should strive to have the property that `from_json(to_json(report)) == report`. In this case it means that we should have `self.kwargs` itself should be serializable, not just in `to_json`.

Code after:
if TYPE_CHECKING:
    from typing_extensions import Self


def pytest_addoption(parser: Parser) -> None:
    Config._add_verbosity_ini(
        parser,
        Config.VERBOSITY_SUBTESTS,
        help=(
            "Specify verbosity level for subtests. "
            "Higher levels will generate output for passed subtests. Failed subtests are always reported."
        ),
    )


@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
class SubtestContext:
    """The values passed to Subtests.test() that are included in the test report."""

    msg: str | None
    kwargs: Mapping[str, Any]

    def __post_init__(self) -> None:
        # Brute-force the returned kwargs dict to be JSON serializable (pytest-dev/pytest-xdist#1273).
        object.__setattr__(
            self, "kwargs", {k: saferepr(v) for (k, v) in self.kwargs.items()}
        )

    def _to_json(self) -> dict[str, Any]:
        result = dataclasses.asdict(self)
        return result

    @classmethod
    def _from_json(cls, d: dict[str, Any]) -> Self:
        return cls(msg=d["msg"], kwargs=d["kwargs"])


@dataclasses.dataclass(init=False)
class SubtestReport(TestReport):
    context: SubtestContext

    @property
    def head_line(self) -> str:
        _, _, domain = self.location
        return f"{domain} {self._sub_test_description()}"

    def _sub_test_description(self) -> str:
        parts = []
        if self.context.msg is not None:
            parts.append(f"[{self.context.msg}]")
        if self.context.kwargs: