如何用Maturin运行Rust编写的Python模块单元测试?
核心问题
用Maturin构建Rust自定义Python模块,PyEnv管理的Python3.12.2(安装时指定了--enable-shared),运行cargo test时出现链接错误,提示undefined reference to _Py_Dealloc。手动设置RUSTFLAGS或编写.cargo/config.toml能解决问题,但需要硬编码路径不够优雅;按PyO3文档修改Cargo.toml后用cargo test --no-default-features能解决链接错误,但运行时又找不到libpython3.12.so,得额外设置LD_LIBRARY_PATH。
优雅解决方案
方案1:用Maturin代理运行Cargo测试
直接用maturin run包裹cargo test,Maturin会自动识别PyEnv的Python环境,注入所有必要的链接参数和环境变量:
maturin run -- cargo test
无需手动设置任何路径,Maturin会自动处理RUSTFLAGS、LD_LIBRARY_PATH等配置,直接运行测试。
方案2:导出Maturin的环境变量后再跑测试
如果不想用maturin run,可以让Cargo继承Maturin识别到的环境:
eval "$(maturin env)" cargo test
maturin env会输出适配当前Python环境的所有必要环境变量设置,用eval执行后,Cargo就能自动找到PyEnv的Python共享库,不用手动写路径。
针对PyO3无默认特性测试的优化
如果坚持用cargo test --no-default-features的方式,解决运行时库找不到的问题可以这样做:
- 临时运行:直接把
LD_LIBRARY_PATH嵌入命令
LD_LIBRARY_PATH=$(pyenv prefix)/lib cargo test --no-default-features
- 永久项目配置:在项目根目录创建
.env文件,内容如下:
LD_LIBRARY_PATH=$(pyenv prefix)/lib
之后每次测试前执行source .env && cargo test --no-default-features,如果安装了direnv工具,还能自动加载这个环境变量,无需手动执行source。
为什么这方法更优雅
Maturin本身具备自动检测Python环境的能力,不管是PyEnv、虚拟环境还是系统Python,它都能准确获取共享库路径、链接参数等信息。用上面的方法,不用硬编码任何绝对路径,换Python版本或环境时也不用修改配置,完全由Maturin自动适配。
内容的提问来源于stack exchange,提问作者davidA

