Page 1 of 1

[SOLVED]mp_obj_dict_get - why throw on element not found?

Posted: Thu Mar 29, 2018 3:41 pm
by jickster
Why does this throw instead of returning MP_OBJ_NULL?

Code: Select all

mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) {
    mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
    mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP);
    if (elem == NULL) {
        nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, index));
    } else {
        return elem->value;
    }
}

Re: mp_obj_dict_get - why throw on element not found?

Posted: Thu Mar 29, 2018 4:53 pm
by dhylands
jickster wrote:
Thu Mar 29, 2018 3:41 pm
Why does this throw instead of returning MP_OBJ_NULL?

Code: Select all

mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) {
    mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
    mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP);
    if (elem == NULL) {
        nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, index));
    } else {
        return elem->value;
    }
}
Probably because in all of the places where its called, throwing is more appropriate. Having each caller check and then throw uses more code. Also MP_OBJ_NULL isn't a valid mp_obj_t, so the caller needs to make sure it never gets back to python. You could all mp_obj_dict_get_map and then call mp_map_lookup if you don't want the raise behaviour (i.e. look inside mp_obj_dict_get)

Re: mp_obj_dict_get - why throw on element not found?

Posted: Thu Mar 29, 2018 7:23 pm
by jickster
dhylands wrote:
Thu Mar 29, 2018 4:53 pm
jickster wrote:
Thu Mar 29, 2018 3:41 pm
Why does this throw instead of returning MP_OBJ_NULL?

Code: Select all

mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) {
    mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
    mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP);
    if (elem == NULL) {
        nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, index));
    } else {
        return elem->value;
    }
}
Probably because in all of the places where its called, throwing is more appropriate. Having each caller check and then throw uses more code. Also MP_OBJ_NULL isn't a valid mp_obj_t, so the caller needs to make sure it never gets back to python. You could all mp_obj_dict_get_map and then call mp_map_lookup if you don't want the raise behaviour (i.e. look inside mp_obj_dict_get)

Then why is it ok for this to return MP_OBJ_NULL?

Code: Select all

mp_obj_t mp_module_get(qstr module_name) {
    mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map;
    // lookup module
    mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP);

    if (el == NULL) {
        // module not found, look for builtin module names
        el = mp_map_lookup((mp_map_t*)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP);
        if (el == NULL) {
            return MP_OBJ_NULL;
        }

        if (MICROPY_MODULE_BUILTIN_INIT) {
            // look for __init__ and call it if it exists
            mp_obj_t dest[2];
            mp_load_method_maybe(el->value, MP_QSTR___init__, dest);
            if (dest[0] != MP_OBJ_NULL) {
                mp_call_method_n_kw(0, 0, dest);
                // register module so __init__ is not called again
                mp_module_register(module_name, el->value);
            }
        }
    }

    // module found, return it
    return el->value;
}

Re: mp_obj_dict_get - why throw on element not found?

Posted: Fri Mar 30, 2018 5:26 am
by dhylands
Probably because that function is never directly called from python and it's return code is never passed back to python.