rustpyo3

How do I convert pyo3 PyComplex back into PyObject in rust


I'm using pyo3 to try and make python functions run in rust

I am trying to make a function which takes in two PyAny objects and gets a f64 value out of them and uses those to initialize a PyComplex object and then return it as a PyResult. The .into_py() is giving me a strange error

the trait bound `&PyComplex: pyo3::IntoPy<PyObject>` is not satisfied
the trait `pyo3::IntoPy<Py<PyComplex>>` is implemented for `&PyComplex`
for that trait implementation, expected `Py<PyComplex>`, found `PyObject`

My function is the following:

#[pyfunction]
fn imaginary(a: &PyAny, b: &PyAny) -> PyResult<PyObject> {
    let gil: GILGuard = Python::acquire_gil();
    let py: Python<'_> = gil.python();
    let real: f64 = a.extract()?;
    let imag: f64 = b.extract()?;
    let d: PyObject = PyComplex::from_doubles(py, real, imag).into_py(py);
    Ok(d)
}

Please excuse the sloppy code, I'm only just learning how to use pyo3 and pointers are appreciated

I tried the compilers suggestion which was to use this line

let d: PyObject = <&PyComplex as pyo3::IntoPy<T>>::into_py(PyComplex::from_doubles(py, real, imag), py)?;

instead of the other let d: line I had, but it didn't work because T isn't defined in the scope. I tried adding T to the function signature but that didn't work because python functions cant take a rust type parameter.

I also tried replacing T with PyComplex or &PyComplex hoping that would work but no luck.

What can I do to get this working?


Solution

  • The easiest way to do this conversion is by calling to_object method.

    use pyo3::{prelude::*, types::PyComplex};
    
    
    #[pyfunction]
    fn imaginary(py: Python<'_>, a: &PyAny, b: &PyAny) -> PyResult<PyObject> {
        let real: f64 = a.extract()?;
        let imag: f64 = b.extract()?;
        let d: PyObject = PyComplex::from_doubles(py, real, imag).to_object(py);
        Ok(d)
    }
    
    /// A Python module implemented in Rust.
    #[pymodule]
    fn complex_python(_py: Python, m: &PyModule) -> PyResult<()> {
        m.add_function(wrap_pyfunction!(imaginary, m)?)?;
        Ok(())
    }
    

    Now you can import this into your python interpretter.

    >>> import complex_python
    >>> 
    >>> complex = complex_python.imaginary(1, 2)
    >>> complex
    (1+2j)
    >>> type(_)
    <class 'complex'>