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}