Rust bindings for Python. This includes running and interacting with Python code from a Rust binary, as well as writing native Python modules.
A comparison with rust-cpython can be found in the guide.
PyO3 supports Python 3.6 and up. The minimum required Rust version is 1.45.0.
Building with PyPy is also possible (via cpyext) for Python 3.6, targeted PyPy version is 7.3+. Please refer to the pypy section in the guide.
You can either write a native Python module in Rust, or use Python from a Rust binary.
However, on some OSs, you need some additional packages. E.g. if you are on Ubuntu 18.04, please run
bash
sudo apt install python3-dev python-dev
PyO3 can be used to generate a native Python module.
Cargo.toml
```toml [package] name = "string-sum" version = "0.1.0" edition = "2018"
[lib] name = "string_sum" crate-type = ["cdylib"]
[dependencies.pyo3] version = "0.13.0" features = ["extension-module"] ```
src/lib.rs
```rust use pyo3::prelude::*; use pyo3::wrap_pyfunction;
/// Formats the sum of two numbers as string.
fn sumasstring(a: usize, b: usize) -> PyResult
/// A Python module implemented in Rust.
fn stringsum(py: Python, m: &PyModule) -> PyResult<()> { m.addfunction(wrappyfunction!(sumas_string, m)?)?;
Ok(())
} ```
On Windows and Linux, you can build normally with cargo build --release
. On macOS, you need to set additional linker arguments. One option is to compile with cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup
, the other is to create a .cargo/config
with the following content:
```toml [target.x8664-apple-darwin] rustflags = [ "-C", "link-arg=-undefined", "-C", "link-arg=dynamiclookup", ]
[target.aarch64-apple-darwin] rustflags = [ "-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup", ] ```
While developing, you can symlink (or copy) and rename the shared library from the target folder: On MacOS, rename libstring_sum.dylib
to string_sum.so
, on Windows libstring_sum.dll
to string_sum.pyd
, and on Linux libstring_sum.so
to string_sum.so
. Then open a Python shell in the same folder and you'll be able to import string_sum
.
Adding the cdylib
arguments in the Cargo.toml
files changes the way your crate is compiled.
Other Rust projects using your crate will have to link against the .so
or .pyd
file rather than include your library directly as normal.
In order to make available your crate in the usual way for Rust user, you you might want to consider using both crate-type = ["cdylib", "rlib"]
so that Rust users can use the rlib
(the default lib crate type).
Another possibility is to create a new crate to perform the binding.
To build, test and publish your crate as a Python module, you can use maturin or setuptools-rust. You can find an example for setuptools-rust in examples/word-count, while maturin should work on your crate without any configuration.
If you want your Rust application to create a Python interpreter internally and
use it to run Python code, add pyo3
to your Cargo.toml
like this:
toml
[dependencies]
pyo3 = "0.13.0"
Example program displaying the value of sys.version
and the current user name:
```rust use pyo3::prelude::*; use pyo3::types::IntoPyDict;
fn main() -> Result<(), ()> { Python::withgil(|py| { main(py).maperr(|e| { // We can't display Python exceptions via std::fmt::Display, // so print the error here manually. e.printandsetsyslastvars(py); }) }) }
fn main(py: Python) -> PyResult<()> { let sys = py.import("sys")?; let version: String = sys.get("version")?.extract()?; let locals = [("os", py.import("os")?)].intopy_dict(py); let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; let user: String = py.eval(code, None, Some(&locals))?.extract()?; println!("Hello {}, I'm Python {}", user, version); Ok(()) } ```
Our guide has a section with lots of examples about this topic.
built
crate as a PyDict
PyO3 is licensed under the Apache-2.0 license. Python is licensed under the Python License.