Define a class in Python and import into Rust module, but it "cannot be converted"

84 views Asked by At

This may be documented somewhere in the PyO3 documents, but I couldn't find it.

I make a class like this in Python:

class ProgressData():
    def __init__(self):
        self.start_time_ms = 0
        self.last_progress_percent = 0.0

... I instantiate it and pass it to a Rust module with the following signature:

#[pyfunction]
fn index_documents(py: Python, progress_data: ProgressData) -> PyResult<()> {
    ...

I've defined an "equivalent" struct in the Rust module:

#[derive(Clone)]
#[pyclass]
pub struct ProgressData {
    start_time_ms: usize,
    last_progress_percent: f64,
}

and I've added this as one of the exportable classes:

#[pymodule]
#[pyo3(name = "populate_index")]
fn my_extension(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(index_documents, m)?)?;
    m.add_class::<HandlingFramework>()?;
    m.add_class::<TextDocument>()?;
    m.add_class::<ProgressData>()?;
    Ok(())
}

... but it doesn't work: I get

argument 'progress_data': 'ProgressData' object cannot be converted to 'ProgressData'

No further explanation.

The above classes "HandlingFramework" and "TextDocument" are created in Rust and exported to Python where their properties and methods can be used. Is there a way to do it the other way round (make in Python, use in Rust)? Or maybe the only way is to make a factory function in the Rust module to deliver a ProgressData object to the Python, put data in this, and then re-export it to another function in the Rust module?

I'd also like to define a couple of methods for ProgressData: again, any way to define this in Python, but have it accepted by Rust as a shoe-in for an equivalent Rust struct + impl?

1

There are 1 answers

0
mike rodent On

Masklinn pointed me to the right part of the manual.

But in fact I found that implementing a factory method solution was perhaps the simplest solution for me in this case: no need to mess around with the py object.

#[derive(Clone)]
#[pyclass]
pub struct ProgressData {
    #[pyo3(get, set)]
    start_time_ms: usize,
    #[pyo3(get, set)]
    last_progress_percent: f64,
}

#[pyfunction]
fn new_progress_data(py: Python) -> ProgressData {
    ProgressData {
        start_time_ms: 0,
        last_progress_percent: 0.0,
    }
}

... I can quickly rustle up an instance in the Python code and having manipulated the data re-export it to the Rust index_documents function ... and the ProgressData parameter is accepted without a murmur. Obviously the Python class definition has been deleted.