Rust bindings for the Python interpreter
Go to file
Rene Leveille e4edc6cdd7 Load compilation options from _sysconfigdata_*.py file
Following the discussion in #1077 this change allows the compilation
script to load the configurations from a _sysconfigdata_ file
in the library directory.

This file is also provided on target systems in the same directory.
At least on Manjaro Linux.
Which could remove the need to run a python script at compile time
for compiling the the host.

I've also addressed the linking need for android in #1082.
2020-08-17 11:25:06 -04:00
.github Test MSRV on github actions 2020-08-17 22:10:27 +09:00
benches Remove bench_pyclass 2020-05-03 11:12:31 +01:00
examples Rename exceptions to PyException etc; reintroduce deprecated ones 2020-07-18 06:02:57 +01:00
guide Merge pull request #1098 from davidhewitt/py-as-into-ref 2020-08-11 20:44:53 +01:00
pyo3-derive-backend Merge branch 'master' into iterator-example 2020-08-10 17:11:00 +09:00
pyo3cls Bump version to 0.11.1 2020-06-30 14:53:55 +09:00
src Merge remote-tracking branch 'upstream/master' into fallible_py_bytes_bytearray_new_with 2020-08-11 20:52:49 +00:00
tests Merge pull request #1098 from davidhewitt/py-as-into-ref 2020-08-11 20:44:53 +01:00
.gitignore Some api improvements 2019-02-23 18:01:22 +01:00
CHANGELOG.md Merge pull request #1098 from davidhewitt/py-as-into-ref 2020-08-11 20:44:53 +01:00
Cargo.toml Load compilation options from _sysconfigdata_*.py file 2020-08-17 11:25:06 -04:00
Code-of-Conduct.md Remove myself from the CoC 2020-04-13 13:38:41 +02:00
Contributing.md Migrate from Travis CI to Github Actions 2020-08-12 08:09:30 +01:00
LICENSE Rename LICENSE-APACHE to LICENSE 2017-10-04 08:56:57 -07:00
Makefile Move fmt and clippy to actions 2020-08-09 12:15:54 +01:00
README.md Migrate from Travis CI to Github Actions 2020-08-12 08:09:30 +01:00
build.rs Load compilation options from _sysconfigdata_*.py file 2020-08-17 11:25:06 -04:00
codecov.yml Turn off codecov PR comments 2019-08-05 11:36:20 +02:00
pyproject.toml Format examples with black (#590) 2019-09-06 01:16:09 +02:00
tox.ini Add Python 3.8 to CI 2020-02-01 15:08:41 +01:00

README.md

PyO3

Actions Status codecov crates.io minimum rustc 1.39 Join the dev chat

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.

Usage

PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.39.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

sudo apt install python3-dev python-dev

Using Rust from Python

PyO3 can be used to generate a native Python module.

Cargo.toml

[package]
name = "string-sum"
version = "0.1.0"
edition = "2018"

[lib]
name = "string_sum"
crate-type = ["cdylib"]

[dependencies.pyo3]
version = "0.11.1"
features = ["extension-module"]

src/lib.rs

use pyo3::prelude::*;
use pyo3::wrap_pyfunction;

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

/// A Python module implemented in Rust.
#[pymodule]
fn string_sum(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_wrapped(wrap_pyfunction!(sum_as_string))?;

    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:

[target.x86_64-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.

Using Python from Rust

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:

[dependencies]
pyo3 = "0.11.1"

Example program displaying the value of sys.version and the current user name:

use pyo3::prelude::*;
use pyo3::types::IntoPyDict;

fn main() -> Result<(), ()> {
    Python::with_gil(|py| {
        main_(py).map_err(|e| {
          // We can't display Python exceptions via std::fmt::Display,
          // so print the error here manually.
          e.print_and_set_sys_last_vars(py);
        })
    })
}

fn main_(py: Python) -> PyResult<()> {
    let sys = py.import("sys")?;
    let version: String = sys.get("version")?.extract()?;
    let locals = [("os", py.import("os")?)].into_py_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.

Tools and libraries

  • maturin Zero configuration build tool for Rust-made Python extensions.
  • setuptools-rust Setuptools plugin for Rust support.
  • pyo3-built Simple macro to expose metadata obtained with the built crate as a PyDict
  • rust-numpy Rust binding of NumPy C-API
  • dict-derive Derive FromPyObject to automatically transform Python dicts into Rust structs
  • pyo3-log Bridge from Rust to Python logging

Examples

  • hyperjson A hyper-fast Python module for reading/writing JSON data using Rust's serde-json
  • html-py-ever Using html5ever through kuchiki to speed up html parsing and css-selecting.
  • point-process High level API for pointprocesses as a Python library
  • autopy A simple, cross-platform GUI automation library for Python and Rust.
    • Contains an example of building wheels on TravisCI and appveyor using cibuildwheel
  • orjson Fast Python JSON library
  • inline-python Inline Python code directly in your Rust code
  • Rogue-Gym Customizable rogue-like game for AI experiments
    • Contains an example of building wheels on Azure Pipelines
  • fastuuid Python bindings to Rust's UUID library
  • python-ext-wasm Python library to run WebAssembly binaries
  • mocpy Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere
  • tokenizers Python bindings to the Hugging Face tokenizers (NLP) written in Rust

License

PyO3 is licensed under the Apache-2.0 license. Python is licensed under the Python License.