A Quick Start

The pyo3 package in Python is a tool for building and using Rust bindings in Python. It allows developers to write Rust code that can be called from Python.

Let’s start by running a simple Rust code. After running it successfully, we will understand what we did.

Install Rust to your computer.Open up a new project folder. Open VS Code in that folder.Create a new Python environment: python3 -m venv venv . Activate that environment.Install maturin. pip install maturinCreate a new folder for the project: mkdir project_rust1 (macOS). cd project_rust1 .Initialize maturin. maturin init . Select pyo3 . It will create the default project files.Build the project. maturin developCreate a Python file in the project folder, mine: python_code1.pyProject file structure.# python_code1.py

import project_rust1

def main():
print(project_rust1.sum_as_string(5, 6))

if __name__ == “__main__”:
main()Let’s run the python file from the terminal: python python_code1.py . We will get an output of 11.

So what happened here?

Maturin is a build system and packaging tool designed for creating Python packages from Rust code.

The maturin init command is used to create a new Rust-based Python extension project with a ready-to-use template. It sets up a basic project structure that include the necessary configurations for integrating Rust with Python.

It creates a Cargo.toml file configured for building a Python extension. This configuration file includes dependencies such as pyo3 and any necessary settings to build a Python-compatible library (like specifying crate-type as cdylib).

[package]name = “project_rust1”
version = “0.1.0”
edition = “2021”

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]name = “project_rust1”
crate-type = [“cdylib”] [dependencies]pyo3 = “0.21.1”

Cargo, the Rust package manager, uses the crate-type key to determine the binary output format of our project. The cdylib option specifically tells Cargo to generate C-compatible DLL (A DLL is a type of library file that can be loaded and used by programs written in various languages, not just Rust).

Another file created is pyproject.toml which streamlines the build process with maturin and provides metadata for our Python package.

[build-system]requires = [“maturin>=1.6,<2.0”]build-backend = “maturin”

[project]name = “project_rust1”
requires-python = “>=3.8”
classifiers = [
“Programming Language :: Rust”,
“Programming Language :: Python :: Implementation :: CPython”,
“Programming Language :: Python :: Implementation :: PyPy”,
]dynamic = [“version”][tool.maturin]features = [“pyo3/extension-module”] [build-system] section specifies the requirements and configuration for the build system used to compile the Python package.

[project] section contains metadata about our project.

[tool.maturin] section is specific to maturin and defines settings that are directly used by maturin during the build process.

The src/lib.rs file contains some Rust code that uses pyo3 to define functions and modules that can be imported into Python.

use pyo3::prelude::*;

/// 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 project_rust1(m: &Bound<‘_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}

use pyo3::prelude::*; line imports the necessary components from the pyo3 crate. The prelude module typically includes the most commonly used items.

The pyfunction macro transforms the annotated Rust function into a form that Python can call. It tells pyo3 that this function should be accessible from Python.

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

PyResult<String> is the return type that handle both successful Ok(String) or Err(PyErr) cases.

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

pymodule indicates that the function project_rust1 is defining a Python module.

m: &Bound <‘_, PyModule> is a reference to the Python module object being created. The Bound is part of pyo3 ‘s API to ensure that the Python GIL is held when this function is called, which is required for thread safety when interacting with Python objects.

add_function is used to add the sum_as_string function to the Python module. wrap_pyfunction! macro is used to wrap the Rust function in a way that it can be called from Python. The ? operator is used to propagate errors. If wrapping function fails (returns Err), it will return early from the project_rust1 function with the error.

If everything is successful, the function returns Ok(()) , indicating a successful module initialization with no value.

The maturin develop command builds the Rust project in debug mode. It installs the compiled extension directly into our current virtual environment. This allows us to import and use Rust code from our Python scripts without needing to rebuild.

And lastly, the Python code; it is possible to import the project_rust1 module we have just built.

import project_rust1

def main():
print(project_rust1.sum_as_string(5, 6))

if __name__ == “__main__”:
main()

Welcome aboard for our pyo3 exploration. We will continue to delve into the integration of Rust with Python.

Read More

Learning Go: #1 Getting StartedMove an Object in OpenGL C++Scala #5: ClassesSQL JoinsjQuery User Interface WidgetsLangChain in Chains #26: A Simple Pdf Reader App

Sources

https://www.rust-lang.org/tools/install

https://pyo3.rs/v0.21.2/

https://github.com/PyO3/maturin

https://www.maturin.rs/tutorial

https://www.youtube.com/watch?v=lyG6AKzu4ew

PyO3: Rust-to-Python Integration was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.

​ Level Up Coding – Medium

about Infinite Loop Digital

We support businesses by identifying requirements and helping clients integrate AI seamlessly into their operations.

Gartner
Gartner Digital Workplace Summit Generative Al

GenAI sessions:

  • 4 Use Cases for Generative AI and ChatGPT in the Digital Workplace
  • How the Power of Generative AI Will Transform Knowledge Management
  • The Perils and Promises of Microsoft 365 Copilot
  • How to Be the Generative AI Champion Your CIO and Organization Need
  • How to Shift Organizational Culture Today to Embrace Generative AI Tomorrow
  • Mitigate the Risks of Generative AI by Enhancing Your Information Governance
  • Cultivate Essential Skills for Collaborating With Artificial Intelligence
  • Ask the Expert: Microsoft 365 Copilot
  • Generative AI Across Digital Workplace Markets
10 – 11 June 2024

London, U.K.