py/objmodule: Support delegating failed attr lookups.

This commit adds generic support for mutable module attributes on built in
modules, by adding support for an optional hook function for module
attribute lookup.  If a module wants to support additional attribute load/
store/delete (beyond what is in the constant, globals dict) then it should
add at the very end of its globals dict MP_MODULE_ATTR_DELEGATION_ENTRY().
This should point to a custom function which will handle any additional
attributes.

The mp_module_generic_attr() function is provided as a helper function for
additional attributes: it requires an array of qstrs (terminated in
MP_QSTRnull) and a corresponding array of objects (with a 1-1 mapping
between qstrs and objects).  If the qstr is found in the array then the
corresponding object is loaded/stored/deleted.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2021-07-27 00:38:21 +10:00
parent d470c5a5ba
commit 3356b5ef8d
3 changed files with 47 additions and 0 deletions

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -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