py/objtype: Add __dict__ attribute for class objects.

The behavior mirrors the instance object dict attribute where a copy of the
local attributes are provided (unless the dict is read-only, then that dict
itself is returned, as an optimisation).  MicroPython does not support
modifying this dict because the changes will not be reflected in the class.

The feature is only enabled if MICROPY_CPYTHON_COMPAT is set, the same as
the instance version.
This commit is contained in:
Andrew Leech 2020-06-10 10:45:24 +10:00 committed by Damien George
parent 29e258611a
commit 28370c0450
4 changed files with 37 additions and 2 deletions

View File

@ -895,6 +895,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in);
mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index); mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index);
mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key); mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key);
mp_obj_t mp_obj_dict_copy(mp_obj_t self_in);
static inline mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) { static inline mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) {
return &((mp_obj_dict_t *)MP_OBJ_TO_PTR(dict))->map; return &((mp_obj_dict_t *)MP_OBJ_TO_PTR(dict))->map;
} }

View File

@ -227,7 +227,7 @@ STATIC mp_obj_t dict_clear(mp_obj_t self_in) {
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear);
STATIC mp_obj_t dict_copy(mp_obj_t self_in) { mp_obj_t mp_obj_dict_copy(mp_obj_t self_in) {
mp_check_self(mp_obj_is_dict_type(self_in)); mp_check_self(mp_obj_is_dict_type(self_in));
mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); mp_obj_t other_out = mp_obj_new_dict(self->map.alloc);
@ -240,7 +240,7 @@ STATIC mp_obj_t dict_copy(mp_obj_t self_in) {
memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t));
return other_out; return other_out;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy); STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, mp_obj_dict_copy);
#if MICROPY_PY_BUILTINS_DICT_FROMKEYS #if MICROPY_PY_BUILTINS_DICT_FROMKEYS
// this is a classmethod // this is a classmethod

View File

@ -1013,6 +1013,21 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
dest[0] = MP_OBJ_NEW_QSTR(self->name); dest[0] = MP_OBJ_NEW_QSTR(self->name);
return; return;
} }
#if MICROPY_CPYTHON_COMPAT
if (attr == MP_QSTR___dict__) {
// Returns a read-only dict of the class attributes.
// If the internal locals is not fixed, a copy will be created.
mp_obj_dict_t *dict = self->locals_dict;
if (dict->map.is_fixed) {
dest[0] = MP_OBJ_FROM_PTR(dict);
} else {
dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(dict));
dict = MP_OBJ_TO_PTR(dest[0]);
dict->map.is_fixed = 1;
}
return;
}
#endif
if (attr == MP_QSTR___bases__) { if (attr == MP_QSTR___bases__) {
if (self == &mp_type_object) { if (self == &mp_type_object) {
dest[0] = mp_const_empty_tuple; dest[0] = mp_const_empty_tuple;

View File

@ -0,0 +1,19 @@
# test __dict__ attribute of a class
if not hasattr(int, "__dict__"):
print("SKIP")
raise SystemExit
# dict of a built-in type
print("from_bytes" in int.__dict__)
# dict of a user class
class Foo:
a = 1
b = "bar"
d = Foo.__dict__
print(d["a"], d["b"])