This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 6a29c59c63 feat(pyamber): reject non-string state keys (#8959)
6a29c59c63 is described below
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())