Back in 2017, I wrote about how to create Python modules using Rust. Things changed in between, especially PyO3 project now makes it super easy to create such extension modules.
Requirements
I am using Python 3.7.3 and using the latest nightly build of Rust using rustup tool. Remember to use the nightly toolchain than stable.
rustup default nightly
The example source
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::wrap_pyfunction;
use std::collections::HashMap;
#[pyfunction]
/// Formats the sum of two numbers as string.
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
#[pyfunction]
/// Formats the sum of two numbers as string.
fn get_result() -> PyResult<HashMap<String, String>> {
let mut result = HashMap::new();
result.insert("name".to_string(), "kushal".to_string());
result.insert("age".to_string(), "36".to_string());
Ok(result)
}
#[pyfunction]
// Returns a Person class, takes a dict with {"name": "age", "age": 100} format.
fn give_me_a_person(data: &PyDict) -> PyResult<Person> {
let name: String = data.get_item("name").unwrap().extract().unwrap();
let age: i64 = data.get_item("age").unwrap().extract().unwrap();
let p: Person = Person::new(name, age);
Ok(p)
}
#[pyclass]
#[derive(Debug)]
struct Person {
#[pyo3(get, set)]
name: String,
#[pyo3(get, set)]
age: i64,
}
#[pymethods]
impl Person {
#[new]
fn new(name: String, age: i64) -> Self {
Person { name, age }
}
}
#[pymodule]
/// A Python module implemented in Rust.
fn myfriendrust(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(sum_as_string))?;
m.add_wrapped(wrap_pyfunction!(get_result))?;
m.add_wrapped(wrap_pyfunction!(give_me_a_person))?;
m.add_class::<Person>()?;
Ok(())
}
In this example, we are creating one class Person
, and 3 different function. You can checkout the whole source from this git repo.
get_result
function returns a HashMap
object from Rust. Where as give_me_person
takes a Python dictionary as argument and then creates a Person
class from it and then returns it to Python.
One thing to notice that we are using __new__
to create an instance of the Person
class. You can read about the class creation in the documentation.
Building the module
$ cargo build --release
<omitting output>
Compiling myfriendrust v0.1.0 (/home/kdas/code/rust/myfriendrust)
Finished release [optimized] target(s) in 2.60s
$ ls target/release/
build deps examples incremental libmyfriendrust.d libmyfriendrust.so
This will create a libmodulename.so
file, in this case the name of the file is libmyfriendrust.so
. I have a helper shell script try.sh
to rename the file to myfriendrust.so
so that I can test it.
cd target/release
mv libmyfriendrust.so myfriendrust.so
python3
Now, let us try it out.
To build a wheel for distribution
You can use maturin tool to create a wheel for distribution.
$ maturin build
🔗 Found pyo3 bindings
🐍 Found CPython 3.7m at python3.7
Compiling proc-macro2 v1.0.10
Compiling unicode-xid v0.2.0
Compiling syn v1.0.18
Compiling serde v1.0.106
Compiling ryu v1.0.4
Compiling proc-macro-hack v0.5.15
Compiling libc v0.2.69
Compiling regex-syntax v0.6.17
Compiling autocfg v1.0.0
Compiling itoa v0.4.5
Compiling scopeguard v1.1.0
Compiling smallvec v1.4.0
Compiling cfg-if v0.1.10
Compiling unindent v0.1.5
Compiling inventory v0.1.6
Compiling version_check v0.9.1
Compiling num-traits v0.2.11
Compiling lock_api v0.3.4
Compiling quote v1.0.3
Compiling regex v1.3.7
Compiling parking_lot_core v0.7.2
Compiling parking_lot v0.10.2
Compiling pyo3-derive-backend v0.9.2
Compiling serde_derive v1.0.106
Compiling ghost v0.1.1
Compiling ctor v0.1.14
Compiling paste-impl v0.1.11
Compiling inventory-impl v0.1.6
Compiling indoc-impl v0.3.5
Compiling paste v0.1.11
Compiling pyo3cls v0.9.2
Compiling indoc v0.3.5
Compiling serde_json v1.0.51
Compiling pyo3 v0.9.2
Compiling myfriendrust v0.1.0 (/home/kdas/code/rust/myfriendrust)
Finished dev [unoptimized + debuginfo] target(s) in 56.57s
📦 Built wheel for CPython 3.7m to /home/kdas/code/rust/myfriendrust/target/wheels/myfriendrust-0.1.0-cp37-cp37m-manylinux1_x86_64.whl
This post is just an introduction. You can look into the documentation and start writing more complex code. If you say I write bad Rust code, then yes. I am still a very beginner in Rust.
from Planet Python
via read more
No comments:
Post a Comment