Reputation: 65
This question is framed in terms of a specific example, for concreteness, but it's part of a much bigger question of how to return objects in pyo3, generally.
I'm writing a python module with a Rust backend using PyO3.
I'd like to write a wrapper for a function that returns a rational number (of type Ratio<isize>
); the wrapped function should return a Python Fraction
. I'm not sure how to do this, but it would probably suffice to be able to reproduce the following function with PyO3 would be sufficient:
import fractions
def divide( numer, denom ):
return fractions.Fraction( numer, denom )
Currently my library just destructures a fraction into a pair of integers (numer, denom)
and exports to python, then uses pure python wrapper code to reassemble the fraction, but this causes all sorts of organizational issues.
The following is my latest (clearly wrong) attempt.
pub fn export_fraction( numer: isize, denom: isize )
->
PyResult< PyAny > {
Python::with_gil(|py| {
let frac = py.import("fractions").ok().unwrap();
Ok(frac.call_method("Fraction",( numer, denom),None) )
})
}
The error message is a mismatch for the return type; I've tried playing around with it but my ability to parse the various Result
, PyResult
, etc. types is too nubile.
error[E0308]: mismatched types
--> src/simplex_filtered.rs:70:9
|
68 | PyResult< PyAny > {
| ----------------- expected `Result<pyo3::PyAny, PyErr>` because of return type
69 |
70 | / Python::with_gil(|py| {
71 | | let frac = py.import("fractions").ok().unwrap();
72 | | Ok(frac.call_method("Fraction",( numer, denom),None) )
73 | |
74 | | })
| |__________^ expected `Result<PyAny, PyErr>`, found `Result<Result<&PyAny, PyErr>, _>`
|
= note: expected enum `Result<pyo3::PyAny, PyErr>`
found enum `Result<Result<&pyo3::PyAny, PyErr>, _>`
Upvotes: 1
Views: 441
Reputation: 27249
PyAny
is a type that only exists within the Python
environment so it has to be a reference, either one bound to a current GIL, but you have to pass in the Python<'py>
instance in that case:
pub fn export_fraction_with_current_gil<'py>(py: Python<'py>, numer: isize, denom: isize) -> PyResult<&'py PyAny> {
let frac = py.import("fractions")?;
frac.call_method("Fraction", (numer, denom), None)
}
Or you use Py
which is "A GIL-independent reference to an object allocated on the Python heap.":
pub fn export_fraction(numer: isize, denom: isize) -> PyResult<Py<PyAny>> {
Python::with_gil(|py| {
let frac = py.import("fractions")?;
frac.call_method("Fraction", (numer, denom), None)
.map(Into::into)
})
}
Upvotes: 4