Rust PyO3 使用

2021-01-26 language rust python

Python 有强于 Bash 的语法检查,但是,不适合一些高性能的场景,所以,很多时候会作为胶水语言使用。而且,由于 CPython 作为事实标准,使得其兼容 C/C++ 要容易的多,所以,通常高性能场景下会通过 C/C++ 进行重构。

这里介绍 Python 如何调用 Rust 的实现。

简介

社区有个很不错的库 PyO3 可以用来在 Rust 和 Python 之间相互调用。

示例

为了简化处理,同时提供了 maturin 工具,可以通过 pip install maturin 安装。

----- 如下新建目录,或者在某目录下 init 初始化
$ maturin new pyex
----- 其中Cargo.toml关键内容如下
$ cat Cargo.toml
[lib]
name = "pyex"
crate-type = ["cdylib"]

[dependencies]
pyo3 = {version="0.22.0", features=["extension-module"]}

----- 会自动生成如下文件
$ cat src/lib.rs
use pyo3::prelude::*;

/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum(a: usize, b: usize) -> PyResult<String> {
    Ok((a + b).to_string())
}

/// A Python module implemented in Rust.
#[pymodule]
fn pyex(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(sum, m)?)?;
    Ok(())
}

----- 可以直接编译
$ cargo build

此时会在 target/debug 目录下生成 libpyex.so 文件,然后切换到上述 target/debug 目录,并将源文件重命名为 pyex.so 文件,然后通过如下方式调用。

$ python
>>> import pyex
>>> pyex.sum(1, 2)
'3'

Maturin

为了简化,可以使用 maturin 命令,注意,其依赖 virtualenv 或者 venv 环境,更多可以参考 Python 虚拟环境详解 内容,如下是常用的命令。

----- 启动 Python 虚拟环境,可以防止对老环境的污染
$ python -m venv rust
----- 后续可以直接使用该命名
$ source rust/bin/activate

----- 编译,会在 target/wheels 目录下生成 *.whl 文件
(rust) $ maturin build

----- 会自动安装到 Python 环境中,可以通过 pip list 查看
(rust) $ maturin develop
----- 生成 .pyd 文件,可以在 Python 中直接导入
(rust) $ maturin develop --skip-install
----- 打包生成 .whl 可发布的文件
(rust) $ maturin develop --release

然后就可以使用上述的方式验证,此时不需要再切换目录、重命名文件。

常用技巧

错误处理

如上示例的函数会返回 PyResult 结果,在 src/err/mod.rs 文件中定义如下。

pub struct PyErr {
    state: UnsafeCell<Option<PyErrState>>,
}
pub type PyResult<T> = Result<T, PyErr>;

常规的,可以通过 PyOSError::new_err("error") 类似方式生成,如果有些三方库自定义的错误类型,简单的可以通过 map_err 函数进行映射,详见 Rust 中的错误处理 中的介绍。

当三方库的错误类型比较多时,上述 map_err 方式处理起来会比较繁琐,而受限于 Rust 的 Orphan Rule 的限制,无法对一个三方的错误实现某个三方的特征,也就是如下的方式会报错。

impl From<hello::Error> for PyErr {
    fn from(err: hello::Error) -> Self {
        Self(err)
    }
}

此时就需要引入一个中间的错误类型进行转换,详见 Error Handling 的介绍。

参考