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