6

From Python, I want to call a Rust function that returns a Python object:

my_rust_module.my_function()  # => <object>

I am not sure how to create this function since the PYO3 guide on class instantiation describes how to instantiate such an object as a PyRef, however I can't work out how to return such a reference from a pyfunction.

This is what I am trying:

#[pyfunction]
fn my_function(py: Python) -> PyRef {
    let gil = Python::acquire_gil();
    let py = gil.python();
    PyRef::new(py, MyStruct { }).unwrap()
}

However, PyRef does not seem to be a valid return type (compiler says "wrong number of type arguments: expected 1, found 0"), and I don't know how to convert a PyRef into something that can be returned such as a PyObject.

1 Answer 1

4

Given the proper scaffolding is in place, such as pymodule and pyclass definitions, there are multiple ways to accomplish this:

#[pyfunction]
fn my_function() -> PyResult<PyObject> {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let pyref = PyRef::new(py, MyStruct {})?;

    Ok(pyref.to_object(py))
}

Or:

#[pyfunction]
fn my_function() -> PyResult<Py<MyStruct>> {
    let gil = Python::acquire_gil();
    let py = gil.python();

    Py::new(py, MyStruct {})
}

I also take the liberty of wrapping returned object in PyResult in order to propagate the possible errors to python land. You can unwrap in Rust and return the naked object instead, but I'd argue this way of handling error is recommended.


Edit: the previous answer was not complete and a little bit misleading. The simplest way is actually just:

#[pyfunction]
fn my_function() -> PyResult<MyStruct> {
    Ok(MyStruct {})
}

The PYO3 pyclass guide states:

You can use pyclasses like normal rust structs.

However, if instantiated normally, you can't treat pyclasses as Python objects.

To get a Python object which includes pyclass, we have to use some special methods.

I believe this is only true if the said value doesn't cross Rust / Python boundary. If it does, pyfunction macro automatically converts the Python objects to Rust values and the Rust return value back into a Python object. The PYO3 guide could be more specific here.

Sign up to request clarification or add additional context in comments.

5 Comments

Perfect, thank you so much! Am I right in thinking that if MyStruct does not contain any embedded references to other Python objects, then I don't need to worry about garbage collection here? i.e. the Python GC will take care of clearing this up whenever there are no more references to it on the Python side? Thanks again!
@Ginty the answer has been updated. And yes, gc works.
Thanks @edwardw, I see it working without the gil stuff as you describe.
I've just noticed that there is an advantage to returning PyObject if you are implementing a function that can return different types, in that case you can cast them all to PyObject via to_object(py)
@Ginty indeed, that is a jolly good point. Quite impress that PYO3 manages to bridge the gap of two type systems this way, one strong type and the other dynamic type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.