diff --git a/py/bc.c b/py/bc.c index 750ca2aed804bbeefa29d75203298b48091ce4f8..5cdaa4770db88c835f1e3c69107ad74a137f3395 100644 --- a/py/bc.c +++ b/py/bc.c @@ -171,6 +171,7 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_fun_bc_t *self, size_ const mp_obj_t *arg_names = (const mp_obj_t*)code_state->const_table; for (size_t i = 0; i < n_kw; i++) { + // the keys in kwargs are expected to be qstr objects mp_obj_t wanted_arg_name = kwargs[2 * i]; for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { if (wanted_arg_name == arg_names[j]) { diff --git a/py/runtime.c b/py/runtime.c index adbab579f0cdc1792c1a6f89bc58530520d3241b..67534c4b5e35910c020c89eb3ca34125280a2029 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -704,7 +704,12 @@ void mp_call_prepare_args_n_kw_var(bool have_self, mp_uint_t n_args_n_kw, const assert(args2_len + 2 * map->used <= args2_alloc); // should have enough, since kw_dict_len is in this case hinted correctly above for (mp_uint_t i = 0; i < map->alloc; i++) { if (MP_MAP_SLOT_IS_FILLED(map, i)) { - args2[args2_len++] = map->table[i].key; + // the key must be a qstr, so intern it if it's a string + mp_obj_t key = map->table[i].key; + if (MP_OBJ_IS_TYPE(key, &mp_type_str)) { + key = mp_obj_str_intern(key); + } + args2[args2_len++] = key; args2[args2_len++] = map->table[i].value; } } @@ -726,7 +731,12 @@ void mp_call_prepare_args_n_kw_var(bool have_self, mp_uint_t n_args_n_kw, const } mp_obj_t *items; mp_obj_get_array_fixed_n(item, 2, &items); - args2[args2_len++] = items[0]; + // the key must be a qstr, so intern it if it's a string + mp_obj_t key = items[0]; + if (MP_OBJ_IS_TYPE(key, &mp_type_str)) { + key = mp_obj_str_intern(key); + } + args2[args2_len++] = key; args2[args2_len++] = items[1]; } } diff --git a/tests/basics/fun_calldblstar2.py b/tests/basics/fun_calldblstar2.py new file mode 100644 index 0000000000000000000000000000000000000000..cf982ef5b8a6dee8db7682090094ab89b666dfc3 --- /dev/null +++ b/tests/basics/fun_calldblstar2.py @@ -0,0 +1,13 @@ +# test passing a string object as the key for a keyword argument + +# they key in this dict is a string object and is not interned +args = {'thisisaverylongargumentname': 123} + +# when this string is executed it will intern the keyword argument +exec("def foo(*,thisisaverylongargumentname=1):\n print(thisisaverylongargumentname)") + +# test default arg +foo() + +# the string from the dict should match the interned keyword argument +foo(**args)