如何用Maturin运行Rust库单元测试?

2 投票
2 回答
85 浏览
提问于 2025-04-13 02:21

我正在用 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'

我用以下两种方法解决了这个问题:

  1. 使用 RUSTFLAGSLD_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"
  1. 或者创建 .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 个回答

-1

今天是我第一次使用maturin,所以我还不是专家,但我有两个想法:

  1. 可以试试在PyEnv不工作的情况下,conda(我用的是miniconda3)是否能正常工作。
  2. 你可以尝试使用Makefile或者Just,然后让cargo watch在运行测试之前重新调用maturin。
0

我有差不多一样的设置:用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"]

撰写回答