py: Implement __getattr__.

It's not completely satisfactory, because a failed call to __getattr__
should not raise an exception.

__setattr__ could be implemented, but it would slow down all stores to a
user created object.  Need to implement some caching system.
This commit is contained in:
Damien George
2014-03-31 22:57:56 +01:00
parent 4db727afea
commit e44d26ae0c
5 changed files with 65 additions and 41 deletions

View File

@@ -701,7 +701,7 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) {
// no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL
// normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL
// method attribute found, returns: dest[0] == <method>, dest[1] == <self>
STATIC void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
// clear output to indicate no attribute/method found yet
dest[0] = MP_OBJ_NULL;
dest[1] = MP_OBJ_NULL;
@@ -709,48 +709,45 @@ STATIC void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
// get the type
mp_obj_type_t *type = mp_obj_get_type(base);
// if this type can do its own load, then call it
if (type->load_attr != NULL) {
type->load_attr(base, attr, dest);
}
// if nothing found yet, look for built-in and generic names
if (dest[0] == MP_OBJ_NULL) {
// look for built-in names
if (0) {
#if MICROPY_CPYTHON_COMPAT
if (attr == MP_QSTR___class__) {
// a.__class__ is equivalent to type(a)
dest[0] = type;
} else
} else if (attr == MP_QSTR___class__) {
// a.__class__ is equivalent to type(a)
dest[0] = type;
#endif
if (attr == MP_QSTR___next__ && type->iternext != NULL) {
dest[0] = (mp_obj_t)&mp_builtin_next_obj;
dest[1] = base;
} else if (type->load_attr == NULL) {
// generic method lookup if type didn't provide a specific one
// this is a lookup in the object (ie not class or type)
if (type->locals_dict != NULL) {
assert(MP_OBJ_IS_TYPE(type->locals_dict, &mp_type_dict)); // Micro Python restriction, for now
mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict);
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
if (elem != NULL) {
// check if the methods are functions, static or class methods
// see http://docs.python.org/3.3/howto/descriptor.html
if (MP_OBJ_IS_TYPE(elem->value, &mp_type_staticmethod)) {
// return just the function
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
} else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_classmethod)) {
// return a bound method, with self being the type of this object
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
dest[1] = mp_obj_get_type(base);
} else if (mp_obj_is_callable(elem->value)) {
// return a bound method, with self being this object
dest[0] = elem->value;
dest[1] = base;
} else {
// class member is a value, so just return that value
dest[0] = elem->value;
}
}
} else if (attr == MP_QSTR___next__ && type->iternext != NULL) {
dest[0] = (mp_obj_t)&mp_builtin_next_obj;
dest[1] = base;
} else if (type->load_attr != NULL) {
// this type can do its own load, so call it
type->load_attr(base, attr, dest);
} else if (type->locals_dict != NULL) {
// generic method lookup
// this is a lookup in the object (ie not class or type)
assert(MP_OBJ_IS_TYPE(type->locals_dict, &mp_type_dict)); // Micro Python restriction, for now
mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict);
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
if (elem != NULL) {
// check if the methods are functions, static or class methods
// see http://docs.python.org/3.3/howto/descriptor.html
if (MP_OBJ_IS_TYPE(elem->value, &mp_type_staticmethod)) {
// return just the function
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
} else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_classmethod)) {
// return a bound method, with self being the type of this object
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
dest[1] = mp_obj_get_type(base);
} else if (mp_obj_is_callable(elem->value)) {
// return a bound method, with self being this object
dest[0] = elem->value;
dest[1] = base;
} else {
// class member is a value, so just return that value
dest[0] = elem->value;
}
}
}