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),则尝试在冻结模块文件名列表中找到请求的文件名。
#