Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,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.

- Ordinary absolute imports of already-loaded modules in sandboxed workflows no longer go through
importlib's module locks, fixing intermittent `Failed validating workflow` errors on Python 3.10
caused by a `KeyError` in `importlib._bootstrap._ModuleLock.acquire` when a garbage-collection
Expand Down
4 changes: 3 additions & 1 deletion temporalio/converter/_payload_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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)})
Expand Down
Loading