summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Leech <andrew.leech@planetinnovation.com.au>2020-06-10 10:45:24 +1000
committerDamien George <damien.p.george@gmail.com>2020-06-10 21:58:13 +1000
commit28370c04509a6255cd3d6b9424443a5b57bb7467 (patch)
tree98d1f78a1805d14b85a88e54c910e0cf46168598
parent29e258611ace8e4de51d810d5ff14364a5fbb950 (diff)
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.
-rw-r--r--py/obj.h1
-rw-r--r--py/objdict.c4
-rw-r--r--py/objtype.c15
-rw-r--r--tests/basics/class_dict.py19
4 files changed, 37 insertions, 2 deletions
diff --git a/py/obj.h b/py/obj.h
index 125acf118..468125eb7 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 7690eeab2..69eda9973 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 d08c69e28..cb0fb267c 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -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);
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 000000000..f80ded678
--- /dev/null
+++ b/tests/basics/class_dict.py
@@ -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"])