Skip to content

Commit cfd5a8e

Browse files
committed
webassembly/proxy_c: Return undefined if dict lookup failed on JS side.
Instead of raising KeyError. These semantics match JavaScript behaviour and make it much more seamless to pass Python dicts through to JavaScript as though they were JavaScript {} objects. Signed-off-by: Damien George <damien@micropython.org>
1 parent aa2e388 commit cfd5a8e

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

ports/webassembly/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,6 @@ A Python `dict` instance is proxied such that:
182182
}
183183

184184
works as expected on the JavaScript side and iterates through the keys of the
185-
Python `dict`.
185+
Python `dict`. Furthermore, when JavaScript accesses a key that does not exist
186+
in the Python dict, the JavaScript code receives `undefined` instead of a
187+
`KeyError` exception being raised.

ports/webassembly/proxy_c.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,17 @@ void proxy_c_to_js_lookup_attr(uint32_t c_ref, const char *attr_in, uint32_t *ou
241241
qstr attr = qstr_from_str(attr_in);
242242
mp_obj_t member;
243243
if (mp_obj_is_dict_or_ordereddict(obj)) {
244-
member = mp_obj_dict_get(obj, MP_OBJ_NEW_QSTR(attr));
244+
// Lookup the requested attribute as a key in the target dict, and
245+
// return `undefined` if not found (instead of raising `KeyError`).
246+
mp_obj_dict_t *self = MP_OBJ_TO_PTR(obj);
247+
mp_map_elem_t *elem = mp_map_lookup(&self->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
248+
if (elem == NULL) {
249+
member = mp_const_undefined;
250+
} else {
251+
member = elem->value;
252+
}
245253
} else {
254+
// Lookup the requested attribute as a member/method of the target object.
246255
member = mp_load_attr(obj, attr);
247256
}
248257
nlr_pop();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Test passing a Python dict into JavaScript, how it behaves with undefined keys.
2+
// If JavaScript accesses a key that does not exist, `undefined` should be returned.
3+
// This is different to Python-side behaviour, where `KeyError` is raised.
4+
5+
const mp = await (await import(process.argv[2])).loadMicroPython();
6+
7+
// Create a JavaScript function with default arguments.
8+
// When `value` is `undefined` it will receive its default.
9+
function withDefault({ value = "OK" } = {}) {
10+
console.log(value);
11+
}
12+
13+
globalThis.withDefault = withDefault;
14+
15+
// Call the function from JavaScript with various arguments.
16+
withDefault();
17+
withDefault({});
18+
withDefault({ value: null });
19+
withDefault({ value: undefined });
20+
withDefault({ value: () => {} });
21+
22+
console.log("====");
23+
24+
// Call the function from Python with the same arguments as above.
25+
// The results should be the same.
26+
mp.runPython(`
27+
import js
28+
29+
js.withDefault()
30+
js.withDefault({})
31+
js.withDefault({"value": None})
32+
js.withDefault({"value": js.undefined})
33+
js.withDefault({"value": (lambda: {})})
34+
`);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
OK
2+
OK
3+
null
4+
OK
5+
[Function: value]
6+
====
7+
OK
8+
OK
9+
null
10+
OK
11+
[Function: obj] { _ref: 7 }

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy