Skip to content

Commit de923a0

Browse files
committed
Use saferepr right after constructing SubtestContext
1 parent 5228d92 commit de923a0

File tree

2 files changed

+21
-19
lines changed

2 files changed

+21
-19
lines changed

src/_pytest/subtests.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from contextlib import ExitStack
1212
from contextlib import nullcontext
1313
import dataclasses
14-
import pickle
1514
import time
1615
from types import TracebackType
1716
from typing import Any
@@ -62,17 +61,19 @@ class SubtestContext:
6261
msg: str | None
6362
kwargs: Mapping[str, Any]
6463

64+
def __post_init__(self) -> None:
65+
# Brute-force the returned kwargs dict to be JSON serializable (pytest-dev/pytest-xdist#1273).
66+
object.__setattr__(
67+
self, "kwargs", {k: saferepr(v) for (k, v) in self.kwargs.items()}
68+
)
69+
6570
def _to_json(self) -> dict[str, Any]:
6671
result = dataclasses.asdict(self)
67-
# Use protocol 0 because it is human-readable and guaranteed to be not-binary.
68-
protocol = 0
69-
data = pickle.dumps(result["kwargs"], protocol=protocol)
70-
result["kwargs"] = data.decode("UTF-8")
7172
return result
7273

7374
@classmethod
7475
def _from_json(cls, d: dict[str, Any]) -> Self:
75-
return cls(msg=d["msg"], kwargs=pickle.loads(d["kwargs"].encode("UTF-8")))
76+
return cls(msg=d["msg"], kwargs=d["kwargs"])
7677

7778

7879
@dataclasses.dataclass(init=False)

testing/test_subtests.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
from typing import Literal
77

8+
from _pytest._io.saferepr import saferepr
89
from _pytest.subtests import SubtestContext
910
from _pytest.subtests import SubtestReport
1011
import pytest
@@ -304,10 +305,10 @@ def test_foo(subtests, x):
304305
result = pytester.runpytest("-v")
305306
result.stdout.fnmatch_lines(
306307
[
307-
"*.py::test_foo[[]0[]] SUBFAILED[[]custom[]] (i=1) *[[] 50%[]]",
308-
"*.py::test_foo[[]0[]] FAILED *[[] 50%[]]",
309-
"*.py::test_foo[[]1[]] SUBFAILED[[]custom[]] (i=1) *[[]100%[]]",
310-
"*.py::test_foo[[]1[]] FAILED *[[]100%[]]",
308+
"*.py::test_foo[[]0[]] SUBFAILED[[]custom[]] (i='1') *[[] 50%[]]",
309+
"*.py::test_foo[[]0[]] FAILED *[[] 50%[]]",
310+
"*.py::test_foo[[]1[]] SUBFAILED[[]custom[]] (i='1') *[[]100%[]]",
311+
"*.py::test_foo[[]1[]] FAILED *[[]100%[]]",
311312
"contains 1 failed subtest",
312313
"* 4 failed, 4 subtests passed in *",
313314
]
@@ -322,10 +323,10 @@ def test_foo(subtests, x):
322323
result = pytester.runpytest("-v")
323324
result.stdout.fnmatch_lines(
324325
[
325-
"*.py::test_foo[[]0[]] SUBFAILED[[]custom[]] (i=1) *[[] 50%[]]",
326-
"*.py::test_foo[[]0[]] FAILED *[[] 50%[]]",
327-
"*.py::test_foo[[]1[]] SUBFAILED[[]custom[]] (i=1) *[[]100%[]]",
328-
"*.py::test_foo[[]1[]] FAILED *[[]100%[]]",
326+
"*.py::test_foo[[]0[]] SUBFAILED[[]custom[]] (i='1') *[[] 50%[]]",
327+
"*.py::test_foo[[]0[]] FAILED *[[] 50%[]]",
328+
"*.py::test_foo[[]1[]] SUBFAILED[[]custom[]] (i='1') *[[]100%[]]",
329+
"*.py::test_foo[[]1[]] FAILED *[[]100%[]]",
329330
"contains 1 failed subtest",
330331
"* 4 failed in *",
331332
]
@@ -652,12 +653,12 @@ def test_capturing(self, pytester: pytest.Pytester, mode: str) -> None:
652653
result = pytester.runpytest(f"--capture={mode}")
653654
result.stdout.fnmatch_lines(
654655
[
655-
"*__ test (i='A') __*",
656+
"*__ test (i=\"'A'\") __*",
656657
"*Captured stdout call*",
657658
"hello stdout A",
658659
"*Captured stderr call*",
659660
"hello stderr A",
660-
"*__ test (i='B') __*",
661+
"*__ test (i=\"'B'\") __*",
661662
"*Captured stdout call*",
662663
"hello stdout B",
663664
"*Captured stderr call*",
@@ -678,8 +679,8 @@ def test_no_capture(self, pytester: pytest.Pytester) -> None:
678679
"hello stdout A",
679680
"uhello stdout B",
680681
"uend test",
681-
"*__ test (i='A') __*",
682-
"*__ test (i='B') __*",
682+
"*__ test (i=\"'A'\") __*",
683+
"*__ test (i=\"'B'\") __*",
683684
"*__ test __*",
684685
]
685686
)
@@ -986,7 +987,7 @@ def test_serialization() -> None:
986987
new_report = pytest_report_from_serializable(data)
987988
assert new_report is not None
988989
assert new_report.context == SubtestContext(
989-
msg="custom message", kwargs=dict(i=10, a=MyEnum.A)
990+
msg="custom message", kwargs=dict(i=saferepr(10), a=saferepr(MyEnum.A))
990991
)
991992

992993

0 commit comments

Comments
 (0)