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}