summaryrefslogtreecommitdiff
path: root/py
diff options
context:
space:
mode:
authorYoctopuce dev <dev@yoctopuce.com>2025-05-20 15:57:11 +0200
committerDamien George <damien@micropython.org>2025-06-23 16:05:12 +1000
commit66c01480226aaf880a9d6e88340772bd53bc0421 (patch)
treeb9b27e2c3705cc44c8548c230e4e808fc578863a /py
parent35f15cfdf2a04a0ad290d70383fc3e4be4f79812 (diff)
py/runtime: Add support for using __all__ in star import.
When the symbol `__all__` is defined in a module, `mp_import_all()` should import all listed symbols into the global environment, rather than relying on the underscore-is-private default. This is the standard in CPython. Each item is loaded in the same way as if it would be an explicit import statement, and will invoke the module's `__getattr__` function if needed. This provides a straightforward solution for fixing star import of modules using a dynamic loader, such as `extmod/asyncio` (see issue #7266). This improvement has been enabled at BASIC_FEATURES level, to avoid impacting devices with limited ressources, for which star import is of little use anyway. Additionally, detailled reporting of errors during `__all__` import has been implemented to match CPython, but this is only enabled when ERROR_REPORTING is set to MICROPY_ERROR_REPORTING_DETAILED. Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
Diffstat (limited to 'py')
-rw-r--r--py/mpconfig.h5
-rw-r--r--py/runtime.c35
2 files changed, 39 insertions, 1 deletions
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 01712bd5b..94b845300 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -1323,6 +1323,11 @@ typedef double mp_float_t;
#define MICROPY_PY___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
#endif
+// Whether to process __all__ when importing all public symbols from module
+#ifndef MICROPY_MODULE___ALL__
+#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES)
+#endif
+
// Whether to provide mem-info related functions in micropython module
#ifndef MICROPY_PY_MICROPYTHON_MEM_INFO
#define MICROPY_PY_MICROPYTHON_MEM_INFO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
diff --git a/py/runtime.c b/py/runtime.c
index 7979e520d..90587a010 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1247,6 +1247,19 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
mp_raise_msg_varg(&mp_type_AttributeError,
MP_ERROR_TEXT("type object '%q' has no attribute '%q'"),
((mp_obj_type_t *)MP_OBJ_TO_PTR(base))->name, attr);
+ #if MICROPY_MODULE___ALL__ && MICROPY_ERROR_REPORTING >= MICROPY_ERROR_REPORTING_DETAILED
+ } else if (mp_obj_is_type(base, &mp_type_module)) {
+ // report errors in __all__ as done by CPython
+ mp_obj_t dest_name[2];
+ qstr module_name = MP_QSTR_;
+ mp_load_method_maybe(base, MP_QSTR___name__, dest_name);
+ if (mp_obj_is_qstr(dest_name[0])) {
+ module_name = mp_obj_str_get_qstr(dest_name[0]);
+ }
+ mp_raise_msg_varg(&mp_type_AttributeError,
+ MP_ERROR_TEXT("module '%q' has no attribute '%q'"),
+ module_name, attr);
+ #endif
} else {
mp_raise_msg_varg(&mp_type_AttributeError,
MP_ERROR_TEXT("'%s' object has no attribute '%q'"),
@@ -1593,8 +1606,28 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name) {
void mp_import_all(mp_obj_t module) {
DEBUG_printf("import all %p\n", module);
- // TODO: Support __all__
mp_map_t *map = &mp_obj_module_get_globals(module)->map;
+
+ #if MICROPY_MODULE___ALL__
+ mp_map_elem_t *elem = mp_map_lookup(map, MP_OBJ_NEW_QSTR(MP_QSTR___all__), MP_MAP_LOOKUP);
+ if (elem != NULL) {
+ // When __all__ is defined, we must explicitly load all specified
+ // symbols, possibly invoking the module __getattr__ function
+ size_t len;
+ mp_obj_t *items;
+ mp_obj_get_array(elem->value, &len, &items);
+ for (size_t i = 0; i < len; i++) {
+ qstr qname = mp_obj_str_get_qstr(items[i]);
+ mp_obj_t dest[2];
+ mp_load_method(module, qname, dest);
+ mp_store_name(qname, dest[0]);
+ }
+ return;
+ }
+ #endif
+
+ // By default, the set of public names includes all names found in the module's
+ // namespace which do not begin with an underscore character ('_')
for (size_t i = 0; i < map->alloc; i++) {
if (mp_map_slot_is_filled(map, i)) {
// Entry in module global scope may be generated programmatically