Skip to content

Commit fdfa22f

Browse files
committed
Best-effort to convert to JSON
1 parent 7585f45 commit fdfa22f

File tree

3 files changed

+20
-5
lines changed

3 files changed

+20
-5
lines changed

changelog/13963.bugfix.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Fixed subtests running with `pytest-xdist <https://github.com/pytest-dev/pytest>`__ when their contexts contain non-standard objects.
1+
Fixed subtests running with `pytest-xdist <https://github.com/pytest-dev/pytest>`__ when their contexts contain objects that are not JSON-serializable.
22

33
Fixes `pytest-dev/pytest-xdist#1273 <https://github.com/pytest-dev/pytest-xdist/issues/1273>`__.

src/_pytest/subtests.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from contextlib import ExitStack
1212
from contextlib import nullcontext
1313
import dataclasses
14+
import json
1415
import time
1516
from types import TracebackType
1617
from typing import Any
@@ -63,8 +64,19 @@ class SubtestContext:
6364

6465
def _to_json(self) -> dict[str, Any]:
6566
result = dataclasses.asdict(self)
66-
# Brute-force the returned kwargs dict to be JSON serializable (pytest-dev/pytest-xdist#1273).
67-
result["kwargs"] = {k: saferepr(v) for (k, v) in result["kwargs"].items()}
67+
68+
# Best-effort to convert the kwargs values to JSON (pytest-dev/pytest-xdist#1273).
69+
# If they can be converted, we return as it is, otherwise we return its saferepr because it seems
70+
# this is the best we can do at this point.
71+
def convert(x: Any) -> Any:
72+
try:
73+
json.dumps(x)
74+
except TypeError:
75+
return saferepr(x)
76+
else:
77+
return x
78+
79+
result["kwargs"] = {k: convert(v) for (k, v) in result["kwargs"].items()}
6880
return result
6981

7082
@classmethod

testing/test_subtests.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
from enum import Enum
4+
import json
45
import sys
56
from typing import Literal
67

@@ -978,14 +979,16 @@ class MyEnum(Enum):
978979
)
979980
data = pytest_report_to_serializable(report)
980981
assert data is not None
982+
# Ensure the report is actually serializable to JSON.
983+
_ = json.dumps(data)
981984
new_report = pytest_report_from_serializable(data)
982985
assert new_report is not None
983986
assert new_report.context == SubtestContext(
984-
msg="custom message", kwargs=dict(i=saferepr(10), a=saferepr(MyEnum.A))
987+
msg="custom message", kwargs=dict(i=10, a=saferepr(MyEnum.A))
985988
)
986989

987990

988-
def test_serialization_xdist(pytester: pytest.Pytester) -> None:
991+
def test_serialization_xdist(pytester: pytest.Pytester) -> None: # pragma: no cover
989992
"""Regression test for pytest-dev/pytest-xdist#1273."""
990993
pytest.importorskip("xdist")
991994
pytester.makepyfile(

0 commit comments

Comments
 (0)