如何用Maturin运行Rust库单元测试?
我正在用 Rust 和 maturin 创建一个自定义的 Python 模块。
我使用的是 PyEnv,安装了 Python 3.12.2,命令是 env PYTHON_CONFIGURE_OPTS="--enable-shared"
和 --keep
,这样 Python 的源代码就可以用了。我有一个虚拟环境(venv),并且我用 maturin develop
来构建我的库,并根据需要更新虚拟环境。
在我的 lib.rs
文件里有一些单元测试,通常我只需要运行 cargo test
就可以测试它们。
但是在 Maturin 管理的环境中,我遇到了链接错误:
error: linking with `cc` failed: exit status: 1
...
= note: /usr/bin/ld: .../target/debug/deps/libpyo3-c286c2d4bbe22ea2.rlib(pyo3-c286c2d4bbe22ea2.pyo3.7555584b645de9e8-cgu.01.rcgu.o): in function `pyo3_ffi::object::Py_DECREF':
$HOME/.cargo/git/checkouts/pyo3-a22e69bc62b9f0fd/da24f0c/pyo3-ffi/src/object.rs:597: undefined reference to `_Py_Dealloc'
我用以下两种方法解决了这个问题:
- 使用
RUSTFLAGS
和LD_LIBRARY_PATH
/ rpath:
$ export RUSTFLAGS="-C link-arg=-Wl,-rpath,.../pyenv.git/versions/3.12.2/lib -C link-arg=-L.../pyenv.git/versions/3.12.2/lib -C link-arg=-lpython3.12"
- 或者创建
.cargo/config.toml
文件:
[target.'cfg(all())']
rustflags = [
"-C", "link-arg=-Wl,-rpath,.../pyenv.git/versions/3.12.2/lib",
"-C", "link-arg=-L.../pyenv.git/versions/3.12.2/lib",
"-C", "link-arg=-lpython3.12",
]
为了简洁和隐私,我省略了一些路径。
这两种方法其实做的事情是一样的——提供一个明确的链接器路径、要链接的库和运行时库路径。这是可行的,但感觉有点不对劲。
我没找到 maturin test
或类似的命令,感觉不应该我手动给 cargo
/ rustc
指定链接器参数,因为我已经有 maturin 可以帮我处理这些了。
有没有什么好的方法可以用 maturin 来做到这一点?
编辑:在 PyO3 的文档中,我找到了 测试 部分,推荐将 Cargo.toml
的这一部分转换为:
[dependencies]
pyo3 = { git = "https://github.com/pyo3/pyo3", features = ["extension-module"] }
变成这样:
[dependencies.pyo3]
git = "https://github.com/pyo3/pyo3"
[features]
extension-module = ["pyo3/extension-module"]
default = ["extension-module"]
然后运行 cargo test --no-default-features
。这样确实解决了链接错误,但在运行时仍然找不到库:
Running unittests src/lib.rs (target/debug/deps/pyo3_example-cc3941bd091dc64e)
.../target/debug/deps/pyo3_example-cc3941bd091dc64e: error while loading shared libraries: libpython3.12.so.1.0: cannot open shared object file: No such file or directory
这个问题可以通过将 LD_LIBRARY_PATH
设置为 .../pyenv.git/versions/3.12.2/lib
来解决,这样能帮上点忙,但还是不够充分。
2 个回答
今天是我第一次使用maturin,所以我还不是专家,但我有两个想法:
- 可以试试在PyEnv不工作的情况下,conda(我用的是miniconda3)是否能正常工作。
- 你可以尝试使用Makefile或者Just,然后让cargo watch在运行测试之前重新调用maturin。
我有差不多一样的设置:用PyO3和maturin的Python模块。我运行cargo test
没有问题。可能是因为在我的cargo.toml
文件里有这一部分:
[lib]
name = "module_name_whatever"
crate-type = ["cdylib", "lib"]
编辑:为了帮助提问者排查问题,这里是我的完整Cargo.toml文件
[package]
name = "package_name"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "package_name"
crate-type = ["cdylib", "lib"]
[dependencies.pyo3]
version = "0.20.0"
# "abi3-py38" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.8
features = ["abi3-py38"]