rigetti_pyo3/
lib.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//! Helpful macros and traits for creating a Python wrapper of a Rust library.
16//!
17//! See [Macros](#macros) and [Traits](#traits) for the main items in this crate.
18//!
19//! # Usage
20//!
21//! See the examples directory in the source for example usage of a majority of the crate.
22//!
23//! Alternatively, check the examples on the macros in this documentation.
24
25// Covers correctness, suspicious, style, complexity, and perf
26#![deny(clippy::all)]
27#![deny(clippy::pedantic)]
28#![allow(clippy::module_name_repetitions)]
29#![deny(clippy::cargo)]
30#![allow(clippy::multiple_crate_versions)]
31#![warn(clippy::nursery)]
32// Conflicts with unreachable_pub
33#![allow(clippy::redundant_pub_crate)]
34#![deny(clippy::missing_docs_in_private_items)]
35#![deny(
36    absolute_paths_not_starting_with_crate,
37    anonymous_parameters,
38    bad_style,
39    dead_code,
40    keyword_idents,
41    improper_ctypes,
42    macro_use_extern_crate,
43    meta_variable_misuse,
44    missing_abi,
45    missing_debug_implementations,
46    missing_docs,
47    no_mangle_generic_items,
48    non_shorthand_field_patterns,
49    noop_method_call,
50    overflowing_literals,
51    path_statements,
52    patterns_in_fns_without_body,
53    semicolon_in_expressions_from_macros,
54    trivial_casts,
55    trivial_numeric_casts,
56    unconditional_recursion,
57    unreachable_pub,
58    unsafe_code,
59    unused,
60    unused_allocation,
61    unused_comparisons,
62    unused_extern_crates,
63    unused_import_braces,
64    unused_lifetimes,
65    unused_parens,
66    unused_qualifications,
67    variant_size_differences,
68    while_true
69)]
70
71use pyo3::PyErr;
72
73#[cfg(feature = "time")]
74pub mod datetime;
75mod py_try_from;
76mod sync;
77mod to_python;
78mod traits;
79mod wrappers;
80
81#[cfg(feature = "complex")]
82pub use num_complex;
83pub use paste;
84pub use py_try_from::PyTryFrom;
85pub use pyo3;
86pub use to_python::ToPython;
87
88#[cfg(all(feature = "abi3", feature = "time"))]
89compile_error!(
90    "Cannot enable the time feature with the pyo3/abi3 feature, \
91     but you've asked for rigetti-pyo3 to be abi3-compatible."
92);
93
94/// Implemented by error types generated with [`py_wrap_error`].
95///
96/// Trait-ifies the ability to convert an error into a [`PyErr`](pyo3::PyErr).
97pub trait ToPythonError {
98    /// Convert this error into a [`PyErr`](pyo3::PyErr).
99    fn to_py_err(self) -> PyErr;
100}
101
102impl ToPythonError for PyErr {
103    fn to_py_err(self) -> PyErr {
104        self
105    }
106}
107
108impl ToPythonError for std::convert::Infallible {
109    fn to_py_err(self) -> PyErr {
110        unreachable!("Infallible can never happen")
111    }
112}
113
114/// Implemented by wrapper types generated with `py_wrap_*` macros:
115///
116/// - [`py_wrap_struct`]
117/// - [`py_wrap_union_enum`]
118/// - [`py_wrap_simple_enum`]
119/// - [`py_wrap_type`]
120pub trait PyWrapper: From<Self::Inner> + Into<Self::Inner> + AsRef<Self::Inner> {
121    /// The Rust type being wrapped.
122    type Inner;
123
124    /// Returns a reference to the inner item.
125    ///
126    /// Like [`AsRef`], but doesn't require generics.
127    fn as_inner(&self) -> &Self::Inner {
128        self.as_ref()
129    }
130
131    /// Converts this into the inner item.
132    ///
133    /// Like [`Into`], but doesn't require generics.
134    fn into_inner(self) -> Self::Inner {
135        self.into()
136    }
137}
138
139/// Implemented by wrapper types containing the source type, generated with `py_wrap_*` macros:
140///
141/// - [`py_wrap_struct`]
142/// - [`py_wrap_union_enum`]
143/// - [`py_wrap_type`]
144///
145/// The notable exception is [`py_wrap_simple_enum`], where it does not make sense to have a mutable
146/// reference to a unit enum.
147pub trait PyWrapperMut: PyWrapper + AsMut<Self::Inner> {
148    /// Returns a mutable reference to the inner item.
149    ///
150    /// Like [`AsMut`], but doesn't require generics.
151    fn as_inner_mut(&mut self) -> &mut Self::Inner {
152        self.as_mut()
153    }
154}
155
156impl<T> PyWrapperMut for T where T: PyWrapper + AsMut<Self::Inner> {}
157
158/// Create a crate-private function `init_submodule` to set up this submodule and call the same
159/// function on child modules (which should also use this macro).
160///
161/// This generates boilerplate for exposing classes, exceptions, functions, and child modules to
162/// the Python runtime, including a hack to allow importing from submodules, i.e.:
163///
164/// ```python,ignore
165/// from foo.bar import baz
166/// ```
167///
168/// # Example
169///
170/// ```
171/// use rigetti_pyo3::{py_wrap_type, py_wrap_error, wrap_error, create_init_submodule};
172/// use rigetti_pyo3::pyo3::{pyfunction, pymodule, Python, PyResult, types::PyModule};
173/// use rigetti_pyo3::pyo3::exceptions::PyRuntimeError;
174///
175/// #[pyfunction]
176/// fn do_nothing() {}
177///
178/// py_wrap_type! {
179///     PyCoolString(String) as "CoolString";
180/// }
181///
182/// wrap_error!{
183///     RustIOError(std::io::Error);
184/// }
185///
186/// py_wrap_error!(errors, RustIOError, IOError, PyRuntimeError);
187///
188/// mod my_submodule {
189///     use rigetti_pyo3::{py_wrap_type, create_init_submodule};
190///     
191///     py_wrap_type! {
192///         PyCoolInt(i32) as "CoolInt";
193///     }
194///
195///     create_init_submodule! {
196///         classes: [ PyCoolInt ],
197///     }
198/// }
199///
200/// create_init_submodule! {
201///     /// Initialize this module and all its submodules
202///     classes: [ PyCoolString ],
203///     errors: [ IOError ],
204///     funcs: [ do_nothing ],
205///     submodules: [ "my_submodule": my_submodule::init_submodule ],
206/// }
207///
208/// #[pymodule]
209/// fn example(py: Python<'_>, m: &PyModule) -> PyResult<()> {
210///     init_submodule("example", py, m)
211/// }
212/// ```
213#[macro_export]
214macro_rules! create_init_submodule {
215    (
216        $(#[$meta:meta])*
217        $(classes: [ $($class: ty),+ ],)?
218        $(consts: [ $($const: ident),+ ],)?
219        $(errors: [ $($error: ty),+ ],)?
220        $(funcs: [ $($func: path),+ ],)?
221        $(submodules: [ $($mod_name: literal: $init_submod: path),+ ],)?
222    ) => {
223        $(#[$meta])*
224        pub(crate) fn init_submodule(_name: &str, _py: $crate::pyo3::Python, m: &$crate::pyo3::types::PyModule) -> $crate::pyo3::PyResult<()> {
225            $($(
226            m.add_class::<$class>()?;
227            )+)?
228            $($(
229            m.add(::std::stringify!($const), $crate::ToPython::<$crate::pyo3::Py<$crate::pyo3::PyAny>>::to_python(&$const, _py)?)?;
230            )+)?
231            $($(
232            m.add(std::stringify!($error), _py.get_type::<$error>())?;
233            )+)?
234            $($(
235            m.add_function($crate::pyo3::wrap_pyfunction!($func, m)?)?;
236            )+)?
237            $(
238                let modules = _py.import("sys")?.getattr("modules")?;
239                $(
240                let qualified_name = format!("{}.{}", _name, $mod_name);
241                let submod = $crate::pyo3::types::PyModule::new(_py, &qualified_name)?;
242                $init_submod(&qualified_name, _py, submod)?;
243                m.add($mod_name, submod)?;
244                modules.set_item(&qualified_name, submod)?;
245                )+
246            )?
247            Ok(())
248        }
249    }
250}