macro_rules! fix_complex_enums {
($py:expr, $($name:path),* $(,)?) => { ... };
}Expand description
Fix the __qualname__ on a list of complex enums so that they can be pickled.
The first argument should be a Python<'py> instance;
all others should be names of #[pyclass]-annotated enums with non-unit variants
(aka “complex enums”).
See documentation on fix_enum_qual_names for information on how this works.
§Notes
- You still must implement appropriate methods to enable
picklesupport; because PyO3 adds constructors for the enum variants,__getnewargs__is a great choice. - If you use this macro directly, you should do so after adding the classes to the module,
since the underlying call to
fix_enum_qual_namesmodifies the__qualname__. This should happen in the module initializer. - If you use
create_init_submodule, you can specify classes in thecomplex_enumslist, and it will add the classes and apply the__qualname__fix in the correct order for you.
§Example
The following example demonstrates how you can use this macro to enable pickling complex enums.
For completeness, it shows stub generation and use of the create_init_submodule macro,
but this macro and fix_enum_qual_names can be used without these, if desired.
use pyo3::{prelude::*, types::PyTuple};
use rigetti_pyo3::{create_init_submodule, fix_complex_enums, fix_enum_qual_names};
// Stubs aren't required, but they're compatible with this macro (and nice to have).
#[cfg(feature = "stubs")]
use pyo3_stub_gen::derive::{gen_stub_pyclass_complex_enum, gen_stub_pymethods};
// The easiest way to apply this fix is to simply use the `create_init_submodule` macro
// and specify the classes in the `complex_enums` list:
mod submod {
use rigetti_pyo3::create_init_submodule;
use super::{Foo, Bar};
create_init_submodule! {
complex_enums: [Foo, Bar],
}
}
// If you are setting up the module manually, you can use the function or macro directly...
#[pymodule(name = "mainmod")]
fn main_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
let py = m.py();
// ...but be sure to add your classes to the module before calling either.
m.add_class::<Foo>()?;
m.add_class::<Bar>()?;
submod::init_submodule("submod", py, m)?;
// You can apply the enum `__qualname__` fix via the macro or the function;
// they are functionally equivalent, but the first has a slight performance optimization
// by bundling together some interactions with the Python interpreter.
// Method one (preferred): use the macro and specify a list of complex enum types:
fix_complex_enums!(py, Foo, Bar);
// Method two: manually call `fix_enum_qual_names` for each class:
// fix_enum_qual_names(&py.get_type::<Foo>())?;
// fix_enum_qual_names(&py.get_type::<Bar>())?;
Ok(())
}
#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
#[pyo3::pyclass(module = "mainmod.submod")]
pub enum Foo {
Integer(i64),
Real(f64),
}
#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
#[pyo3::pyclass(module = "mainmod.submod")]
pub enum Bar {
Integer(i64),
Real(f64),
}
// Note that in order to support pickling in general,
// you'll still need `__getnewargs__` or another method used by the `pickle` module.
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Foo {
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Foo::Integer(value) => PyTuple::new(py, [value]),
Foo::Real(value) => PyTuple::new(py, [value]),
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Bar {
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Bar::Integer(value) => PyTuple::new(py, [value]),
Bar::Real(value) => PyTuple::new(py, [value]),
}
}
}