py/runtime: Allow types to use both .attr and .locals_dict.

Make it possible to proceed to a regular lookup in locals_dict if the
custom type->attr fails.  This allows type->attr to extend rather than
completely replace the lookup in locals_dict.

This is useful for custom builtin classes that have mostly regular methods
but just a few special attributes/properties.  This way, type->attr needs
to deal with the special cases only and the default lookup will be used for
generic methods.

Signed-off-by: Laurens Valk <laurens@pybricks.com>
This commit is contained in:
Laurens Valk 2020-07-17 22:31:09 +02:00 committed by Damien George
parent 01ceb9aca3
commit e2ca8ab8fc
2 changed files with 14 additions and 4 deletions

View File

@ -551,6 +551,7 @@ struct _mp_obj_type_t {
//
// dest[0] = MP_OBJ_NULL means load
// return: for fail, do nothing
// for fail but continue lookup in locals_dict, dest[1] = MP_OBJ_SENTINEL
// for attr, dest[0] = value
// for method, dest[0] = method, dest[1] = self
//

View File

@ -1100,12 +1100,20 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
if (attr == MP_QSTR___next__ && type->iternext != NULL) {
dest[0] = MP_OBJ_FROM_PTR(&mp_builtin_next_obj);
dest[1] = obj;
} else if (type->attr != NULL) {
return;
}
if (type->attr != NULL) {
// this type can do its own load, so call it
type->attr(obj, attr, dest);
} else if (type->locals_dict != NULL) {
// If type->attr has set dest[1] = MP_OBJ_SENTINEL, we should proceed
// with lookups below (i.e. in locals_dict). If not, return right away.
if (dest[1] != MP_OBJ_SENTINEL) {
return;
}
// Clear the fail flag set by type->attr so it's like it never ran.
dest[1] = MP_OBJ_NULL;
}
if (type->locals_dict != NULL) {
// generic method lookup
// this is a lookup in the object (ie not class or type)
assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now
@ -1114,6 +1122,7 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
if (elem != NULL) {
mp_convert_member_lookup(obj, type, elem->value, dest);
}
return;
}
}