Macro rigetti_pyo3::py_wrap_struct

source ·
macro_rules! py_wrap_struct {
    (
        $(#[$meta: meta])*
        $name: ident($rs_from: ty) $(as $py_class: literal)? {
            /// Fallible transformation from Python type `P` to Rust type `T` where `Foo: From<T>`.
            /// Used to implement `TryFrom<P> for PyFoo`. Any errors returned must be `PyErr`.
            ///
            /// $py_for_from should conventionally be `py` -- it is the name of the `Python<'_>` parameter.
            $($py_for_from: ident -> rs {
                $($py_ident: ident: $py_src: ty => $rs_dest: ty $to_rs: block),+
            },)?
            /// Fallible transformation from Rust type `T` to Python type `P` where `T: From<Foo>`
            /// Used to implement `TryFrom<PyFoo> for P`. Any errors returned must be `PyErr`.
            ///
            /// $py_for_to should conventionally be `py` -- it is the name of the `Python<'_>` parameter.
            $(rs -> $py_for_to: ident {
                $($rs_ident: ident: $rs_src: ty => $py_dest: ty $to_py: block),+
            })?
        }
    ) => { ... };
}
Expand description

Create a newtype wrapper for a Rust struct.

Implements the following:

  • Conversion to/from the contained Rust type
  • Conversion to/from the related Python/Rust types
  • Constructor taking any type that can be converted from

§Limitations

This macro generates a __new__ constructor for the Python type from the given py -> rs conversions. This constructor expects exactly one parameter, which cannot be omitted (i.e. has no default value).

To have more control over the constructor, use py_wrap_type with a manual implementation in a pymethods impl block.

§Example

use rigetti_pyo3::py_wrap_struct;
use rigetti_pyo3::pyo3::{Py, PyErr, Python};
use rigetti_pyo3::pyo3::conversion::{IntoPy, PyTryFrom, ToPyObject};
use rigetti_pyo3::pyo3::types::{PyDict, PyTuple};

#[derive(Clone)]
pub struct Foo {
    bar: String,
    baz: f32,
}

impl From<(String, f32)> for Foo {
    fn from(tuple: (String, f32)) -> Self {
        Self { bar: tuple.0, baz: tuple.1 }
    }
}

impl From<Foo> for (String, f32) {
    fn from(foo: Foo) -> Self {
        (foo.bar, foo.baz)
    }
}

py_wrap_struct! {
    PyFoo(Foo) {
        // Fallible transformation from Python type `P` to Rust type `T` where `Foo: From<T>`.
        // Used to implement `TryFrom<P> for PyFoo`. Any errors returned must be `PyErr`.
        py -> rs {
            py_dict: Py<PyDict> => Foo {
                let bar = py_dict.as_ref(py).get_item("bar")?.unwrap().extract().unwrap();
                let baz = py_dict.as_ref(py).get_item("baz")?.unwrap().extract().unwrap();
                Ok::<_, PyErr>(Foo { bar, baz })
            },
            py_tuple: Py<PyTuple> => (String, f32) {
                Ok::<_, PyErr>((
                    py_tuple.as_ref(py).get_item(0)?.extract().unwrap(),
                    py_tuple.as_ref(py).get_item(1)?.extract().unwrap(),
                ))
            }
        },
        // Infallible transformation from Rust type `T` to Python type `P` where `T: From<Foo>`.
        // Used to implement `From<PyFoo> for P`.
        rs -> py {
            rs_tuple: (String, f32) => Py<PyTuple> {
                Python::with_gil(|py| {
                    let obj = rs_tuple.to_object(py);
                    <PyTuple as PyTryFrom>::try_from(obj.as_ref(py))
                        .map(|tuple| tuple.into_py(py))
                        .map_err(PyErr::from)
                })
            }
        }
    }
}