@ -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_fi le ( dest ) ;
mp_import_stat_t stat = stat_modu le ( 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_fi le ( dest ) ;
return stat_modu le ( 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 modu le (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 ( leve l_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 ) {
// No 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
}
// 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