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:
parent
4db727afea
commit
e44d26ae0c
14
py/objtype.c
14
py/objtype.c
@ -16,6 +16,7 @@
|
|||||||
typedef struct _mp_obj_class_t {
|
typedef struct _mp_obj_class_t {
|
||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
mp_map_t members;
|
mp_map_t members;
|
||||||
|
// TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them
|
||||||
} mp_obj_class_t;
|
} mp_obj_class_t;
|
||||||
|
|
||||||
STATIC mp_obj_t mp_obj_new_class(mp_obj_t class) {
|
STATIC mp_obj_t mp_obj_new_class(mp_obj_t class) {
|
||||||
@ -225,6 +226,19 @@ STATIC void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|||||||
} else {
|
} else {
|
||||||
// class member is a value, so just return that value
|
// class member is a value, so just return that value
|
||||||
dest[0] = member;
|
dest[0] = member;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try __getattr__
|
||||||
|
if (attr != MP_QSTR___getattr__) {
|
||||||
|
mp_obj_t dest2[3];
|
||||||
|
mp_load_method_maybe(self_in, MP_QSTR___getattr__, dest2);
|
||||||
|
if (dest2[0] != MP_OBJ_NULL) {
|
||||||
|
// __getattr__ exists, call it and return its result
|
||||||
|
// XXX if this fails to load the requested attr, should we catch the attribute error and return silently?
|
||||||
|
dest2[2] = MP_OBJ_NEW_QSTR(attr);
|
||||||
|
dest[0] = mp_call_method_n_kw(1, 0, dest2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ Q(__add__)
|
|||||||
Q(__sub__)
|
Q(__sub__)
|
||||||
Q(__repr__)
|
Q(__repr__)
|
||||||
Q(__str__)
|
Q(__str__)
|
||||||
|
Q(__getattr__)
|
||||||
|
|
||||||
Q(micropython)
|
Q(micropython)
|
||||||
Q(byte_code)
|
Q(byte_code)
|
||||||
|
29
py/runtime.c
29
py/runtime.c
@ -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
|
// 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
|
// normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL
|
||||||
// method attribute found, returns: dest[0] == <method>, dest[1] == <self>
|
// 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
|
// clear output to indicate no attribute/method found yet
|
||||||
dest[0] = MP_OBJ_NULL;
|
dest[0] = MP_OBJ_NULL;
|
||||||
dest[1] = MP_OBJ_NULL;
|
dest[1] = MP_OBJ_NULL;
|
||||||
@ -709,26 +709,25 @@ STATIC void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
|
|||||||
// get the type
|
// get the type
|
||||||
mp_obj_type_t *type = mp_obj_get_type(base);
|
mp_obj_type_t *type = mp_obj_get_type(base);
|
||||||
|
|
||||||
// if this type can do its own load, then call it
|
// look for built-in names
|
||||||
if (type->load_attr != NULL) {
|
if (0) {
|
||||||
type->load_attr(base, attr, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if nothing found yet, look for built-in and generic names
|
|
||||||
if (dest[0] == MP_OBJ_NULL) {
|
|
||||||
#if MICROPY_CPYTHON_COMPAT
|
#if MICROPY_CPYTHON_COMPAT
|
||||||
if (attr == MP_QSTR___class__) {
|
} else if (attr == MP_QSTR___class__) {
|
||||||
// a.__class__ is equivalent to type(a)
|
// a.__class__ is equivalent to type(a)
|
||||||
dest[0] = type;
|
dest[0] = type;
|
||||||
} else
|
|
||||||
#endif
|
#endif
|
||||||
if (attr == MP_QSTR___next__ && type->iternext != NULL) {
|
|
||||||
|
} else if (attr == MP_QSTR___next__ && type->iternext != NULL) {
|
||||||
dest[0] = (mp_obj_t)&mp_builtin_next_obj;
|
dest[0] = (mp_obj_t)&mp_builtin_next_obj;
|
||||||
dest[1] = base;
|
dest[1] = base;
|
||||||
} else if (type->load_attr == NULL) {
|
|
||||||
// generic method lookup if type didn't provide a specific one
|
} 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)
|
// 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
|
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_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);
|
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
|
||||||
@ -752,8 +751,6 @@ STATIC void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
|
void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
|
||||||
|
@ -46,6 +46,7 @@ void mp_unpack_sequence(mp_obj_t seq, uint num, mp_obj_t *items);
|
|||||||
mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value);
|
mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value);
|
||||||
mp_obj_t mp_load_attr(mp_obj_t base, qstr attr);
|
mp_obj_t mp_load_attr(mp_obj_t base, qstr attr);
|
||||||
void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
|
void mp_load_method(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);
|
||||||
void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
|
void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
|
||||||
void mp_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val);
|
void mp_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val);
|
||||||
|
|
||||||
|
11
tests/basics/getattr.py
Normal file
11
tests/basics/getattr.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# test __getattr__
|
||||||
|
|
||||||
|
class A:
|
||||||
|
def __init__(self, d):
|
||||||
|
self.d = d
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return self.d[attr]
|
||||||
|
|
||||||
|
a = A({'a':1, 'b':2})
|
||||||
|
print(a.a, a.b)
|
Loading…
Reference in New Issue
Block a user