diff options
-rw-r--r-- | ports/webassembly/README.md | 4 | ||||
-rw-r--r-- | ports/webassembly/proxy_c.c | 11 | ||||
-rw-r--r-- | tests/ports/webassembly/py_proxy_dict_undefined.mjs | 34 | ||||
-rw-r--r-- | tests/ports/webassembly/py_proxy_dict_undefined.mjs.exp | 11 |
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 } |