Skip to content

FET-CN/mPyMacro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mPyMacro

MicroPython 条件编译组件。提取自 pyrite-cli 的条件编译功能,已解除对 pyrite 内部模块的依赖,可独立安装、独立测试、被任意工具复用。

@feature/@target 装饰器与 with feature()/target() 上下文管理器在源码级重写为 if True/False 块,配合 manifest 文件级过滤与标签解析,实现「一份源码、按目标板/特性裁剪后再刷入」。

┌──────────────┐   preprocess (libcst)   ┌──────────────────┐   mpy-cross    ┌─────────────┐
│ 带宏的源码    │ ──────────────────────→ │ 展开为 if True/   │ ─────────────→ │ 精简后的     │
│ feature/target│                         │ False,死代码消除 │   死代码消除    │ 字节码       │
└──────────────┘                         └──────────────────┘                └─────────────┘

安装

pip install -e ".[dev]"

唯一运行时依赖:libcst>=1.1.0(Python 3.10 额外需要 tomli 以读取 pyproject.toml)。

公共 API

from mpymacro import (
    preprocess,          # 源码级转换
    load_manifest,       # manifest 文件级过滤
    build_project,       # 按 manifest 离线裁剪整个项目到输出目录
    resolve_active_tags, # target/feature/no_feature 合并为最终标签集
    resolve_board_tags,  # 设备 machine/platform 字符串 → 板级标签
    load_board_tags,     # 读 pyproject.toml 的 board_tags(与 pyrite-cli 兼容)
    DEFAULT_BOARD_TAGS,  # 默认板级标签表
)

preprocess(source, active_tags, filename="") -> str

对单个 .py 源码执行条件编译转换。active_tagsset[str];传 None 时保留全部块(不做裁剪)。

src = '@feature("wifi")\ndef connect():\n    import network\n'
preprocess(src, {"wifi"})   # 保留 connect(),去掉装饰器
preprocess(src, {"ble"})    # 不匹配 → 包进 if False:

转换规则:

  • with feature("x"): / with target("X"):if True:(命中)或 if False:(未命中)
  • @feature("x") / @target("X") 装饰的函数 → 命中则去掉装饰器保留函数;未命中则整体包进 if False:
  • 同时做静态分析:被裁掉的函数若存在未被 guard 保护的「裸调用」,发出 warning(运行时会 NameError)

load_manifest(manifest_path, active_tags, base_dir=None) -> list[(local, remote)]

安全解析 manifest.py(AST 解析,非 exec),仅允许 module() / package() 字面量调用,支持 remote=(路径重映射)与 features=[...](标签过滤)。

# manifest.py
module("main.py")
module("wifi_manager.py", features=["wifi"])   # 仅 wifi 激活时刷入
package("drivers/", remote="/lib", features=["sdcard"])

resolve_active_tags(target=None, detected=None, feature=None, no_feature=None) -> set[str]

合并出最终标签集,收敛了原 pyrite-cli 中分散多处的逻辑:

  • 显式 target → 查 board_tags 取标签列表并包含 target 自身(大写);否则用 detected
  • 并入 feature、差去 no_feature
  • feature/no_feature/detected 均接受逗号分隔字符串或集合
resolve_active_tags(target="ESP32", feature="ble")   # {"ESP32", "wifi", "ble"}
resolve_active_tags(detected={"RP2040"}, no_feature="debug")

resolve_board_tags(machine, platform="", board_tags=DEFAULT_BOARD_TAGS) -> set[str]

将设备 os.uname().machine / sys.platform 字符串映射为板级标签(纯逻辑,不含串口 I/O,设备读取由调用方负责)。

build_project(manifest_path, out_dir, active_tags, base_dir=None) -> list[(src, dst)]

把「manifest 文件级过滤」与「逐文件 preprocess 代码级裁剪」串成一步,将裁剪后的副本写到 out_dir(按条目 remote 相对路径建目录;非 .py 文件原样拷贝)。纯文件操作,零设备依赖。

build_project("manifest.py", "dist/", {"wifi"}, base_dir="src/")

load_board_tags(start_dir=None, defaults=DEFAULT_BOARD_TAGS) -> dict[str, list[str]]

start_dir(默认 cwd)向上搜索 pyproject.toml,读取并合并 board_tags:

  • [tool.pyrite.board_tags] —— 与 pyrite-cli 完全兼容(同一份配置两边等价)
  • [tool.mpymacro.board_tags] —— mPyMacro 自有命名空间,优先级更高

key 统一大写。未找到或解析失败时回退到 defaults

命令行用法

安装后提供 mpymacro 命令,全程离线、零设备依赖(设备标签只能由 --detected 显式传入)。所有子命令都会向上搜索 pyproject.toml 加载 board_tags(--config-dir 指定起点),与 pyrite-cli 项目配置保持一致。

# 解析并打印 active_tags(逐行,便于 CI / $(...) 取用)
mpymacro tags --target ESP32 --feature ble            # → ESP32 / ble / wifi

# 单文件条件编译(默认输出到 stdout)
mpymacro expand main.py --tag wifi -o main.built.py

# 按 manifest 裁剪整个项目到 dist/
mpymacro build -m manifest.py -o dist/ --target ESP32 --feature sdcard

# 导出 feature/target 类型存根
mpymacro stub -o feature_stub.pyi

通用标签选项:--target--feature a,b--no-feature x--tag T(可重复)、--detected a,b(调用方从设备读出的标签)、--config-dir DIR

在 pyrite-cli 中作为依赖使用

mPyMacro 的 preprocess / load_manifest 与 pyrite-cli 内部实现 API 同构,manifest 格式、board_tags 配置均互相兼容。pyrite-cli 可将其作为 package 依赖引入、删除内部重复实现,同时保留板型自动识别、自动编译等设备侧能力。详见 docs/pyrite-cli-integration.md

在自己的刷入流程中接入

from mpymacro import resolve_board_tags, resolve_active_tags, preprocess, load_manifest

# 1. 从设备读取 machine/platform(你自己的串口逻辑),映射板级标签
detected = resolve_board_tags(machine_str, platform_str)

# 2. 合并 CLI 选项得到 active_tags
active_tags = resolve_active_tags(detected=detected, feature="sdcard", no_feature="debug")

# 3. 文件级过滤
for local, remote in load_manifest("manifest.py", active_tags):
    src = open(local).read()
    # 4. 代码级转换后再刷入
    out = preprocess(src, active_tags, local)
    flash(remote, out)   # 你自己的刷入逻辑

feature/target 类型提示

mpymacro/stubs/feature_stub.pyi 提供 feature / target 的类型存根。把它拷到你的工程目录(或加入 stub 搜索路径),即可在编辑器中获得 @feature(...) / with feature(...): 的类型提示——这些符号仅用于预处理标记,运行时不会保留。

测试

python -m pytest -v

target vs feature 语义

target feature
语义 硬件平台(互斥) 功能特性(可组合)
来源 设备自动检测 / target= board_tags 隐式 / feature=
示例 ESP32RP2040STM32 wifiblesdcarddebug

两者语法完全相同,仅语义区分。标签匹配为精确字符串匹配target 自动大写,feature 原样传递。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages