diff --git a/py/obj.h b/py/obj.h index 125acf118f08d..468125eb75699 100644 --- a/py/obj.h +++ b/py/obj.h @@ -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_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_copy(mp_obj_t self_in); 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; } diff --git a/py/objdict.c b/py/objdict.c index 7690eeab29af1..69eda99738eec 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -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_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_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); 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)); 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 // this is a classmethod diff --git a/py/objtype.c b/py/objtype.c index d08c69e284b3e..709beb610a45b 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -587,17 +587,16 @@ STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des } #if MICROPY_CPYTHON_COMPAT if (attr == MP_QSTR___dict__) { - // Create a new dict with a copy of the instance's map items. + // Returns a read-only dict of the instance members. + // If the internal locals is not fixed, a copy will be created. // This creates, unlike CPython, a 'read-only' __dict__: modifying // it will not result in modifications to the actual instance members. - mp_map_t *map = &self->members; - mp_obj_t attr_dict = mp_obj_new_dict(map->used); - for (size_t i = 0; i < map->alloc; ++i) { - if (mp_map_slot_is_filled(map, i)) { - mp_obj_dict_store(attr_dict, map->table[i].key, map->table[i].value); - } - } - dest[0] = attr_dict; + mp_obj_dict_t dict; + dict.map = self->members; + dict.base.type = &mp_type_dict; + dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(&dict)); + mp_obj_dict_t *dest_dict = MP_OBJ_TO_PTR(dest[0]); + dest_dict->map.is_fixed = 1; return; } #endif @@ -1013,6 +1012,21 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_NEW_QSTR(self->name); 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 (self == &mp_type_object) { dest[0] = mp_const_empty_tuple; diff --git a/tests/basics/class_dict.py b/tests/basics/class_dict.py new file mode 100644 index 0000000000000..6c8fa680ba67d --- /dev/null +++ b/tests/basics/class_dict.py @@ -0,0 +1,10 @@ + +class Foo: + a = 1 + b = "bar" + +if not hasattr(Foo, "__dict__"): + print("SKIP") + raise SystemExit + +print(Foo.__dict__ == {'a': 1, 'b': 'bar'})
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: