This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-8959-9cf43f2de96c9a1a8a93579835b8b7de66243d80 in repository https://gitbox.apache.org/repos/asf/texera.git
commit 6a29c59c6312da95826b269fca2a6a14006fb34a Author: carloea2 <[email protected]> AuthorDate: Mon Oct 12 22:28:20 2026 +0000 feat(pyamber): reject non-string state keys (#8959) ### What changes were proposed in this PR? Reject non-string state dictionary keys before serialization. Converting both 1 and "1" to the same JSON key silently loses a value. This introduces an explicit TypeError for non-string keys, including nested mappings. ### Any related issues, documentation, discussions? Closes #8215 Replaces #8216, which the stale bot closed. The original PR cannot be reopened by this account. ### How was this PR tested? Tested on freshly fetched main at d27b7b49877bcc52d80602e57f5b0e5f86291736 with Python 3.12 on Windows. The regression failed before the source change and passed after it. From the amber directory, with generated protobuf bindings available on PYTHONPATH: ```text python -m pytest src/test/python/core/models/test_state.py::TestState::test_to_json_rejects_non_string_keys -q --tb=line -p no:cacheprovider python -m pytest src/test/python/core/models src/test/python/core/architecture/sendsemantics/test_partitioners.py --deselect src/test/python/core/models/test_tuple.py::TestTuple::test_hash -q --tb=line -p no:cacheprovider ruff check src/main/python src/test/python ruff format --check src/main/python src/test/python ``` The broader suites passed 351 tests with one expected failure. Ruff lint and formatting passed for all 216 Python files. The existing Windows pre-epoch hash fixture was deselected; the new hash regressions were run separately. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Codex (GPT-6) --- amber/src/main/python/core/models/state.py | 4 +++- amber/src/test/python/core/models/test_state.py | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/amber/src/main/python/core/models/state.py b/amber/src/main/python/core/models/state.py index 559d6eca45..7e47fa9833 100644 --- a/amber/src/main/python/core/models/state.py +++ b/amber/src/main/python/core/models/state.py @@ -93,7 +93,9 @@ def _to_json_value(value: Any) -> Any: _PAYLOAD_MARKER: base64.b64encode(value).decode("ascii"), } if isinstance(value, dict): - return {str(key): _to_json_value(inner) for key, inner in value.items()} + if any(not isinstance(key, str) for key in value): + raise TypeError("State dictionary keys must be strings") + return {key: _to_json_value(inner) for key, inner in value.items()} if isinstance(value, (list, tuple)): return [_to_json_value(inner) for inner in value] raise TypeError( diff --git a/amber/src/test/python/core/models/test_state.py b/amber/src/test/python/core/models/test_state.py index b1c9fad35a..19afab9e94 100644 --- a/amber/src/test/python/core/models/test_state.py +++ b/amber/src/test/python/core/models/test_state.py @@ -90,6 +90,13 @@ class TestState: with pytest.raises(TypeError): State({"bad": Custom()}).to_json() + @pytest.mark.parametrize( + "state", [State({1: "value"}), State({"nested": {1: "value"}})] + ) + def test_to_json_rejects_non_string_keys(self, state): + with pytest.raises(TypeError, match="keys must be strings"): + state.to_json() + def test_tuple_round_trip(self): original = State({"i": 3, "label": "outer", "blob": b"\x01\x02"}) decoded = State.from_tuple(original.to_tuple())
