py: Implement delete for property and descriptors.

Without this patch deleting a property, or class with descriptor, will
call the setter with a NULL value and lead to a crash.
This commit is contained in:
Damien George 2015-04-04 20:15:31 +01:00
parent 0528c5a22a
commit 56606f3475
2 changed files with 44 additions and 19 deletions

View File

@ -533,35 +533,59 @@ bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
if (member[0] != MP_OBJ_NULL) { if (member[0] != MP_OBJ_NULL) {
#if MICROPY_PY_BUILTINS_PROPERTY #if MICROPY_PY_BUILTINS_PROPERTY
if (MP_OBJ_IS_TYPE(member[0], &mp_type_property)) { if (MP_OBJ_IS_TYPE(member[0], &mp_type_property)) {
// attribute exists and is a property; delegate the store // attribute exists and is a property; delegate the store/delete
// Note: This is an optimisation for code size and execution time. // Note: This is an optimisation for code size and execution time.
// The proper way to do it is have the functionality just below // The proper way to do it is have the functionality just below in
// in a __set__ method of the property object, and then it would // a __set__/__delete__ method of the property object, and then it
// be called by the descriptor code down below. But that way // would be called by the descriptor code down below. But that way
// requires overhead for the nested mp_call's and overhead for // requires overhead for the nested mp_call's and overhead for
// the code. // the code.
const mp_obj_t *proxy = mp_obj_property_get(member[0]); const mp_obj_t *proxy = mp_obj_property_get(member[0]);
if (proxy[1] == mp_const_none) { mp_obj_t dest[2] = {self_in, value};
// TODO better error message? if (value == MP_OBJ_NULL) {
return false; // delete attribute
if (proxy[2] == mp_const_none) {
// TODO better error message?
return false;
} else {
mp_call_function_n_kw(proxy[2], 1, 0, dest);
return true;
}
} else { } else {
mp_obj_t dest[2] = {self_in, value}; // store attribute
mp_call_function_n_kw(proxy[1], 2, 0, dest); if (proxy[1] == mp_const_none) {
return true; // TODO better error message?
return false;
} else {
mp_call_function_n_kw(proxy[1], 2, 0, dest);
return true;
}
} }
} }
#endif #endif
#if MICROPY_PY_DESCRIPTORS #if MICROPY_PY_DESCRIPTORS
// found a class attribute; if it has a __set__ method then call it with the // found a class attribute; if it has a __set__/__delete__ method then
// class instance and value as arguments // call it with the class instance (and value) as arguments
mp_obj_t attr_set_method[4]; if (value == MP_OBJ_NULL) {
mp_load_method_maybe(member[0], MP_QSTR___set__, attr_set_method); // delete attribute
if (attr_set_method[0] != MP_OBJ_NULL) { mp_obj_t attr_delete_method[3];
attr_set_method[2] = self_in; mp_load_method_maybe(member[0], MP_QSTR___delete__, attr_delete_method);
attr_set_method[3] = value; if (attr_delete_method[0] != MP_OBJ_NULL) {
mp_call_method_n_kw(2, 0, attr_set_method); attr_delete_method[2] = self_in;
return true; mp_call_method_n_kw(1, 0, attr_delete_method);
return true;
}
} else {
// store attribute
mp_obj_t attr_set_method[4];
mp_load_method_maybe(member[0], MP_QSTR___set__, attr_set_method);
if (attr_set_method[0] != MP_OBJ_NULL) {
attr_set_method[2] = self_in;
attr_set_method[3] = value;
mp_call_method_n_kw(2, 0, attr_set_method);
return true;
}
} }
#endif #endif
} }

View File

@ -69,6 +69,7 @@ Q(__str__)
#if MICROPY_PY_DESCRIPTORS #if MICROPY_PY_DESCRIPTORS
Q(__get__) Q(__get__)
Q(__set__) Q(__set__)
Q(__delete__)
#endif #endif
Q(__getattr__) Q(__getattr__)
Q(__del__) Q(__del__)