summaryrefslogtreecommitdiff
path: root/py
diff options
context:
space:
mode:
Diffstat (limited to 'py')
-rw-r--r--py/builtinimport.c307
-rw-r--r--py/obj.h2
-rw-r--r--py/objmodule.c32
-rw-r--r--py/objmodule.h3
-rw-r--r--py/runtime.c3
5 files changed, 184 insertions, 163 deletions
diff --git a/py/builtinimport.c b/py/builtinimport.c
index 025fff928..d8127a66f 100644
--- a/py/builtinimport.c
+++ b/py/builtinimport.c
@@ -62,39 +62,39 @@ STATIC qstr make_weak_link_name(vstr_t *buffer, qstr name) {
// Virtual sys.path entry that maps to the frozen modules.
#define MP_FROZEN_PATH_PREFIX ".frozen/"
-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;
-}
-
// Wrapper for mp_import_stat (which is provided by the port, and typically
// uses mp_vfs_import_stat) to also search frozen modules. Given an exact
// path to a file or directory (e.g. "foo/bar", foo/bar.py" or "foo/bar.mpy"),
// will return whether the path is a file, directory, or doesn't exist.
-STATIC mp_import_stat_t stat_path_or_frozen(const char *path) {
+STATIC mp_import_stat_t stat_path(const char *path) {
#if MICROPY_MODULE_FROZEN
// Only try and load as a frozen module if it starts with .frozen/.
const int frozen_path_prefix_len = strlen(MP_FROZEN_PATH_PREFIX);
if (strncmp(path, MP_FROZEN_PATH_PREFIX, frozen_path_prefix_len) == 0) {
+ // Just stat (which is the return value), don't get the data.
return mp_find_frozen_module(path + frozen_path_prefix_len, NULL, NULL);
}
#endif
return mp_import_stat(path);
}
-// Given a path to a .py file, try and find this path as either a .py or .mpy
-// in either the filesystem or frozen modules.
+// Stat a given filesystem path to a .py file. If the file does not exist,
+// then attempt to stat the corresponding .mpy file, and update the path
+// argument. This is the logic that makes .py files take precedent over .mpy
+// files. This uses stat_path above, rather than mp_import_stat directly, so
+// that the .frozen path prefix is handled.
STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) {
- mp_import_stat_t stat = stat_path_or_frozen(vstr_null_terminated_str(path));
+ mp_import_stat_t stat = stat_path(vstr_null_terminated_str(path));
if (stat == MP_IMPORT_STAT_FILE) {
return stat;
}
#if MICROPY_PERSISTENT_CODE_LOAD
// Didn't find .py -- try the .mpy instead by inserting an 'm' into the '.py'.
+ // Note: There's no point doing this if it's a frozen path, but adding the check
+ // would be extra code, and no harm letting mp_find_frozen_module fail instead.
vstr_ins_byte(path, path->len - 2, 'm');
- stat = stat_path_or_frozen(vstr_null_terminated_str(path));
+ stat = stat_path(vstr_null_terminated_str(path));
if (stat == MP_IMPORT_STAT_FILE) {
return stat;
}
@@ -104,15 +104,17 @@ STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) {
}
// Given an import path (e.g. "foo/bar"), try and find "foo/bar" (a directory)
-// or "foo/bar.(m)py" in either the filesystem or frozen modules.
-STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
- mp_import_stat_t stat = stat_path_or_frozen(vstr_null_terminated_str(path));
+// or "foo/bar.(m)py" in either the filesystem or frozen modules. If the
+// result is a file, the path argument will be updated to include the file
+// extension.
+STATIC mp_import_stat_t stat_module(vstr_t *path) {
+ mp_import_stat_t stat = stat_path(vstr_null_terminated_str(path));
DEBUG_printf("stat %s: %d\n", vstr_str(path), stat);
if (stat == MP_IMPORT_STAT_DIR) {
return stat;
}
- // not a directory, add .py and try as a file
+ // Not a directory, add .py and try as a file.
vstr_add_str(path, ".py");
return stat_file_py_or_mpy(path);
}
@@ -120,8 +122,8 @@ STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
// Given a top-level module name, try and find it in each of the sys.path
// entries. Note: On success, the dest argument will be updated to the matching
// path (i.e. "<entry>/mod_name(.py)").
-STATIC mp_import_stat_t stat_top_level_dir_or_file(qstr mod_name, vstr_t *dest) {
- DEBUG_printf("stat_top_level_dir_or_file: '%s'\n", qstr_str(mod_name));
+STATIC mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) {
+ DEBUG_printf("stat_top_level: '%s'\n", qstr_str(mod_name));
#if MICROPY_PY_SYS
size_t path_num;
mp_obj_t *path_items;
@@ -138,7 +140,7 @@ STATIC mp_import_stat_t stat_top_level_dir_or_file(qstr mod_name, vstr_t *dest)
vstr_add_char(dest, PATH_SEP_CHAR[0]);
}
vstr_add_str(dest, qstr_str(mod_name));
- mp_import_stat_t stat = stat_dir_or_file(dest);
+ mp_import_stat_t stat = stat_module(dest);
if (stat != MP_IMPORT_STAT_NO_EXIST) {
return stat;
}
@@ -152,7 +154,7 @@ STATIC mp_import_stat_t stat_top_level_dir_or_file(qstr mod_name, vstr_t *dest)
// mp_sys_path is not enabled, so just stat the given path directly.
vstr_add_str(dest, qstr_str(mod_name));
- return stat_dir_or_file(dest);
+ return stat_module(dest);
#endif
}
@@ -350,128 +352,168 @@ STATIC void evaluate_relative_import(mp_int_t level, const char **module_name, s
}
// Load a module at the specified absolute path, possibly as a submodule of the given outer module.
-// full_mod_name: The full absolute path to this module (e.g. "foo.bar.baz").
+// full_mod_name: The full absolute path up to this level (e.g. "foo.bar.baz").
// level_mod_name: The final component of the path (e.g. "baz").
// outer_module_obj: The parent module (we need to store this module as an
// attribute on it) (or MP_OBJ_NULL for top-level).
-// path: The filesystem path where we found the parent module
-// (or empty for a top level module).
// override_main: Whether to set the __name__ to "__main__" (and use __main__
// for the actual path).
-STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, mp_obj_t outer_module_obj, vstr_t *path, bool override_main) {
- mp_import_stat_t stat = MP_IMPORT_STAT_NO_EXIST;
+STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, mp_obj_t outer_module_obj, bool override_main) {
+ // Immediately return if the module at this level is already loaded.
+ mp_map_elem_t *elem;
- // Exact-match of built-in (or already-loaded) takes priority.
- mp_obj_t module_obj = mp_module_get_loaded_or_builtin(full_mod_name);
+ #if MICROPY_PY_SYS
+ // If sys.path is empty, the intention is to force using a built-in. This
+ // means we should also ignore any loaded modules with the same name
+ // which may have come from the filesystem.
+ size_t path_num;
+ mp_obj_t *path_items;
+ mp_obj_list_get(mp_sys_path, &path_num, &path_items);
+ if (path_num)
+ #endif
+ {
+ elem = mp_map_lookup(&MP_STATE_VM(mp_loaded_modules_dict).map, MP_OBJ_NEW_QSTR(full_mod_name), MP_MAP_LOOKUP);
+ if (elem) {
+ return elem->value;
+ }
+ }
- // Even if we find the module, go through the motions of searching for it
- // because we may actually be in the process of importing a sub-module.
- // So we need to (re-)find the correct path to be finding the sub-module
- // on the next iteration of process_import_at_level.
+ VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX);
+ mp_import_stat_t stat = MP_IMPORT_STAT_NO_EXIST;
+ mp_obj_t module_obj;
if (outer_module_obj == MP_OBJ_NULL) {
DEBUG_printf("Searching for top-level module\n");
+ // An exact match of a built-in will always bypass the filesystem.
+ // Note that CPython-compatible built-ins are named e.g. utime, so this
+ // means that an exact match is only for `import utime`, so `import
+ // time` will search the filesystem and failing that hit the weak
+ // link handling below. Whereas micropython-specific built-ins like
+ // `micropython`, `pyb`, `network`, etc will match exactly and cannot
+ // be overridden by the filesystem.
+ module_obj = mp_module_get_builtin(level_mod_name);
+ if (module_obj != MP_OBJ_NULL) {
+ return module_obj;
+ }
+
+ #if MICROPY_PY_SYS
+ // Never allow sys to be overridden from the filesystem. If weak links
+ // are disabled, then this also provides a default weak link so that
+ // `import sys` is treated like `import usys` (and therefore bypasses
+ // the filesystem).
+ if (level_mod_name == MP_QSTR_sys) {
+ return MP_OBJ_FROM_PTR(&mp_module_sys);
+ }
+ #endif
+
// First module in the dotted-name; search for a directory or file
// relative to all the locations in sys.path.
- stat = stat_top_level_dir_or_file(full_mod_name, path);
+ stat = stat_top_level(level_mod_name, &path);
- // If the module "foo" doesn't exist on the filesystem, and it's not a
- // builtin, try and find "ufoo" as a built-in. (This feature was
- // formerly known as "weak links").
#if MICROPY_MODULE_WEAK_LINKS
- if (stat == MP_IMPORT_STAT_NO_EXIST && module_obj == MP_OBJ_NULL) {
- qstr umodule_name = make_weak_link_name(path, level_mod_name);
+ if (stat == MP_IMPORT_STAT_NO_EXIST) {
+ // No match on the filesystem. (And not a built-in either).
+ // If "foo" was requested, then try "ufoo" as a built-in. This
+ // allows `import time` to use built-in `utime`, unless `time`
+ // exists on the filesystem. This feature was formerly known
+ // as "weak links".
+ qstr umodule_name = make_weak_link_name(&path, level_mod_name);
module_obj = mp_module_get_builtin(umodule_name);
- }
- #elif MICROPY_PY_SYS
- if (stat == MP_IMPORT_STAT_NO_EXIST && module_obj == MP_OBJ_NULL && level_mod_name == MP_QSTR_sys) {
- module_obj = MP_OBJ_FROM_PTR(&mp_module_sys);
+ if (module_obj != MP_OBJ_NULL) {
+ return module_obj;
+ }
}
#endif
} else {
DEBUG_printf("Searching for sub-module\n");
- // Add the current part of the module name to the path.
- vstr_add_char(path, PATH_SEP_CHAR[0]);
- vstr_add_str(path, qstr_str(level_mod_name));
+ // If the outer module is a package, it will have __path__ set.
+ // We can use that as the path to search inside.
+ mp_obj_t dest[2];
+ mp_load_method_maybe(outer_module_obj, MP_QSTR___path__, dest);
+ if (dest[0] != MP_OBJ_NULL) {
+ // e.g. __path__ will be "<matched search path>/foo/bar"
+ vstr_add_str(&path, mp_obj_str_get_str(dest[0]));
+
+ // Add the level module name to the path to get "<matched search path>/foo/bar/baz".
+ vstr_add_char(&path, PATH_SEP_CHAR[0]);
+ vstr_add_str(&path, qstr_str(level_mod_name));
- // Because it's not top level, we already know which path the parent was found in.
- stat = stat_dir_or_file(path);
+ stat = stat_module(&path);
+ }
}
- DEBUG_printf("Current path: %.*s\n", (int)vstr_len(path), vstr_str(path));
- if (module_obj == MP_OBJ_NULL) {
- // Not a built-in and not already-loaded.
+ // Not already loaded, and not a built-in, so look at the stat result from the filesystem/frozen.
- if (stat == MP_IMPORT_STAT_NO_EXIST) {
- // And the file wasn't found -- fail.
- #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
- mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found"));
- #else
- mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), full_mod_name);
- #endif
- }
+ if (stat == MP_IMPORT_STAT_NO_EXIST) {
+ // Not found -- fail.
+ #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
+ mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found"));
+ #else
+ mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), full_mod_name);
+ #endif
+ }
- // Not a built-in but found on the filesystem, try and load it.
-
- DEBUG_printf("Found path: %.*s\n", (int)vstr_len(path), vstr_str(path));
-
- // Prepare for loading from the filesystem. Create a new shell module.
- module_obj = mp_obj_new_module(full_mod_name);
-
- #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
- // If this module is being loaded via -m on unix, then
- // override __name__ to "__main__". Do this only for *modules*
- // however - packages never have their names replaced, instead
- // they're -m'ed using a special __main__ submodule in them. (This all
- // apparently is done to not touch the package name itself, which is
- // important for future imports).
- if (override_main && stat != MP_IMPORT_STAT_DIR) {
- mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj);
- mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
- #if MICROPY_CPYTHON_COMPAT
- // Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules).
- mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj);
- // Store real name in "__main__" attribute. Need this for
- // resolving relative imports later. "__main__ was chosen
- // semi-randonly, to reuse existing qstr's.
- mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(full_mod_name));
- #endif
- }
- #endif // MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
-
- if (stat == MP_IMPORT_STAT_DIR) {
- // Directory -- execute "path/__init__.py".
- DEBUG_printf("%.*s is dir\n", (int)vstr_len(path), vstr_str(path));
- // Store the __path__ attribute onto this module.
- // 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_str(path, PATH_SEP_CHAR "__init__.py");
- if (stat_file_py_or_mpy(path) == MP_IMPORT_STAT_FILE) {
- do_load(MP_OBJ_TO_PTR(module_obj), path);
- } else {
- // No-op. Nothing to load.
- // mp_warning("%s is imported as namespace package", vstr_str(&path));
- }
- // Remove /__init__.py suffix.
- path->len = orig_path_len;
- } else { // MP_IMPORT_STAT_FILE
- // File -- execute "path.(m)py".
- do_load(MP_OBJ_TO_PTR(module_obj), path);
- // Note: This should be the last component in the import path. If
- // there are remaining components then it's an ImportError
- // because the current path(the module that was just loaded) is
- // not a package. This will be caught on the next iteration
- // because the file will not exist.
+ // Module was found on the filesystem/frozen, try and load it.
+ DEBUG_printf("Found path to load: %.*s\n", (int)vstr_len(&path), vstr_str(&path));
+
+ // Prepare for loading from the filesystem. Create a new shell module.
+ module_obj = mp_obj_new_module(full_mod_name);
+
+ #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
+ // If this module is being loaded via -m on unix, then
+ // override __name__ to "__main__". Do this only for *modules*
+ // however - packages never have their names replaced, instead
+ // they're -m'ed using a special __main__ submodule in them. (This all
+ // apparently is done to not touch the package name itself, which is
+ // important for future imports).
+ if (override_main && stat != MP_IMPORT_STAT_DIR) {
+ mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj);
+ mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
+ #if MICROPY_CPYTHON_COMPAT
+ // Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules).
+ mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj);
+ // Store real name in "__main__" attribute. Need this for
+ // resolving relative imports later. "__main__ was chosen
+ // semi-randonly, to reuse existing qstr's.
+ mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(full_mod_name));
+ #endif
+ }
+ #endif // MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
+
+ if (stat == MP_IMPORT_STAT_DIR) {
+ // Directory (i.e. a package).
+ DEBUG_printf("%.*s is dir\n", (int)vstr_len(&path), vstr_str(&path));
+
+ // Store the __path__ attribute onto this module.
+ // https://docs.python.org/3/reference/import.html
+ // "Specifically, any module that contains a __path__ attribute is considered a package."
+ // This gets used later to locate any subpackages of this module.
+ 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_str(&path, PATH_SEP_CHAR "__init__.py");
+
+ // execute "path/__init__.py" (if available).
+ if (stat_file_py_or_mpy(&path) == MP_IMPORT_STAT_FILE) {
+ do_load(MP_OBJ_TO_PTR(module_obj), &path);
+ } else {
+ // No-op. Nothing to load.
+ // mp_warning("%s is imported as namespace package", vstr_str(&path));
}
+ // Remove /__init__.py suffix from path.
+ path.len = orig_path_len;
+ } else { // MP_IMPORT_STAT_FILE
+ // File -- execute "path.(m)py".
+ do_load(MP_OBJ_TO_PTR(module_obj), &path);
+ // Note: This should be the last component in the import path. If
+ // there are remaining components then in the next call to
+ // process_import_at_level will detect that it doesn't have
+ // a __path__ attribute, and not attempt to stat it.
}
if (outer_module_obj != MP_OBJ_NULL) {
- // If it's a sub-module (not a built-in one), then make it available on
- // the parent module.
+ // If it's a sub-module then make it available on the parent module.
mp_store_attr(outer_module_obj, level_mod_name, module_obj);
}
@@ -504,7 +546,6 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
// i.e. "from . import foo" --> level=1
// i.e. "from ...foo.bar import baz" --> level=3
mp_int_t level = 0;
-
if (n_args >= 4) {
fromtuple = args[3];
if (n_args >= 5) {
@@ -519,8 +560,10 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
const char *module_name = mp_obj_str_get_data(module_name_obj, &module_name_len);
if (level != 0) {
- // Turn "foo.bar" into "<current module minus 3 components>.foo.bar".
+ // Turn "foo.bar" with level=3 into "<current module 3 components>.foo.bar".
+ // Current module name is extracted from globals().__name__.
evaluate_relative_import(level, &module_name, &module_name_len);
+ // module_name is now an absolute module path.
}
if (module_name_len == 0) {
@@ -529,11 +572,12 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
DEBUG_printf("Starting module search for '%s'\n", module_name);
- VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX)
mp_obj_t top_module_obj = MP_OBJ_NULL;
mp_obj_t outer_module_obj = MP_OBJ_NULL;
- // Search for the end of each component.
+ // Iterate the absolute path, finding the end of each component of the path.
+ // foo.bar.baz
+ // ^ ^ ^
size_t current_component_start = 0;
for (size_t i = 1; i <= module_name_len; i++) {
if (i == module_name_len || module_name[i] == '.') {
@@ -543,18 +587,18 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
qstr level_mod_name = qstr_from_strn(module_name + current_component_start, i - current_component_start);
DEBUG_printf("Processing module: '%s' at level '%s'\n", qstr_str(full_mod_name), qstr_str(level_mod_name));
- DEBUG_printf("Previous path: =%.*s=\n", (int)vstr_len(&path), vstr_str(&path));
#if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
- // On unix, if this is being loaded via -m (magic mp_const_false),
- // then handle that if it's the final component.
+ // On unix, if this is being loaded via -m (indicated by sentinel
+ // fromtuple=mp_const_false), then handle that if it's the final
+ // component.
bool override_main = (i == module_name_len && fromtuple == mp_const_false);
#else
bool override_main = false;
#endif
// Import this module.
- mp_obj_t module_obj = process_import_at_level(full_mod_name, level_mod_name, outer_module_obj, &path, override_main);
+ mp_obj_t module_obj = process_import_at_level(full_mod_name, level_mod_name, outer_module_obj, override_main);
// Set this as the parent module, and remember the top-level module if it's the first.
outer_module_obj = module_obj;
@@ -577,31 +621,38 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
#else // MICROPY_ENABLE_EXTERNAL_IMPORT
-bool mp_obj_is_package(mp_obj_t module) {
- return false;
-}
-
mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
- // Check that it's not a relative import
+ // Check that it's not a relative import.
if (n_args >= 5 && MP_OBJ_SMALL_INT_VALUE(args[4]) != 0) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("relative import"));
}
- // Check if module already exists, and return it if it does
+ // Check if the module is already loaded.
+ mp_map_elem_t *elem = mp_map_lookup(&MP_STATE_VM(mp_loaded_modules_dict).map, args[0], MP_MAP_LOOKUP);
+ if (elem) {
+ return elem->value;
+ }
+
+ // Try the name directly as a built-in.
qstr module_name_qstr = mp_obj_str_get_qstr(args[0]);
- mp_obj_t module_obj = mp_module_get_loaded_or_builtin(module_name_qstr);
+ mp_obj_t module_obj = mp_module_get_builtin(module_name_qstr);
if (module_obj != MP_OBJ_NULL) {
return module_obj;
}
#if MICROPY_MODULE_WEAK_LINKS
- // Check if there is a weak link to this module
+ // Check if the u-prefixed name is a built-in.
VSTR_FIXED(umodule_path, MICROPY_ALLOC_PATH_MAX);
qstr umodule_name_qstr = make_weak_link_name(&umodule_path, module_name_qstr);
- module_obj = mp_module_get_loaded_or_builtin(umodule_name_qstr);
+ module_obj = mp_module_get_builtin(umodule_name_qstr);
if (module_obj != MP_OBJ_NULL) {
return module_obj;
}
+ #elif MICROPY_PY_SYS
+ // Special handling to make `import sys` work even if weak links aren't enabled.
+ if (module_name_qstr == MP_QSTR_sys) {
+ return MP_OBJ_FROM_PTR(&mp_module_sys);
+ }
#endif
// Couldn't find the module, so fail
diff --git a/py/obj.h b/py/obj.h
index 04c153f50..3735b72c2 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -1201,8 +1201,6 @@ typedef struct _mp_obj_module_t {
static inline mp_obj_dict_t *mp_obj_module_get_globals(mp_obj_t module) {
return ((mp_obj_module_t *)MP_OBJ_TO_PTR(module))->globals;
}
-// check if given module object is a package
-bool mp_obj_is_package(mp_obj_t module);
// staticmethod and classmethod types; defined here so we can make const versions
// this structure is used for instances of both staticmethod and classmethod
diff --git a/py/objmodule.c b/py/objmodule.c
index 63e9fa475..6f061a940 100644
--- a/py/objmodule.c
+++ b/py/objmodule.c
@@ -176,35 +176,10 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = {
MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table);
-// Tries to find a loaded module, otherwise attempts to load a builtin, otherwise MP_OBJ_NULL.
-mp_obj_t mp_module_get_loaded_or_builtin(qstr module_name) {
- // First try loaded modules.
- mp_map_elem_t *elem = mp_map_lookup(&MP_STATE_VM(mp_loaded_modules_dict).map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP);
-
- if (!elem) {
- #if MICROPY_MODULE_WEAK_LINKS
- return mp_module_get_builtin(module_name);
- #else
- // Otherwise try builtin.
- 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
- #endif
- }
-
- return elem->value;
-}
-
-#if MICROPY_MODULE_WEAK_LINKS
-// Tries to find a loaded module, otherwise attempts to load a builtin, otherwise MP_OBJ_NULL.
+// 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.
mp_obj_t mp_module_get_builtin(qstr module_name) {
- // Try builtin.
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;
@@ -217,7 +192,6 @@ mp_obj_t mp_module_get_builtin(qstr module_name) {
return elem->value;
}
-#endif
#if MICROPY_MODULE_BUILTIN_INIT
STATIC void mp_module_register(mp_obj_t module_name, mp_obj_t module) {
diff --git a/py/objmodule.h b/py/objmodule.h
index d11d5bcd7..11bd7bb42 100644
--- a/py/objmodule.h
+++ b/py/objmodule.h
@@ -33,10 +33,7 @@
extern const mp_map_t mp_builtin_module_map;
-mp_obj_t mp_module_get_loaded_or_builtin(qstr module_name);
-#if MICROPY_MODULE_WEAK_LINKS
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);
diff --git a/py/runtime.c b/py/runtime.c
index 99f7f6109..cbc7fb931 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1531,7 +1531,8 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name) {
#if MICROPY_ENABLE_EXTERNAL_IMPORT
// See if it's a package, then can try FS import
- if (!mp_obj_is_package(module)) {
+ mp_load_method_maybe(module, MP_QSTR___path__, dest);
+ if (dest[0] == MP_OBJ_NULL) {
goto import_error;
}