diff --git a/py/builtinimport.c b/py/builtinimport.c index 1a333b5404c03..14316e258593d 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -44,16 +44,38 @@ #define DEBUG_printf(...) (void)0 #endif -#if MICROPY_ENABLE_EXTERNAL_IMPORT - -#define PATH_SEP_CHAR '/' +STATIC mp_obj_t create_builtin(qstr module_name, mp_map_t *map) { + mp_map_elem_t *el = mp_map_lookup(map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); + if (el == NULL) { + return MP_OBJ_NULL; + } -bool mp_obj_is_package(mp_obj_t module) { +#if MICROPY_MODULE_BUILTIN_INIT + // Look for __init__ and call it if it exists mp_obj_t dest[2]; - mp_load_method_maybe(module, MP_QSTR___path__, dest); - return dest[0] != MP_OBJ_NULL; + mp_load_method_maybe(el->value, MP_QSTR___init__, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_call_method_n_kw(0, 0, dest); + } +#endif + + DEBUG_printf("Registering built-in module %s\n", qstr_str(module_name)); + + // Register module so __init__ is not called again. + // If a module can be referenced by more than one name (eg due to weak links) + // then __init__ will still be called for each distinct import, and it's then + // up to the particular module to make sure it's __init__ code only runs once. + mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; + mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = el->value; + + return el->value; } +#if MICROPY_ENABLE_EXTERNAL_IMPORT + +#define PATH_SEP_CHAR '/' +#define PATH_SEP_CHAR_STR "/" + // Stat either frozen or normal module by a given path // (whatever is available, if at all). STATIC mp_import_stat_t mp_import_stat_any(const char *path) { @@ -245,6 +267,11 @@ STATIC void chop_component(const char *start, const char **end) { *end = p; } +// // returns MP_OBJ_NULL if not found +// mp_obj_t mp_module_get(qstr module_name) { + +// } + mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #if DEBUG_PRINT DEBUG_printf("__import__:\n"); @@ -268,6 +295,8 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { } } + mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; + size_t mod_len; const char *mod_str = mp_obj_str_get_data(module_name, &mod_len); @@ -305,8 +334,8 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { const char *p = this_name + this_name_l; if (!is_pkg) { // We have module, but relative imports are anchored at package, so - // go there. - chop_component(this_name, &p); + // take off another level. + level++; } while (level--) { @@ -334,40 +363,34 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { mod_len = new_mod_l; } - // check if module already exists - qstr module_name_qstr = mp_obj_str_get_qstr(module_name); - mp_obj_t module_obj = mp_module_get(module_name_qstr); - if (module_obj != MP_OBJ_NULL) { - DEBUG_printf("Module already loaded\n"); - // If it's not a package, return module right away - char *p = strchr(mod_str, '.'); - if (p == NULL) { - return module_obj; - } - // If fromlist is not empty, return leaf module - if (fromtuple != mp_const_none) { - return module_obj; - } - // Otherwise, we need to return top-level package - qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); - return mp_module_get(pkg_name); - } - DEBUG_printf("Module not yet loaded\n"); - uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) - module_obj = MP_OBJ_NULL; - mp_obj_t top_module_obj = MP_OBJ_NULL; - mp_obj_t outer_module_obj = MP_OBJ_NULL; + + mp_obj_t result_obj = MP_OBJ_NULL; + #if !MICROPY_MODULE_BUILTIN_PACKAGES + mp_obj_t package_obj = MP_OBJ_NULL; + #endif uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); - DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path)); + DEBUG_printf("Previous path: =%.*s=\n", (int)vstr_len(&path), vstr_str(&path)); + + // see if this module is already loaded. + mp_obj_t module_obj; + mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); + if (el != NULL) { + DEBUG_printf("already loaded\n"); + module_obj = el->value; + } else { + // otherwise see if this is a built-in. + module_obj = create_builtin(mod_name, (mp_map_t*)&mp_builtin_module_map); + } - // find the file corresponding to the module name + // find the file corresponding to the module name. + // the module may already be loaded, but this ensures we get the right path. mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file @@ -378,40 +401,36 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } - DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); - - if (stat == MP_IMPORT_STAT_NO_EXIST) { - #if MICROPY_MODULE_WEAK_LINKS - // check if there is a weak link to this module - if (i == mod_len) { - mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); - if (el == NULL) { - goto no_exist; + DEBUG_printf("Current path: %.*s\n", (int)vstr_len(&path), vstr_str(&path)); + + if (module_obj == MP_OBJ_NULL) { + if (stat == MP_IMPORT_STAT_NO_EXIST) { + #if MICROPY_MODULE_WEAK_LINKS + // check if there is a weak link to this module + module_obj = create_builtin(mod_name, (mp_map_t*)&mp_builtin_module_weak_links_map); + if (module_obj == MP_OBJ_NULL) { + #else + { + #endif + // couldn't find the file, so fail + if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { + mp_raise_msg(&mp_type_ImportError, "module not found"); + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, + "no module named '%q'", mod_name)); + } } - // found weak linked module - module_obj = el->value; - mp_module_call_init(mod_name, module_obj); } else { - no_exist: - #else - { - #endif - // couldn't find the file, so fail - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - mp_raise_msg(&mp_type_ImportError, "module not found"); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, - "no module named '%q'", mod_name)); + // found the file, so get the module + el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); + if (el != NULL) { + module_obj = el->value; } } - } else { - // found the file, so get the module - module_obj = mp_module_get(mod_name); } if (module_obj == MP_OBJ_NULL) { - // module not already loaded, so load it! - + // file exists (and it wasn't a builtin) but it's not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); // if args[3] (fromtuple) has magic value False, set up @@ -434,13 +453,12 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { } if (stat == MP_IMPORT_STAT_DIR) { - DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); + DEBUG_printf("%.*s is dir\n", (int)vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path))); size_t orig_path_len = path.len; - vstr_add_char(&path, PATH_SEP_CHAR); - vstr_add_str(&path, "__init__.py"); + vstr_add_str(&path, PATH_SEP_CHAR_STR "__init__.py"); if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) { //mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { @@ -455,24 +473,27 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { // on the next iteration because the file will not exist. } } - if (outer_module_obj != MP_OBJ_NULL) { - qstr s = qstr_from_strn(mod_str + last, i - last); - mp_store_attr(outer_module_obj, s, module_obj); + + // If fromlist is not empty, return leaf module + // Otherwise, we need to return top-level package + if (result_obj == MP_OBJ_NULL || fromtuple != mp_const_none) { + result_obj = module_obj; } - outer_module_obj = module_obj; - if (top_module_obj == MP_OBJ_NULL) { - top_module_obj = module_obj; + + #if !MICROPY_MODULE_BUILTIN_PACKAGES + // If this is a submodule, store it as an attr on the package. + // Note: when builtin packages are enabled, objmodule.c:module_attr() will handle this instead. + if (package_obj != MP_OBJ_NULL) { + qstr s = qstr_from_strn(mod_str + last, i - last); + mp_store_attr(package_obj, s, module_obj); } + package_obj = module_obj; + #endif last = i + 1; } } - // If fromlist is not empty, return leaf module - if (fromtuple != mp_const_none) { - return module_obj; - } - // Otherwise, we need to return top-level package - return top_module_obj; + return result_obj; } #else // MICROPY_ENABLE_EXTERNAL_IMPORT @@ -484,19 +505,24 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { } // Check if module already exists, and return it if it does + mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; + mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, args[0], MP_MAP_LOOKUP); + if (el != NULL) { + return el->value; + } + + // Check if it's a builtin, and create & return if it is. qstr module_name_qstr = mp_obj_str_get_qstr(args[0]); - mp_obj_t module_obj = mp_module_get(module_name_qstr); + mp_obj_t module_obj = create_builtin(module_name_qstr, (mp_map_t*)&mp_builtin_module_map); if (module_obj != MP_OBJ_NULL) { return module_obj; } #if MICROPY_MODULE_WEAK_LINKS - // Check if there is a weak link to this module - mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(module_name_qstr), MP_MAP_LOOKUP); - if (el != NULL) { - // Found weak-linked module - mp_module_call_init(module_name_qstr, el->value); - return el->value; + // Check if it's a weak builtin, and create & return if it is. + module_obj = create_builtin(module_name_qstr, (mp_map_t*)&mp_builtin_module_weak_links_map); + if (module_obj != MP_OBJ_NULL) { + return module_obj; } #endif diff --git a/py/mpconfig.h b/py/mpconfig.h index 893ac7dc7eddb..da0ddecd67627 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -684,6 +684,12 @@ typedef double mp_float_t; #define MICROPY_MODULE_BUILTIN_INIT (0) #endif +// Whether to support built-in packages (e.g. foo.bar) +// (by default all built-ins must be top-level modules only) +#ifndef MICROPY_MODULE_BUILTIN_PACKAGES +#define MICROPY_MODULE_BUILTIN_PACKAGES (0) +#endif + // Whether to support module-level __getattr__ (see PEP 562) #ifndef MICROPY_MODULE_GETATTR #define MICROPY_MODULE_GETATTR (0) diff --git a/py/objmodule.c b/py/objmodule.c index 9191c73ec3138..a242a0e8f638e 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -25,6 +25,7 @@ */ #include +#include #include #include "py/objmodule.h" @@ -63,14 +64,44 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); if (elem != NULL) { dest[0] = elem->value; + #if MICROPY_MODULE_BUILTIN_PACKAGES + } else { + // Check if what they're looking for is a submodule. + + // Normally a package would have its submodule set as an attr, but as the globals dict + // is readonly for a built-in package, we need a way to find it. + // So search for any registered module that matches . + + // Get this package name (the name of the module that's being queried). + elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_MAP_LOOKUP); + size_t package_len; + const char *package_str = mp_obj_str_get_data(elem->value, &package_len); + + size_t attr_len; + const char* attr_str = (const char*)qstr_data(attr, &attr_len); + + // Search for a match. + mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; + for (size_t i = 0; i < mp_loaded_modules_map->alloc; i++) { + if (mp_map_slot_is_filled(mp_loaded_modules_map, i)) { + size_t mod_len; + const char *mod_str = mp_obj_str_get_data(mp_loaded_modules_map->table[i].key, &mod_len); + if (mod_len == package_len + 1 + attr_len && strncmp(package_str, mod_str, package_len) == 0 && mod_str[package_len] == '.' && strncmp(mod_str + package_len + 1, attr_str, attr_len) == 0) { + dest[0] = mp_loaded_modules_map->table[i].value; + } + } + } + #endif + } + #if MICROPY_MODULE_GETATTR - } else if (attr != MP_QSTR___getattr__) { + if (dest[0] == MP_OBJ_NULL && attr != MP_QSTR___getattr__) { 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)); } - #endif } + #endif } else { // delete/store attribute mp_obj_dict_t *dict = self->globals; @@ -124,6 +155,7 @@ mp_obj_t mp_obj_new_module(qstr module_name) { mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(module_name)); // store the new module into the slot in the global dict holding all modules + // this is a shortcut for mp_module_register(). el->value = MP_OBJ_FROM_PTR(o); // return the new module @@ -241,43 +273,3 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_weak_links_table[] = { MP_DEFINE_CONST_MAP(mp_builtin_module_weak_links_map, mp_builtin_module_weak_links_table); #endif - -// returns MP_OBJ_NULL if not found -mp_obj_t mp_module_get(qstr module_name) { - mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; - // lookup module - mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); - - if (el == NULL) { - // module not found, look for builtin module names - el = mp_map_lookup((mp_map_t*)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); - if (el == NULL) { - return MP_OBJ_NULL; - } - mp_module_call_init(module_name, el->value); - } - - // module found, return it - return el->value; -} - -void mp_module_register(qstr qst, mp_obj_t module) { - mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; - mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = module; -} - -#if MICROPY_MODULE_BUILTIN_INIT -void mp_module_call_init(qstr module_name, mp_obj_t module_obj) { - // Look for __init__ and call it if it exists - mp_obj_t dest[2]; - mp_load_method_maybe(module_obj, MP_QSTR___init__, dest); - if (dest[0] != MP_OBJ_NULL) { - mp_call_method_n_kw(0, 0, dest); - // Register module so __init__ is not called again. - // If a module can be referenced by more than one name (eg due to weak links) - // then __init__ will still be called for each distinct import, and it's then - // up to the particular module to make sure it's __init__ code only runs once. - mp_module_register(module_name, module_obj); - } -} -#endif diff --git a/py/objmodule.h b/py/objmodule.h index b7702ec50b686..8624850633bc5 100644 --- a/py/objmodule.h +++ b/py/objmodule.h @@ -31,16 +31,4 @@ extern const mp_map_t mp_builtin_module_map; extern const mp_map_t mp_builtin_module_weak_links_map; -mp_obj_t mp_module_get(qstr module_name); -void mp_module_register(qstr qstr, mp_obj_t module); - -#if MICROPY_MODULE_BUILTIN_INIT -void mp_module_call_init(qstr module_name, mp_obj_t module_obj); -#else -static inline void mp_module_call_init(qstr module_name, mp_obj_t module_obj) { - (void)module_name; - (void)module_obj; -} -#endif - #endif // MICROPY_INCLUDED_PY_OBJMODULE_H diff --git a/py/runtime.c b/py/runtime.c index a3628eecb45a2..6caf3d8584db9 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1340,6 +1340,12 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { return mp_builtin___import__(5, args); } +bool mp_obj_is_package(mp_obj_t module) { + mp_obj_t dest[2]; + mp_load_method_maybe(module, MP_QSTR___path__, dest); + return dest[0] != MP_OBJ_NULL; +} + mp_obj_t mp_import_from(mp_obj_t module, qstr name) { DEBUG_printf("import from %p %s\n", module, qstr_str(name)); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy