summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/webassembly/README.md4
-rw-r--r--ports/webassembly/proxy_c.c11
-rw-r--r--tests/ports/webassembly/py_proxy_dict_undefined.mjs34
-rw-r--r--tests/ports/webassembly/py_proxy_dict_undefined.mjs.exp11
4 files changed, 58 insertions, 2 deletions
diff --git a/ports/webassembly/README.md b/ports/webassembly/README.md
index 97cb397fd..8a3029aa0 100644
--- a/ports/webassembly/README.md
+++ b/ports/webassembly/README.md
@@ -182,4 +182,6 @@ A Python `dict` instance is proxied such that:
}
works as expected on the JavaScript side and iterates through the keys of the
-Python `dict`.
+Python `dict`. Furthermore, when JavaScript accesses a key that does not exist
+in the Python dict, the JavaScript code receives `undefined` instead of a
+`KeyError` exception being raised.
diff --git a/ports/webassembly/proxy_c.c b/ports/webassembly/proxy_c.c
index b55740175..7d5b62368 100644
--- a/ports/webassembly/proxy_c.c
+++ b/ports/webassembly/proxy_c.c
@@ -241,8 +241,17 @@ void proxy_c_to_js_lookup_attr(uint32_t c_ref, const char *attr_in, uint32_t *ou
qstr attr = qstr_from_str(attr_in);
mp_obj_t member;
if (mp_obj_is_dict_or_ordereddict(obj)) {
- member = mp_obj_dict_get(obj, MP_OBJ_NEW_QSTR(attr));
+ // Lookup the requested attribute as a key in the target dict, and
+ // return `undefined` if not found (instead of raising `KeyError`).
+ mp_obj_dict_t *self = MP_OBJ_TO_PTR(obj);
+ mp_map_elem_t *elem = mp_map_lookup(&self->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
+ if (elem == NULL) {
+ member = mp_const_undefined;
+ } else {
+ member = elem->value;
+ }
} else {
+ // Lookup the requested attribute as a member/method of the target object.
member = mp_load_attr(obj, attr);
}
nlr_pop();
diff --git a/tests/ports/webassembly/py_proxy_dict_undefined.mjs b/tests/ports/webassembly/py_proxy_dict_undefined.mjs
new file mode 100644
index 000000000..d47a6a028
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_dict_undefined.mjs
@@ -0,0 +1,34 @@
+// Test passing a Python dict into JavaScript, how it behaves with undefined keys.
+// If JavaScript accesses a key that does not exist, `undefined` should be returned.
+// This is different to Python-side behaviour, where `KeyError` is raised.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+// Create a JavaScript function with default arguments.
+// When `value` is `undefined` it will receive its default.
+function withDefault({ value = "OK" } = {}) {
+ console.log(value);
+}
+
+globalThis.withDefault = withDefault;
+
+// Call the function from JavaScript with various arguments.
+withDefault();
+withDefault({});
+withDefault({ value: null });
+withDefault({ value: undefined });
+withDefault({ value: () => {} });
+
+console.log("====");
+
+// Call the function from Python with the same arguments as above.
+// The results should be the same.
+mp.runPython(`
+import js
+
+js.withDefault()
+js.withDefault({})
+js.withDefault({"value": None})
+js.withDefault({"value": js.undefined})
+js.withDefault({"value": (lambda: {})})
+`);
diff --git a/tests/ports/webassembly/py_proxy_dict_undefined.mjs.exp b/tests/ports/webassembly/py_proxy_dict_undefined.mjs.exp
new file mode 100644
index 000000000..6cf5b9fa1
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_dict_undefined.mjs.exp
@@ -0,0 +1,11 @@
+OK
+OK
+null
+OK
+[Function: value]
+====
+OK
+OK
+null
+OK
+[Function: obj] { _ref: 7 }