rigetti_pyo3/wrappers.rs
1// Copyright 2022 Rigetti Computing
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Macros for wrapping different Rust types for use in Python.
16
17/// Creates a new exception type and implements converting from the given Rust error to the new
18/// exception.
19///
20/// The Rust error type must at least implement [`ToString`](std::string::ToString). All types
21/// that implement [`Error`](std::error::Error) implement this through
22/// [`Display`](std::fmt::Display).
23///
24///
25/// ```
26/// use rigetti_pyo3::py_wrap_error;
27/// use rigetti_pyo3::pyo3::exceptions::PyValueError;
28/// use std::fmt;
29///
30/// #[derive(Debug)]
31/// enum RustError {}
32///
33/// impl fmt::Display for RustError {
34/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35/// unimplemented!()
36/// }
37/// }
38///
39/// impl std::error::Error for RustError {}
40///
41/// py_wrap_error!(my_python_module, RustError, PythonError, PyValueError);
42/// ```
43#[macro_export]
44macro_rules! py_wrap_error {
45 ($module: ident, $rust: ty, $python: ident, $base: ty) => {
46 $crate::pyo3::create_exception!($module, $python, $base);
47
48 impl $crate::ToPythonError for $rust {
49 fn to_py_err(self) -> $crate::pyo3::PyErr {
50 <$python>::new_err(self.to_string())
51 }
52 }
53 };
54}
55
56/// Create a Python wrapper around a Rust type.
57///
58/// You probably do not want to call this directly, as other macros build on top of this.
59///
60/// Implements:
61/// - Conversion between wrapper and inner Rust type
62/// - `AsRef` to access the inner Rust type from [`pyo3`](crate::pyo3) code.
63/// - [`PyWrapper`](crate::PyWrapper) as non-generic aliases for the above
64/// - [`ToPyObject`](crate::pyo3::conversion::ToPyObject)
65///
66/// # Macro inputs:
67///
68/// - `$meta`: Any attributes to apply to the wrapper type. Supports `#[pyo3(...)]`
69/// for configuring the Python type.
70/// - `$name`: The Rust name for the wrapper type (usually `PySomething`).
71/// - `$from`: The Rust type to wrap.
72/// - `$py_alias` (optional): The type name to expose to Python (usually `$name` without a leading `Py`).
73///
74/// ```
75/// use std::collections::HashMap;
76/// use rigetti_pyo3::py_wrap_type;
77///
78/// py_wrap_type! {
79/// #[derive(Debug)]
80/// PyNumberLabels(HashMap<String, i32>) as "NumberLabels";
81/// }
82///
83/// let map = HashMap::new();
84/// let dict = PyNumberLabels::from(map);
85/// let map = HashMap::from(dict);
86/// let dict = PyNumberLabels::from(&map);
87/// assert_eq!(&map, dict.as_ref());
88/// ```
89#[macro_export]
90macro_rules! py_wrap_type {
91 (
92 $(#[$meta: meta])*
93 $name: ident($from: ty)$(as $py_alias: literal)?$(;)?
94 ) => {
95 #[repr(transparent)]
96 #[allow(clippy::use_self)]
97 #[$crate::pyo3::pyclass$((name = $py_alias))?]
98 #[derive(Clone)]
99 $(#[$meta])*
100 pub struct $name($from);
101
102 impl $crate::PyTryFrom<$name> for $from {
103 fn py_try_from(
104 py: $crate::pyo3::Python,
105 item: &$name,
106 ) -> $crate::pyo3::PyResult<Self> {
107 Ok(item.0.clone())
108 }
109 }
110
111 impl $crate::PyTryFrom<$crate::pyo3::PyAny> for $name {
112 fn py_try_from(
113 py: $crate::pyo3::Python,
114 item: &$crate::pyo3::PyAny,
115 ) -> $crate::pyo3::PyResult<Self> {
116 item.extract()
117 }
118 }
119
120 impl $crate::PyTryFrom<$name> for $name {
121 fn py_try_from(
122 py: $crate::pyo3::Python,
123 item: &$name,
124 ) -> $crate::pyo3::PyResult<Self> {
125 Ok(item.clone())
126 }
127 }
128
129 $crate::private_impl_to_python_with_reference!(&self, py, $from => $name {
130 Ok($name::from(self.clone()))
131 });
132
133 $crate::private_impl_to_python_with_reference!(&self, py, $name => $crate::pyo3::Py<$crate::pyo3::PyAny> {
134 Ok(<Self as $crate::pyo3::ToPyObject>::to_object(self, py))
135 });
136
137 impl From<$name> for $from {
138 fn from(wrapper: $name) -> Self {
139 wrapper.0
140 }
141 }
142
143 impl From<$from> for $name {
144 fn from(inner: $from) -> Self {
145 Self(inner)
146 }
147 }
148
149 impl From<&$from> for $name {
150 fn from(inner: &$from) -> Self {
151 Self(inner.clone())
152 }
153 }
154
155 impl AsRef<$from> for $name {
156 fn as_ref(&self) -> &$from {
157 &self.0
158 }
159 }
160
161 impl $crate::PyWrapper for $name {
162 type Inner = $from;
163 }
164
165 impl $crate::pyo3::conversion::ToPyObject for $name {
166 fn to_object(&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyObject {
167 #[allow(clippy::use_self)]
168 const NAME: &'static str = stringify!($name);
169 let cell = $crate::pyo3::PyCell::new(py, self.clone())
170 .unwrap_or_else(|err| {
171 panic!(
172 "failed to create {} on Python heap: {}",
173 NAME,
174 err
175 )
176 });
177 $crate::pyo3::conversion::ToPyObject::to_object(&cell, py)
178 }
179 }
180 };
181}
182
183/// Wrap an enum containing only unit variants.
184///
185/// Implements
186///
187/// - Conversion between Rust and Python types (also converting from references to each)
188///
189/// # Macro Inputs
190///
191/// - `$variant_name`: comma-separated list of variant names on the Rust enum. Required because
192/// there is no way to do reflection to programmatically find them.
193/// - `$variant_alias`: used in conjunction with `$variant_name` as the Python enum member name
194/// when it should be named differently, useful in cases when the enum member name is not a
195/// valid python identifier. If one variant uses an alias, they all must, even if the alias
196/// is the same as the name.
197/// - See also [`py_wrap_type`].
198///
199/// # Example
200///
201/// ```
202/// use rigetti_pyo3::py_wrap_simple_enum;
203///
204/// #[derive(Copy, Clone)]
205/// pub enum RustEnum {
206/// Foo,
207/// Bar,
208/// }
209///
210/// py_wrap_simple_enum! {
211/// PyEnum(RustEnum) {
212/// Foo,
213/// Bar
214/// }
215/// }
216///
217/// py_wrap_simple_enum! {
218/// PyEnumAliased(RustEnum) {
219/// Foo as FOO,
220/// Bar as Bar
221/// }
222/// }
223/// ```
224#[macro_export]
225macro_rules! py_wrap_simple_enum {
226 (
227 $(#[$meta: meta])*
228 $name: ident($rs_inner: ident) $(as $py_class: literal)? {
229 $($variant_name: ident),+
230 }
231 ) => {
232 $crate::py_wrap_simple_enum! {
233 $(#[$meta])*
234 $name($rs_inner) $(as $py_class)? {
235 $($variant_name as $variant_name),+
236 }
237 }
238 };
239 (
240 $(#[$meta: meta])*
241 $name: ident($rs_inner: ident) $(as $py_class: literal)? {
242 $($variant_name: ident as $variant_alias: ident),+
243 }
244 ) => {
245 #[derive(Copy, Clone)]
246 #[$crate::pyo3::pyclass$((name = $py_class))?]
247 $(#[$meta])*
248 pub enum $name {
249 $(
250 $variant_alias
251 ),+
252 }
253
254 impl From<$name> for $rs_inner {
255 fn from(item: $name) -> Self {
256 match item {
257 $(
258 $name::$variant_alias => Self::$variant_name,
259 )+
260 }
261 }
262 }
263
264 impl From<&$name> for $rs_inner {
265 fn from(item: &$name) -> Self {
266 Self::from(*item)
267 }
268 }
269
270 impl From<$rs_inner> for $name {
271 fn from(item: $rs_inner) -> Self {
272 match item {
273 $(
274 $rs_inner::$variant_name => $name::$variant_alias,
275 )+
276 }
277 }
278 }
279
280 impl From<&$rs_inner> for $name {
281 fn from(item: &$rs_inner) -> Self {
282 Self::from(*item)
283 }
284 }
285
286 impl $crate::PyWrapper for $name {
287 type Inner = $rs_inner;
288 }
289
290 impl AsRef<$rs_inner> for $name {
291 fn as_ref(&self) -> &$rs_inner {
292 match self {
293 $(
294 $name::$variant_alias => &$rs_inner::$variant_name,
295 )+
296 }
297 }
298 }
299
300 impl $crate::pyo3::conversion::ToPyObject for $name {
301 fn to_object(&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyObject {
302 let cell = $crate::pyo3::PyCell::new(py, self.clone())
303 .unwrap_or_else(|err| panic!("failed to create {} on Python heap: {}", stringify!($name), err));
304 cell.to_object(py)
305 }
306 }
307
308 $crate::private_impl_to_python_with_reference!(&self, _py, $rs_inner => $name {
309 Ok($name::from(self))
310 });
311
312 $crate::private_impl_py_try_from!(&item, _py, $name => $rs_inner {
313 Ok(*item.as_ref())
314 });
315 }
316}
317
318/// Create a newtype wrapper for a Rust struct.
319///
320/// Implements the following:
321///
322/// - Conversion to/from the contained Rust type
323/// - Conversion to/from the related Python/Rust types
324/// - Constructor taking any type that can be converted from
325///
326/// # Limitations
327///
328/// This macro generates a `__new__` constructor for the Python type from the given
329/// `py -> rs` conversions. This constructor expects exactly one parameter, which cannot
330/// be omitted (i.e. has no default value).
331///
332/// To have more control over the constructor, use [`py_wrap_type`] with a manual
333/// implementation in a `pymethods` `impl` block.
334///
335/// # Example
336///
337/// ```
338/// use rigetti_pyo3::py_wrap_struct;
339/// use rigetti_pyo3::pyo3::{Py, PyErr, Python};
340/// use rigetti_pyo3::pyo3::conversion::{IntoPy, PyTryFrom, ToPyObject};
341/// use rigetti_pyo3::pyo3::types::{PyDict, PyTuple};
342///
343/// #[derive(Clone)]
344/// pub struct Foo {
345/// bar: String,
346/// baz: f32,
347/// }
348///
349/// impl From<(String, f32)> for Foo {
350/// fn from(tuple: (String, f32)) -> Self {
351/// Self { bar: tuple.0, baz: tuple.1 }
352/// }
353/// }
354///
355/// impl From<Foo> for (String, f32) {
356/// fn from(foo: Foo) -> Self {
357/// (foo.bar, foo.baz)
358/// }
359/// }
360///
361/// py_wrap_struct! {
362/// PyFoo(Foo) {
363/// // Fallible transformation from Python type `P` to Rust type `T` where `Foo: From<T>`.
364/// // Used to implement `TryFrom<P> for PyFoo`. Any errors returned must be `PyErr`.
365/// py -> rs {
366/// py_dict: Py<PyDict> => Foo {
367/// let bar = py_dict.as_ref(py).get_item("bar")?.unwrap().extract().unwrap();
368/// let baz = py_dict.as_ref(py).get_item("baz")?.unwrap().extract().unwrap();
369/// Ok::<_, PyErr>(Foo { bar, baz })
370/// },
371/// py_tuple: Py<PyTuple> => (String, f32) {
372/// Ok::<_, PyErr>((
373/// py_tuple.as_ref(py).get_item(0)?.extract().unwrap(),
374/// py_tuple.as_ref(py).get_item(1)?.extract().unwrap(),
375/// ))
376/// }
377/// },
378/// // Infallible transformation from Rust type `T` to Python type `P` where `T: From<Foo>`.
379/// // Used to implement `From<PyFoo> for P`.
380/// rs -> py {
381/// rs_tuple: (String, f32) => Py<PyTuple> {
382/// Python::with_gil(|py| {
383/// let obj = rs_tuple.to_object(py);
384/// <PyTuple as PyTryFrom>::try_from(obj.as_ref(py))
385/// .map(|tuple| tuple.into_py(py))
386/// .map_err(PyErr::from)
387/// })
388/// }
389/// }
390/// }
391/// }
392/// ```
393#[macro_export]
394macro_rules! py_wrap_struct {
395 (
396 $(#[$meta: meta])*
397 $name: ident($rs_from: ty) $(as $py_class: literal)? {
398 /// Fallible transformation from Python type `P` to Rust type `T` where `Foo: From<T>`.
399 /// Used to implement `TryFrom<P> for PyFoo`. Any errors returned must be `PyErr`.
400 ///
401 /// $py_for_from should conventionally be `py` -- it is the name of the `Python<'_>` parameter.
402 $($py_for_from: ident -> rs {
403 $($py_ident: ident: $py_src: ty => $rs_dest: ty $to_rs: block),+
404 },)?
405 /// Fallible transformation from Rust type `T` to Python type `P` where `T: From<Foo>`
406 /// Used to implement `TryFrom<PyFoo> for P`. Any errors returned must be `PyErr`.
407 ///
408 /// $py_for_to should conventionally be `py` -- it is the name of the `Python<'_>` parameter.
409 $(rs -> $py_for_to: ident {
410 $($rs_ident: ident: $rs_src: ty => $py_dest: ty $to_py: block),+
411 })?
412 }
413 ) => {
414 $crate::py_wrap_type! {
415 $(
416 #[$meta]
417 )*
418 $name($rs_from) $(as $py_class)?;
419 }
420
421 $($(
422 impl TryFrom<$py_src> for $name {
423 #[allow(unused_qualifications)]
424 type Error = pyo3::PyErr;
425 fn try_from($py_ident: $py_src) -> Result<Self, Self::Error> {
426 $crate::pyo3::Python::with_gil(|$py_for_from| {
427 let rust = {
428 $to_rs
429 }?;
430 Ok(Self::from(<$rs_from>::from(rust)))
431 })
432 }
433 }
434 )+)?
435
436 $($(
437 impl TryFrom<$name> for $py_dest {
438 #[allow(unused_qualifications)]
439 type Error = pyo3::PyErr;
440 fn try_from(outer: $name) -> Result<Self, Self::Error> {
441 let $rs_ident = $crate::PyWrapper::into_inner(outer);
442 let $rs_ident: $rs_src = From::from($rs_ident);
443 $crate::pyo3::Python::with_gil(|$py_for_to| {
444 $to_py
445 })
446 }
447 }
448 )+)?
449
450 $crate::impl_as_mut_for_wrapper!($name);
451
452 #[$crate::pyo3::pymethods]
453 impl $name {
454 #![allow(clippy::use_self)]
455
456 #[doc = concat!(
457 r"Create a new [`",
458 stringify!($name),
459 r"`] from Python arguments; corresponds to `",
460 $($py_class, r".",)?
461 r"__new__()` in Python"
462 )]
463 #[new]
464 pub fn new(py: $crate::pyo3::Python, input: $crate::pyo3::Py<$crate::pyo3::PyAny>) -> $crate::pyo3::PyResult<Self> {
465 use $crate::pyo3::FromPyObject;
466
467 $($(
468 if let Ok(item) = input.extract::<$py_src>(py) {
469 return Self::try_from(item);
470 }
471 )+)?
472
473 Err($crate::pyo3::exceptions::PyValueError::new_err(
474 concat!("expected one of:" $($(, " ", std::stringify!($py_src))+)?)
475 ))
476 }
477 }
478 }
479}
480
481/// (Internal) Helper macro to get the final type in a chain of conversions.
482///
483/// Necessary because the pattern `$(=> $foo: ty)* => $bar: ty` is ambiguous.
484#[macro_export]
485macro_rules! private_ultimate_type {
486 ($type: ty) => { $type };
487 ($type: ty, $($others: ty),+) => { $crate::private_ultimate_type!($($others),+) }
488}
489
490/// (Internal) Helper macro to implement chained conversion through intermediate types,
491/// where the type system cannot determine a path from the first to last item.
492#[macro_export]
493macro_rules! private_intermediate_to_python {
494 ($py: ident, &$item: ident $(=> $convert: ty)+) => {{
495 $(
496 let $item: $convert = $crate::ToPython::<$convert>::to_python(&$item, $py)?;
497 )+
498 Ok::<_, $crate::pyo3::PyErr>($item)
499 }}
500}
501
502/// (Internal) Helper macro to implement chained conversion through intermediate types,
503/// where the type system cannot determine a path from the last to first item.
504#[macro_export]
505macro_rules! private_intermediate_try_from_python {
506 ($py: ident, &$item: ident => $convert: ty $($(=> $delayed: ty)+)?) => {{
507 $(let $item: $convert = $crate::private_intermediate_try_from_python!($py, &$item $(=> $delayed)+)?;
508 let $item = &$item;)?
509 <_ as $crate::PyTryFrom<$convert>>::py_try_from($py, $item)
510 }};
511}
512
513/// Create a newtype wrapper for a Rust enum with unique 1-tuple variants.
514///
515/// # Implements
516///
517/// - Conversion between the wrapper and the inner enum
518/// - A Python constructor that creates a new instance from one of the Python variants.
519/// - A Python function `inner()` that directly returns the Python version of the variant
520/// discriminant (i.e. `Discriminant` in `Enum::Variant(Discriminant)`).
521/// - Python conversion functions:
522/// - `from_x`: Like the constructor, but for a specific variant `x`.
523/// - `is_x`: Returns `True` if the enum is variant `x`.
524/// - `as_x`: Returns the discriminant if the enum is variant `x`, otherwise `None`.
525/// - `to_x`: Returns the discriminant if the enum is variant `x`, otherwise raises
526/// `ValueError`.
527///
528/// # Example
529///
530/// ```
531/// use rigetti_pyo3::py_wrap_union_enum;
532/// use rigetti_pyo3::pyo3::prelude::*;
533/// use rigetti_pyo3::pyo3::types::*;
534/// use std::collections::HashSet;
535///
536/// #[derive(Clone)]
537/// pub enum TestEnum {
538/// Unit,
539/// String(String),
540/// Integer(i32),
541/// UInteger(u32),
542/// List(Vec<HashSet<String>>),
543/// Mapping(std::collections::HashMap<String, String>),
544/// }
545///
546/// py_wrap_union_enum! {
547/// PyTestEnum(TestEnum) as "TestEnum" {
548/// // Syntax is (1): (2) [=> (3)] [=> (4)] [...], where:
549/// // 1: The name used in generated methods
550/// // 2: The name of the Rust enum variant
551/// // 3: The (Python) type the inner item must convert to (if it has associated data)
552/// // 4: The (Python) type the type from (3) must convert to, etc.
553/// unit: Unit,
554/// // Read as "give the name `string` to variant `String`, which must convert (from
555/// // a `String`) to a `String` and then to a `Py<PyString>`."
556/// //
557/// // That is, `string` is used to generate methods `is_string`, `from_string`, etc.;
558/// // the first `String` is the name of the variant, not the type (which is elided);
559/// // the second `String` is the type to convert the elided type into, and `Py<String>` is
560/// // the final type to convert into.
561/// //
562/// // This specifies an unnecessary conversion from String => String to illustrate
563/// // conversion chaining.
564/// string: String => String => Py<PyString>,
565/// int: Integer => Py<PyInt>,
566/// uint: UInteger => Py<PyInt>,
567/// list: List => Py<PyList>,
568/// // Alternatively, in the case of `Vec<T>` where `T` does not have conversion to `PyAny`.
569/// // list: List => Vec<Py<PySet>> => Py<PyList>,
570/// // Generates `from_dict`, `is_dict`, `as_dict`, `to_dict`
571/// dict: Mapping => Py<PyDict>
572/// }
573/// }
574/// ```
575#[macro_export]
576macro_rules! py_wrap_union_enum {
577 // @from creates its own impl block to avoid an error of "cannot find attribute `staticmethod`
578 // in this scope".
579 //
580 // There may be a performance hit to this, but I (@Shadow53) cannot figure out how to do this
581 // otherwise without rewriting everything as procedural macros.
582 //
583 // Note: the cause of the error was the use of `paste!` *within* a `pymethods` impl block.
584 // Having the impl block within the `paste!` macro is what makes the error go away.
585 (@from $name: ident, $rs_enum: ident, $variant_name: ident, $variant: ident $(=> $convert: ty)+) => {
586 $crate::paste::paste! {
587 #[$crate::pyo3::pymethods]
588 impl $name {
589 #[doc = concat!(
590 r"The Python wrapper for [`",
591 stringify!($rs_enum),
592 r"::",
593 stringify!($variant),
594 r"`], creating a [`",
595 stringify!($name),
596 r"`] and taking a Python argument."
597 )]
598 #[staticmethod]
599 pub fn [< from_ $variant_name >](py: $crate::pyo3::Python, inner: $crate::private_ultimate_type!($($convert),+)) -> $crate::pyo3::PyResult<Self> {
600 let inner = &inner;
601 $crate::private_intermediate_try_from_python!(py, &inner $(=> $convert)+)
602 .map($rs_enum::$variant)
603 .map(Self)
604 }
605 }
606 }
607 };
608 (@from $name: ident, $rs_enum: ident, $variant_name: ident, $variant: ident) => {
609 $crate::paste::paste! {
610 #[$crate::pyo3::pymethods]
611 impl $name {
612 #[doc = concat!(
613 r"Create a new [`", stringify!($name), r"`] wrapping a ",
614 r"[`", stringify!($rs_enum), r"::", stringify!($variant), "`]."
615 )]
616 #[staticmethod]
617 pub fn [< new_ $variant_name >]() -> Self {
618 Self::from($rs_enum::$variant)
619 }
620 }
621 }
622 };
623 (@is_variant $self: ident, $rs_enum: ident, $variant: ident ($(=> $_convert: ty)+)) => {
624 match &$self.0 {
625 $rs_enum::$variant(_) => true,
626 _ => false,
627 }
628 };
629 (@is_variant $self: ident, $rs_enum: ident, $variant: ident) => {
630 match &$self.0 {
631 $rs_enum::$variant => true,
632 _ => false,
633 }
634 };
635 (
636 $(#[$meta: meta])*
637 $name: ident($rs_inner: ident) $(as $py_class: literal)? {
638 $($variant_name: ident: $variant: ident $($(=> $convert: ty)+)?),+
639 }
640 ) => {
641 $crate::py_wrap_type! {
642 $(#[$meta])*
643 $name($rs_inner) $(as $py_class)?;
644 }
645
646 $crate::impl_as_mut_for_wrapper!($name);
647
648 $(
649 $crate::py_wrap_union_enum!(@from $name, $rs_inner, $variant_name, $variant $($(=> $convert)+)?);
650 )+
651
652 $crate::paste::paste! {
653 #[$crate::pyo3::pymethods]
654 impl $name {
655 #[doc = concat!(
656 r"Create a new [`",
657 stringify!($name),
658 r"`] from a Python argument; corresponds to `",
659 $($py_class, r".",)?
660 r"__new__()` in Python"
661 )]
662 #[new]
663 pub fn new(py: $crate::pyo3::Python, input: &$crate::pyo3::PyAny) -> $crate::pyo3::PyResult<Self> {
664 $(
665 $(
666 if let Ok(inner) = <_ as $crate::PyTryFrom<$crate::pyo3::PyAny>>::py_try_from(py, input) {
667 let inner = &inner;
668 let converted = $crate::private_intermediate_try_from_python!(py, &inner $(=> $convert)+);
669 if let Ok(item) = converted {
670 return Ok(Self::from($rs_inner::$variant(item)));
671 }
672 }
673 )?
674 )+
675
676 Err($crate::pyo3::exceptions::PyValueError::new_err(
677 format!(
678 "could not create {} from {}",
679 stringify!($name),
680 input.repr()?
681 )
682 ))
683 }
684
685 #[doc = concat!(
686 r"Directly return the Python version of the variant discriminant wrapped by this ",
687 r"value; ",
688 r"i.e., performs the match `",
689 stringify!($rs_inner),
690 r"::Variant(x) => x` for every variant constructor in [`",
691 stringify!($rs_inner),
692 r"`]"
693 )]
694 #[allow(unreachable_code, unreachable_pattern)]
695 pub fn inner(&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyResult<$crate::pyo3::Py<$crate::pyo3::PyAny>> {
696 match &self.0 {
697 $(
698 $($rs_inner::$variant(inner) => {
699 Ok($crate::pyo3::conversion::IntoPy::<$crate::pyo3::Py<$crate::pyo3::PyAny>>::into_py(
700 $crate::private_intermediate_to_python!(py, &inner $(=> $convert)+)?,
701 py,
702 ))
703 },)?
704 )+
705 _ => {
706 use $crate::pyo3::exceptions::PyRuntimeError;
707 Err(PyRuntimeError::new_err("Enum variant has no inner data or is unimplemented"))
708 }
709 }
710 }
711
712 $(
713 #[doc = concat!(
714 r"Tests if this [`", stringify!($name), r"`] ",
715 r"wraps a [`", stringify!($rs_inner), r"::", stringify!($variant_name), "`] value"
716 )]
717 const fn [< is_ $variant_name >](&self) -> bool {
718 $crate::py_wrap_union_enum!(@is_variant self, $rs_inner, $variant $(($(=> $convert)+))?)
719 }
720
721 $(
722 #[doc = concat!(
723 r"Returns `x` if this [`", stringify!($name), r"`] ",
724 r"wraps a `", stringify!($rs_inner), r"::", stringify!($variant_name), "`(x); ",
725 r"otherwise returns (Python) `None`. On the Rust side, this corresponds to ",
726 r"either `Some(x)` or [`None`]."
727 )]
728 fn [< as_ $variant_name >](&self, py: $crate::pyo3::Python) -> Option<$crate::private_ultimate_type!($($convert),+)> {
729 self.[< to_ $variant_name >](py).ok()
730 }
731
732 #[doc = concat!(
733 r"Returns `x` if this [`", stringify!($name), r"`] ",
734 r"wraps a `", stringify!($rs_inner), r"::", stringify!($variant_name), "`(x); ",
735 r"otherwise raises a `ValueError`. On the Rust side, this corresponds to ",
736 r"either `Ok(x)` or `Err(...)`."
737 )]
738 fn [< to_ $variant_name >](&self, py: $crate::pyo3::Python) -> $crate::pyo3::PyResult<$crate::private_ultimate_type!($($convert),+)> {
739 if let $rs_inner::$variant(inner) = &self.0 {
740 $crate::private_intermediate_to_python!(py, &inner $(=> $convert)+)
741 } else {
742 Err($crate::pyo3::exceptions::PyValueError::new_err(
743 concat!("expected self to be a ", stringify!($variant_name))
744 ))
745 }
746 }
747 )?
748 )+
749 }
750 }
751 }
752}
753
754/// Wraps an external error type in a newtype `struct` so it can be used with [`py_wrap_error`].
755///
756/// # Implements
757///
758/// - [`From`] impls between the newtype and the inner type.
759/// - [`Display`](std::fmt::Display) delegating to the inner type
760/// - [`Error`](std::error::Error)
761///
762/// # Example
763///
764/// ```
765/// use rigetti_pyo3::{wrap_error, py_wrap_error};
766/// use rigetti_pyo3::pyo3::exceptions::PyRuntimeError;
767///
768/// wrap_error!{
769/// RustIOError(std::io::Error);
770/// }
771///
772/// py_wrap_error!(errors, RustIOError, IOError, PyRuntimeError);
773/// ```
774#[macro_export]
775macro_rules! wrap_error {
776 ($(#[$meta: meta])* $name: ident ($inner: ty)$(;)?) => {
777 $(#[$meta])*
778 #[derive(Debug)]
779 #[repr(transparent)]
780 pub struct $name($inner);
781
782 impl From<$inner> for $name {
783 fn from(inner: $inner) -> Self {
784 Self(inner)
785 }
786 }
787
788 impl From<$name> for $inner {
789 fn from(outer: $name) -> Self {
790 outer.0
791 }
792 }
793
794 impl ::std::fmt::Display for $name {
795 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
796 write!(f, "{}", self.0)
797 }
798 }
799
800 impl ::std::error::Error for $name {}
801 };
802}
803
804/// Wraps a data struct and makes (some of) its fields available to Python.
805///
806/// # Implements
807///
808/// - Everything implemented by [`py_wrap_type`].
809/// - [`PyWrapperMut`](crate::PyWrapperMut).
810/// - `get_foo` and `set_foo` methods for field `foo`, which translate to `@property` and
811/// `@foo.setter` in Python, i.e. allowing access to the field as a property.
812///
813/// # Warning!
814///
815/// The mutability of exposed fields may not work as you expect.
816///
817/// Since objects are converted back and forth along the FFI boundary using `Clone`s,
818/// pointers are not shared like in native Python. In native Python, this code runs
819/// without issue:
820///
821/// ```python
822/// class Test:
823/// def __init__(self):
824/// self.inner = {}
825///
826/// @property
827/// def foo(self):
828/// return self.inner
829///
830/// @foo.setter
831/// def foo(self, value):
832/// self.inner = value
833///
834/// c = Test()
835/// d = c.inner
836///
837/// d["a"] = ["a"]
838/// assert "a" in c.inner
839///
840/// d = c.foo
841/// d["b"] = "b"
842/// assert "b" in c.inner
843/// ```
844///
845/// Using these bindings, assuming that this macro was used to create `Test`, the
846/// equivalent would be:
847///
848/// ```python
849/// c = Test()
850/// d = c.foo
851///
852/// d["a"] = ["a"]
853/// assert "a" not in c.foo
854/// c.foo = d
855/// assert "a" in c.foo
856/// ```
857///
858/// # Example
859///
860/// ```
861/// use rigetti_pyo3::pyo3::{Py, types::{PyInt, PyString}};
862/// use rigetti_pyo3::py_wrap_data_struct;
863///
864/// #[derive(Clone)]
865/// pub struct Person {
866/// pub name: String,
867/// pub age: u8,
868/// }
869///
870/// py_wrap_data_struct! {
871/// PyPerson(Person) as "Person" {
872/// name: String => Py<PyString>,
873/// age: u8 => Py<PyInt>
874/// }
875/// }
876/// ```
877#[macro_export]
878macro_rules! py_wrap_data_struct {
879 (
880 $(#[$meta: meta])*
881 $name: ident($rs_inner: ty) $(as $class_name: literal)? {
882 $(
883 $field_name: ident: $field_rs_type: ty $(=> $convert: ty)+
884 ),+
885 }
886 ) => {
887 $crate::py_wrap_type! {
888 $(
889 #[$meta]
890 )*
891 $name($rs_inner) $(as $class_name)?;
892 }
893
894 $crate::impl_as_mut_for_wrapper!($name);
895
896 $crate::paste::paste! {
897 #[rigetti_pyo3::pyo3::pymethods]
898 impl $name {
899 $(
900 #[doc = concat!(
901 r"Get the ", stringify!($field_name), r" field from Python. ",
902 r"Annotated with `@property`."
903 )]
904 #[getter]
905 fn [< get_ $field_name >](&self, py: $crate::pyo3::Python<'_>) -> $crate::pyo3::PyResult<$crate::private_ultimate_type!($($convert),+)> {
906 use $crate::{PyWrapper, ToPython};
907 let inner = &self.as_inner().$field_name;
908 $crate::private_intermediate_to_python!(py, &inner $(=> $convert)+)
909 }
910
911 #[doc = concat!(
912 r"Set the ", stringify!($field_name), r" field from Python. ",
913 r"Annotated with `@", stringify!($field_name), r".setter`."
914 )]
915 #[setter]
916 fn [< set_ $field_name >](&mut self, py: $crate::pyo3::Python<'_>, from: $crate::private_ultimate_type!($($convert),+)) -> $crate::pyo3::PyResult<()> {
917 use $crate::{PyTryFrom, PyWrapperMut};
918 let from = &from;
919 let new_val: $field_rs_type = $crate::private_intermediate_try_from_python!(py, &from $(=> $convert)+)?;
920 self.as_inner_mut().$field_name = new_val;
921 Ok(())
922 }
923 )+
924 }
925 }
926 };
927}