microPython的源码解析之 builtinimport.c

MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计.

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "py/compile.h" // 编译相关头文件
#include "py/objmodule.h" // 模块对象定义
#include "py/persistentcode.h" // 持久化代码支持
#include "py/runtime.h" // MicroPython 运行时环境
#include "py/builtin.h" // 内置函数和模块
#include "py/frozenmod.h" // 冻结模块支持

#if MICROPY_DEBUG_VERBOSE // 打印调试信息
#define DEBUG_PRINT (1)
#define DEBUG_printf DEBUG_printf
#else // 不打印调试信息
#define DEBUG_PRINT (0)
#define DEBUG_printf(...) (void)0
#endif

#if MICROPY_ENABLE_EXTERNAL_IMPORT

// 必须是一个字节长的字符串。
#define PATH_SEP_CHAR "/"

// 映射到冻结模块的虚拟 sys.path 条目前缀。
#define MP_FROZEN_PATH_PREFIX ".frozen/"

// 包装 mp_import_stat(由端口提供,通常使用 mp_vfs_import_stat)也搜索冻结模块。给定文件或目录的确切路径(例如 "foo/bar"、"foo/bar.py" 或 "foo/bar.mpy"),
// 将返回路径是文件、目录还是不存在。
static mp_import_stat_t stat_path(vstr_t *path) {
   
    const char *str = vstr_null_terminated_str(path);
    #if MICROPY_MODULE_FROZEN
    // 只有当它以 .frozen/ 开头时才尝试将其作为冻结模块加载。
    const int frozen_path_prefix_len = strlen(MP_FROZEN_PATH_PREFIX);
    if (strncmp(str, MP_FROZEN_PATH_PREFIX, frozen_path_prefix_len) == 0) {
   
        // 只统计(即返回值),不获取数据。
        return mp_find_frozen_module(str + frozen_path_prefix_len, NULL, NULL);
    }
    #endif
    return mp_import_stat(str);
}

// 给定一个文件系统路径到 .py 文件。如果文件不存在,
// 则尝试统计相应的 .mpy 文件,并更新路径参数。
// 这是让 .py 文件优先于 .mpy 文件的逻辑。
// 使用 stat_path 而不是直接使用 mp_import_stat,以便处理 .frozen 路径前缀。
static mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) {
   
    mp_import_stat_t stat = stat_path(path);
    if (stat == MP_IMPORT_STAT_FILE) {
   
        return stat;
    }

    #if MICROPY_PERSISTENT_CODE_LOAD
    // 没找到 .py -- 尝试通过在 '.py' 中插入一个 'm' 来尝试 .mpy。
    // 注意:如果是冻结路径,没有意义进行检查,但添加检查将是额外的代码,并且没有伤害让 mp_find_frozen_module 失败。
    vstr_ins_byte(path, path->len - 2, 'm');
    stat = stat_path(path);
    if (stat == MP_IMPORT_STAT_FILE) {
   
        return stat;
    }
    #endif

    return MP_IMPORT_STAT_NO_EXIST;
}

// 给定一个顶级模块名称,尝试在每个 sys.path 条目中找到 "foo/bar"(一个目录)
// 或 "foo/bar.(m)py",无论是在文件系统还是冻结模块中。如果结果是一个文件,路径参数将被更新以包括文件扩展名。
static mp_import_stat_t stat_module(vstr_t *path) {
   
    mp_import_stat_t stat = stat_path(path);
    DEBUG_printf("stat %s: %d\n", vstr_str(path), stat);
    if (stat == MP_IMPORT_STAT_DIR) {
   
        return stat;
    }

    // 不是目录,添加 .py 并尝试作为文件。
    vstr_add_str(path, ".py");
    return stat_file_py_or_mpy(path);
}

// 给定一个顶级模块名称,尝试在每个 sys.path 条目中找到它。
// 注意:成功时,dest 参数将被更新为匹配的路径(即 "<entry>/mod_name(.py)")。
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;
    mp_obj_get_array(mp_sys_path, &path_num, &path_items);

    // 遍历每个 sys.path 条目,尝试导入 "<entry>/<mod_name>"。
    for (size_t i = 0; i < path_num; i++) {
   
        vstr_reset(dest);
        size_t p_len;
        const char *p = mp_obj_str_get_data(path_items[i], &p_len);
        if (p_len > 0) {
   
            // 添加路径分隔符(除非条目是 "", 即当前工作目录)。
            vstr_add_strn(dest, p, p_len);
            vstr_add_char(dest, PATH_SEP_CHAR[0]);
        }
        vstr_add_str(dest, qstr_str(mod_name));
        mp_import_stat_t stat = stat_module(dest);
        if (stat != MP_IMPORT_STAT_NO_EXIST) {
   
            return stat;
        }
    }

    // sys.path 为空或没有匹配项,不搜索文件系统或冻结代码。
    return MP_IMPORT_STAT_NO_EXIST;

    #else

    // mp_sys_path 未启用,因此直接统计给定路径。
    vstr_add_str(dest, qstr_str(mod_name));
    return stat_module(dest);

    #endif
}

#if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER
static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) {
   
    #if MICROPY_PY___FILE__
    qstr source_name = lex->source_name;
    mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
    #endif

    // 解析、编译并执行模块在其上下文中
    mp_obj_dict_t *mod_globals = context->module.globals;
    mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals);
}
#endif

#if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY
static void do_execute_proto_fun(const mp_module_context_t *context, mp_proto_fun_t proto_fun, qstr source_name) {
   
    #if MICROPY_PY___FILE__
    mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
    #else
    (void)source_name;
    #endif

    // 在其上下文中执行模块
    mp_obj_dict_t *mod_globals = context->module.globals;

    // 保存上下文
    nlr_jump_callback_node_globals_locals_t ctx;
    ctx.globals = mp_globals_get();
    ctx.locals = mp_locals_get();

    // 设置新上下文
    mp_globals_set(mod_globals);
    mp_locals_set(mod_globals);

    // 设置异常处理程序以在引发异常时恢复上下文
    nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback);

    // 制作并执行函数
    mp_obj_t module_fun = mp_make_function_from_proto_fun(proto_fun, context, NULL);
    mp_call_function_0(module_fun);

    // 注销异常处理程序并恢复上下文
    nlr_pop_jump_callback(true);
}
#endif

static void do_load(mp_module_context_t *module_obj, vstr_t *file) {
   
    #if MICROPY_MODULE_FROZEN || MICROPY_ENABLE_COMPILER || (MICROPY_PERSISTENT_CODE_LOAD && MICROPY_HAS_FILE_READER)
    const char *file_str = vstr_null_terminated_str(file);
    #endif

    // 如果我们支持冻结模块(无论是作为 str 还是 mpy),则尝试在冻结模块文件名列表中找到请求的文件名。
    #
  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值