From 80927bffe5ad1eb9da6521c7a060985f3500a187 Mon Sep 17 00:00:00 2001 From: Alexis Placet Date: Wed, 26 Aug 2026 15:28:28 +0200 Subject: [PATCH] fix: to_js dict key 'size' collides with Map.prototype.size The LiteralMap get trap read map[k] before map.get(k), so a stored key named "size" returned Map.prototype.size (the entry count) instead of the stored value. Check map.has(k) first, matching getOwnPropertyDescriptor. Adds TestPyToJsDict regression tests (test_dict_size_key, int values, roundtrip). Full browser suite: 88 pytest + 4 async passed. --- include/pyjs/pre_js/literal_map.js | 4 +++- tests/tests/test_conversion.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/pyjs/pre_js/literal_map.js b/include/pyjs/pre_js/literal_map.js index 4cf0085..7c2a7b9 100644 --- a/include/pyjs/pre_js/literal_map.js +++ b/include/pyjs/pre_js/literal_map.js @@ -16,11 +16,13 @@ const handler = { deleteProperty: (map, k) => (map.has(k) ? map.delete(k) : delete map[k]), get(map, k, proxy) { if (k === _) return map; + // Stored keys must win over Map.prototype members (e.g. a key named + // "size" would otherwise shadow the entry count getter `map.size`). + if (map.has(k)) return map.get(k); let v = map[k]; if (typeof v === "function" && k !== "constructor") { v = v.bind(map); } - v ||= map.get(k); return v; }, getOwnPropertyDescriptor(map, k) { diff --git a/tests/tests/test_conversion.py b/tests/tests/test_conversion.py index f5be36c..b7ed82d 100644 --- a/tests/tests/test_conversion.py +++ b/tests/tests/test_conversion.py @@ -16,3 +16,25 @@ def test_fundamentals(self, test_input): def test_none(self): t = pyjs.to_js(None) assert pyjs.pyjs_core._module._is_undefined(t) == True + + +class TestPyToJsDict: + def test_dict_size_key(self): + # regression: a dict key named "size" used to collide with + # Map.prototype.size (the entry count getter) in the LiteralMap get trap. + d = pyjs.to_js({"size": 7}) + assert pyjs.to_py(d["size"]) == 7 + + def test_dict_int_values(self): + d = pyjs.to_js({"size": 32, "usage": 136, "zero": 0, "one": 1}) + assert pyjs.to_py(d["size"]) == 32 + assert pyjs.to_py(d["usage"]) == 136 + assert pyjs.to_py(d["zero"]) == 0 + assert pyjs.to_py(d["one"]) == 1 + + def test_dict_roundtrip(self): + x = {"size": 32, "label": "hello", "flag": True} + d = pyjs.to_js(x) + assert pyjs.to_py(d["size"]) == 32 + assert pyjs.to_py(d["label"]) == "hello" + assert pyjs.to_py(d["flag"]) is True