Skip to content

Commit

Permalink
py/objmodule: Don't use sys.modules to track a builtin __init__.
Browse files Browse the repository at this point in the history
This can lead to duplicate initialisations if a module can be imported
via multiple names, so the module must track this itself anyway.

This reduces code size (diff is -40 bytes), and avoids special treatment of
builtin-modules-with-init with respect to sys.modules. No other builtin
modules get put into sys.modules.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
  • Loading branch information
jimmo authored and dpgeorge committed Jun 1, 2023
1 parent ed90f30 commit 6a8114e
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 30 deletions.
4 changes: 3 additions & 1 deletion py/mpconfig.h
Expand Up @@ -845,7 +845,9 @@ typedef double mp_float_t;
#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
// Whether to call __init__ when importing builtin modules for the first time.
// Modules using this need to handle the possibility that __init__ might be
// called multiple times.
#ifndef MICROPY_MODULE_BUILTIN_INIT
#define MICROPY_MODULE_BUILTIN_INIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
#endif
Expand Down
38 changes: 9 additions & 29 deletions py/objmodule.c
Expand Up @@ -40,10 +40,6 @@
#include "genhdr/moduledefs.h"
#endif

#if MICROPY_MODULE_BUILTIN_INIT
STATIC void mp_module_call_init(mp_obj_t module_name, mp_obj_t module_obj);
#endif

STATIC void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in);
Expand Down Expand Up @@ -176,43 +172,27 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = {

MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table);

// Attempts to find (and initialise) a builtin, otherwise returns MP_OBJ_NULL.
// This must only be called after first checking the loaded modules,
// otherwise the module will be re-initialised.
// Attempts to find (and initialise) a builtin, otherwise returns
// MP_OBJ_NULL.
mp_obj_t mp_module_get_builtin(qstr module_name) {
mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP);
if (!elem) {
return MP_OBJ_NULL;
}

#if MICROPY_MODULE_BUILTIN_INIT
// If found, it's a newly loaded built-in, so init it.
mp_module_call_init(MP_OBJ_NEW_QSTR(module_name), elem->value);
#endif

return elem->value;
}

#if MICROPY_MODULE_BUILTIN_INIT
STATIC void mp_module_register(mp_obj_t module_name, 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, module_name, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = module;
}

STATIC void mp_module_call_init(mp_obj_t module_name, mp_obj_t module_obj) {
// Look for __init__ and call it if it exists
// If found, it's a newly loaded built-in, so init it. This can run
// multiple times, so the module must ensure that it handles being
// initialised multiple times.
mp_obj_t dest[2];
mp_load_method_maybe(module_obj, MP_QSTR___init__, dest);
mp_load_method_maybe(elem->value, 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

return elem->value;
}
#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) {
Expand Down

0 comments on commit 6a8114e

Please sign in to comment.