如何分离Rust核心库与PyO3封装的Python扩展及测试方案
问题解答
1. 拆分核心功能与Python绑定的最佳实践(Cargo工作区方案)
你的猜测完全正确,Cargo工作区是这类场景的最优解,能彻底分离Rust核心库和Python绑定层,让Rust用户无需依赖Python生态,同时Python用户仅需安装绑定包即可使用功能。
最终项目结构
FizzBuzz/ ├── Cargo.toml # 工作区根配置 ├── pyproject.toml # Python打包配置 ├── fizzbuzz-core/ # Rust核心功能库 │ ├── Cargo.toml │ ├── src/ │ │ └── lib.rs │ └── tests/ │ └── test_fizzbuzz.rs └── fizzbuzz-py/ # Python绑定层Rust库 ├── Cargo.toml ├── src/ │ └── lib.rs └── tests/ ├── test_fizzbuzzo3.rs # Rust侧测试绑定逻辑 └── test_fizzbuzz.py # Python侧测试功能
关键配置细节
根Cargo.toml(工作区)
[workspace] members = ["fizzbuzz-core", "fizzbuzz-py"]
fizzbuzz-core/Cargo.toml(核心库)
仅保留Rust生态所需配置,完全脱离PyO3依赖:
[package] name = "fizzbuzz-core" version = "0.1.0" edition = "2021" [dependencies] # 只添加核心功能需要的Rust依赖,无PyO3相关配置
fizzbuzz-py/Cargo.toml(绑定层)
依赖核心库和PyO3,指定编译为Python扩展格式:
[package] name = "fizzbuzz-py" version = "0.1.0" edition = "2021" [dependencies] fizzbuzz-core = { path = "../fizzbuzz-core" } pyo3 = { version = "0.21", features = ["extension-module"] } [lib] name = "fizzbuzz_py" # Python导入时的模块名(需用下划线) crate-type = ["cdylib"]
pyproject.toml(Python打包)
配置setuptools-rust指向绑定层项目:
[build-system] requires = ["setuptools", "setuptools-rust"] build-backend = "setuptools.build_meta" [project] name = "fizzbuzz-py" version = "0.1.0" dependencies = [] [tool.setuptools-rust] extension = true rust-version = "1.70" path = "fizzbuzz-py/Cargo.toml"
优势与发布流程
- 核心库
fizzbuzz-core可独立发布到crates.io,Rust用户直接通过cargo add fizzbuzz-core使用,完全不涉及Python依赖。 - Python绑定包
fizzbuzz-py通过python -m build构建后,可发布到PyPI,Python用户用pip install fizzbuzz-py安装。 - 修改核心库代码后,工作区会自动同步依赖,三个测试套件(核心库Rust测试、绑定层Rust测试、Python测试)都能感知变更,直接运行对应测试命令即可。
2. 无需pip install即可测试Python扩展的方案
方案1:用PyO3在Rust中直接测试绑定
在fizzbuzz-py/tests/test_fizzbuzzo3.rs中,利用PyO3的Python上下文直接调用导出的函数,无需安装扩展:
use pyo3::prelude::*; #[test] fn test_fizzbuzz_py() -> PyResult<()> { let gil = Python::acquire_gil(); let py = gil.python(); // 导入当前构建的绑定模块 let fizzbuzz_py = py.import("fizzbuzz_py")?; let result: String = fizzbuzz_py.call_method1("fizzbuzz", (15,))?.extract()?; assert_eq!(result, "FizzBuzz"); Ok(()) }
运行测试:
cd fizzbuzz-py cargo test
方案2:直接加载构建好的动态库进行Python测试
先构建绑定层:
cd fizzbuzz-py cargo build --release
在Python测试文件中添加动态库路径,直接导入测试:
import sys import os # 根据系统动态添加库路径 lib_dir = os.path.join(os.path.dirname(__file__), "../target/release") sys.path.append(lib_dir) import fizzbuzz_py def test_fizzbuzz(): assert fizzbuzz_py.fizzbuzz(15) == "FizzBuzz"
直接运行Python测试:
python fizzbuzz-py/tests/test_fizzbuzz.py
方案3:用maturin简化测试(可选替代setuptools-rust)
如果切换到maturin工具,可直接完成构建并测试,无需手动安装:
# 临时构建并加载扩展到当前环境,不修改全局依赖 maturin develop --release # 运行Python测试 pytest fizzbuzz-py/tests/
或者用maturin test一键执行所有测试(包括Rust和Python测试):
maturin test
内容的提问来源于stack exchange,提问作者MusicalNinja
相关产品推荐
相关产品推荐

