feat(pybot): bot4cj 的 Python 迁移版(.mt 解析 + 硬件驱动 + 插件系统)

- parser: FSM 解析器与仓颉版逐语义对齐,修 S01/S02/S04/Q05
- robot/hardvars: 六类硬件变量工厂,修 r.cj:911 重名 FIXME、Q07 倒序释放
- plugin: Python 插件替代 Lua 桥接,修 S07 三级路径查找
- hd: i2c(每fd锁 Q03/S06)/spi/servo/kalman/mpu6050(S05/Q06)/speaker(S03 实声)
- modules: PCA9685 与 Lua 版逐字节对齐;ADS1115 重写(experimental);ADS1256 空桩
- tests: 42 项 pytest 全绿(Mock 总线 journal 断言)
- CLI: --dry-run / --test roundtrip / --verbose;运行时零第三方依赖
This commit is contained in:
qing committed 2026-08-09 18:57:03 +00:00
1 parent 85eaa2114e
commit bba7ee3eb4
31 files changed
+2155

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
"""插件加载器测试:三级路径查找(S07) / 缺失报错 / 实例隔离"""
import pytest
from pybot import plugin
def test_find_builtin_module():
"""包内置 modules/ 能找到 PCA9685"""
path = plugin.find_module("PCA9685")
assert path.name == "PCA9685.py"
assert path.parent.name == "modules"
def test_plugin_not_found():
with pytest.raises(plugin.PluginNotFound) as ei:
plugin.find_module("NoSuchDevice")
assert "NoSuchDevice" in str(ei.value)
assert "modules" in str(ei.value) # 报错含搜索路径
def test_env_path_priority(tmp_path, monkeypatch):
"""$PYBOT_MODULES_PATH 优先于包内置目录"""
(tmp_path / "PCA9685.py").write_text(
"def build(ctx): return {'from': 'env'}\n"
"def call(ctx): return {}\n"
"def destroy(ctx): return {}\n", encoding="utf-8")
monkeypatch.setenv("PYBOT_MODULES_PATH", str(tmp_path))
assert plugin.find_module("PCA9685") == tmp_path / "PCA9685.py"
mod = plugin.load_module("PCA9685")
assert mod.build({}) == {"from": "env"}
def test_missing_protocol_function(tmp_path, monkeypatch):
"""插件缺 build/call/destroy 任一函数即拒绝加载"""
(tmp_path / "Broken.py").write_text(
"def build(ctx): return {}\n", encoding="utf-8")
monkeypatch.setenv("PYBOT_MODULES_PATH", str(tmp_path))
with pytest.raises(plugin.PluginNotFound):
plugin.load_module("Broken")
def test_independent_instances():
"""每次加载新模块实例:模块级全局状态互不共享(对齐 Lua 每设备状态机)"""
a = plugin.load_module("SPI")
b = plugin.load_module("SPI")
assert a is not b
a._fd = 42
assert b._fd == -1