summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--py/mpconfig.h6
-rw-r--r--py/objmodule.c36
-rw-r--r--py/objmodule.h5
3 files changed, 47 insertions, 0 deletions
diff --git a/py/mpconfig.h b/py/mpconfig.h
index a41e99b4c..2b03b93f4 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -822,6 +822,12 @@ typedef double mp_float_t;
#define MICROPY_STREAMS_POSIX_API (0)
#endif
+// Whether modules can use MP_MODULE_ATTR_DELEGATION_ENTRY() to delegate failed
+// attribute lookups.
+#ifndef MICROPY_MODULE_ATTR_DELEGATION
+#define MICROPY_MODULE_ATTR_DELEGATION (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
+#endif
+
// Whether to call __init__ when importing builtin modules for the first time
#ifndef MICROPY_MODULE_BUILTIN_INIT
#define MICROPY_MODULE_BUILTIN_INIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
diff --git a/py/objmodule.c b/py/objmodule.c
index f7cd437ba..0cd10e61f 100644
--- a/py/objmodule.c
+++ b/py/objmodule.c
@@ -62,6 +62,21 @@ STATIC void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin
mp_printf(print, "<module '%s'>", module_name);
}
+STATIC void module_attr_try_delegation(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+ #if MICROPY_MODULE_ATTR_DELEGATION
+ // Delegate lookup to a module's custom attr method (found in last lot of globals dict).
+ mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_map_t *map = &self->globals->map;
+ if (map->table[map->alloc - 1].key == MP_OBJ_NEW_QSTR(MP_QSTRnull)) {
+ ((mp_attr_fun_t)MP_OBJ_TO_PTR(map->table[map->alloc - 1].value))(self_in, attr, dest);
+ }
+ #else
+ (void)self_in;
+ (void)attr;
+ (void)dest;
+ #endif
+}
+
STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] == MP_OBJ_NULL) {
@@ -74,8 +89,12 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___getattr__), MP_MAP_LOOKUP);
if (elem != NULL) {
dest[0] = mp_call_function_1(elem->value, MP_OBJ_NEW_QSTR(attr));
+ } else {
+ module_attr_try_delegation(self_in, attr, dest);
}
#endif
+ } else {
+ module_attr_try_delegation(self_in, attr, dest);
}
} else {
// delete/store attribute
@@ -91,6 +110,7 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
#endif
{
// can't delete or store to fixed map
+ module_attr_try_delegation(self_in, attr, dest);
return;
}
}
@@ -319,3 +339,19 @@ STATIC void mp_module_call_init(mp_obj_t module_name, mp_obj_t module_obj) {
}
}
#endif
+
+void mp_module_generic_attr(qstr attr, mp_obj_t *dest, const uint16_t *keys, mp_obj_t *values) {
+ for (size_t i = 0; keys[i] != MP_QSTRnull; ++i) {
+ if (attr == keys[i]) {
+ if (dest[0] == MP_OBJ_NULL) {
+ // load attribute (MP_OBJ_NULL returned for deleted items)
+ dest[0] = values[i];
+ } else {
+ // delete or store (delete stores MP_OBJ_NULL)
+ values[i] = dest[1];
+ dest[0] = MP_OBJ_NULL; // indicate success
+ }
+ return;
+ }
+ }
+}
diff --git a/py/objmodule.h b/py/objmodule.h
index 8a82d13fe..d11d5bcd7 100644
--- a/py/objmodule.h
+++ b/py/objmodule.h
@@ -28,6 +28,9 @@
#include "py/obj.h"
+// Place at the very end of a module's globals_table.
+#define MP_MODULE_ATTR_DELEGATION_ENTRY(ptr) { MP_ROM_QSTR(MP_QSTRnull), MP_ROM_PTR(ptr) }
+
extern const mp_map_t mp_builtin_module_map;
mp_obj_t mp_module_get_loaded_or_builtin(qstr module_name);
@@ -35,4 +38,6 @@ mp_obj_t mp_module_get_loaded_or_builtin(qstr module_name);
mp_obj_t mp_module_get_builtin(qstr module_name);
#endif
+void mp_module_generic_attr(qstr attr, mp_obj_t *dest, const uint16_t *keys, mp_obj_t *values);
+
#endif // MICROPY_INCLUDED_PY_OBJMODULE_H