Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a Rust function from a Python file using pyo3?

I'm working on a videogame in which I'll need to set Rust objects (like, add a button with texture:"", coords:"", text:"", action:"") from Python files.

I'm using the pyo3 crate to link Python and Rust. I succeed calling Python scripts from my Rust code, but I can't find how to call a Rust function from a Python file.

Rust code that executes my Python script:

fn main() -> PyResult<()> {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let script = fs::read_to_string("deep_hello.py")?;

    println!("RUNNING :\n[\n{}]", script);
    py.run(&script, None, None)
}

Rust function that I'd like to call from my Python script:

/// hello_from_rust(/)
/// --
///
/// This function prints hello because she is very nice.
#[pyfunction]
fn hello_from_rust() {
    println!("Hello from rust from python !");
}

My Python script:

hello_from_rust()

I'm getting this output:

RUNNING :
[
hello_from_rust()
]
Error: PyErr { type: Py(0x7f9cdd1e9580, PhantomData) }
like image 414
Suraii Avatar asked Jun 28 '19 09:06

Suraii


People also ask

Can you use Rust with Python?

Rust can benefit from Python's ease of use, and Python can benefit from Rust's speed and safety. If you want to use Rust with Python, or Python with Rust, you'll need to have at least passing familiarity with both languages to get the best results.


1 Answers

If I understood your issue correctly, what you're trying to do involves three steps:

  1. Create a Python module containing the function hello_from_rust()
  2. Use it in a Python script deep_hello.py
  3. Run deep_hello.py from within Rust.

I wasn't able to completely reproduce your issue, but it seems like the root of your problem might be in the first or second step.

Defining a Python module using PyO3

From the PyO3 documentation, I would expect hello_from_rust() to be in a Rust file defining a Python module that looks something like this:

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

#[pyfunction]
fn hello_from_rust() {
    println!("Hello from rust from python !");
}

#[pymodule]
fn hello_module(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_wrapped(wrap_pyfunction!(hello_from_rust));

    Ok(())
}

You can build this into a Python module with Cargo (see here; instructions are slightly different depending on your OS) and place the resulting .so (Linux or MacOS) or .pyd (Windows) file in the same directory as your Python script.

Note

You have to create a function something along the lines of hello_module to initialize your Python module. If you don't, your Rust library might still compile, but you won't be able to import hello_from_rust.

Using your new Python module

Your deep_hello.py script should look like this:

import hello_module
hello_module.hello_from_rust()

Making sure that the .so or .pyd file is accessible in your PYTHON_PATH (eg by putting it in the same directory as your script), running deep_hello.py with Python >= 3.5 should work. (Note that your system Python is probably Python 2, so you might want to create an Anaconda environment with a newer version of Python and work from in there.)

For me, following these steps did the trick.

(ruspy) ruspy $ python deep_hello.py
Hello from rust from python !

Note

Make sure you remember to import hello_module in your Python script! The PyErr returned by your py.run(&script, None, None) makes me wonder if the problem is actually in your Python script, and indeed I would expect hello_from_rust() without a preceding from deep_hello import * to produce a NameError, even if you put together your deep_hello module correctly.

Calling Rust from Python from Rust

I'm not totally sure why you want to do this, but you should now be able to run deep_hello.py from Rust.

like image 103
Emerson Harkin Avatar answered Oct 11 '22 11:10

Emerson Harkin