From c46247898a97e6fb05a4b542bd2978f996171b7c Mon Sep 17 00:00:00 2001 From: Yingzi Zhang Date: Mon, 28 Sep 2026 10:15:20 -0400 Subject: [PATCH 1/2] Restore frozensets when decoding typed JSON payloads --- CHANGELOG.md | 3 +++ temporalio/converter/_payload_converter.py | 6 ++++-- tests/test_converter.py | 23 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03a2243edd..cd772f8ce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,9 @@ to include examples, links to docs, or any other relevant information. ### Fixed +- Restore `frozenset` values when decoding JSON payloads with a `frozenset` type hint, + including nested frozen sets. + - `GoogleAdkPlugin` now passes the optional `anthropic`, `litellm`, and `openai` SDKs through the workflow sandbox. - `contrib.deepagents`: prevent duplicate input messages after continue-as-new. diff --git a/temporalio/converter/_payload_converter.py b/temporalio/converter/_payload_converter.py index 00c63e340d..77fabdf8de 100644 --- a/temporalio/converter/_payload_converter.py +++ b/temporalio/converter/_payload_converter.py @@ -744,7 +744,7 @@ class JSONPlainPayloadConverter(EncodingPayloadConverter): ``dict()`` methods, and all iterables. For decoding, this uses type hints to attempt to rebuild the type from the - type hint. + type hint, including ``frozenset`` collections. """ _encoder: type[json.JSONEncoder] | None @@ -1086,11 +1086,13 @@ def value_to_type( ret_list.append(value_to_type(arg_type, item, custom_converters)) except Exception as err: raise TypeError(f"Failed converting {hint} index {i}") from err - # If tuple, set, or deque convert back to that type + # If tuple, set, frozenset, or deque convert back to that type if origin is tuple: return tuple(ret_list) elif origin is set: return set(ret_list) + elif origin is frozenset: + return frozenset(ret_list) elif origin is collections.deque: return collections.deque(ret_list) return ret_list diff --git a/tests/test_converter.py b/tests/test_converter.py index 4c64e25ba4..8444ae0ba1 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -351,6 +351,28 @@ class MyPydanticClass(pydantic.BaseModel): # type: ignore[reportUnreachable] baz: UUID | None = None +@pytest.mark.parametrize( + "hint", + [frozenset, frozenset[int], typing_extensions.FrozenSet[int]], # type:ignore[reportDeprecated] +) +@pytest.mark.parametrize("value", [frozenset(), frozenset({1, 2})]) +def test_json_frozenset_round_trip(hint: Any, value: frozenset[int]): + converter = JSONPlainPayloadConverter() + payload = converter.to_payload(value) + assert payload is not None + converted = converter.from_payload(payload, hint) + assert isinstance(converted, frozenset) + assert converted == value + + +def test_json_nested_frozenset_round_trip(): + converter = JSONPlainPayloadConverter() + value = {frozenset({1, 2}), frozenset({3})} + payload = converter.to_payload(value) + assert payload is not None + assert converter.from_payload(payload, set[frozenset[int]]) == value + + def test_json_type_hints(): converter = JSONPlainPayloadConverter() @@ -439,6 +461,7 @@ def fail(hint: Any, value: Any) -> None: ok(deque[int], deque([5, 6])) ok(Sequence[int], [5, 6]) fail(list[int], [1, 2, "3"]) + fail(frozenset[int], [1, 2, "3"]) # Dict-like ok(dict[str, MyDataClass], {"foo": MyDataClass("foo", 5, SerializableEnum.FOO)}) From a5d86dc86d6bccda2b6650a06269d991e68732b7 Mon Sep 17 00:00:00 2001 From: Tim Conley Date: Mon, 28 Sep 2026 14:57:52 -0700 Subject: [PATCH 2/2] Remove unnecessary comment --- temporalio/converter/_payload_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/temporalio/converter/_payload_converter.py b/temporalio/converter/_payload_converter.py index 77fabdf8de..a32de04094 100644 --- a/temporalio/converter/_payload_converter.py +++ b/temporalio/converter/_payload_converter.py @@ -744,7 +744,7 @@ class JSONPlainPayloadConverter(EncodingPayloadConverter): ``dict()`` methods, and all iterables. For decoding, this uses type hints to attempt to rebuild the type from the - type hint, including ``frozenset`` collections. + type hint. """ _encoder: type[json.JSONEncoder] | None