Skip to main content

pyo3/impl_/
callback.rs

1//! Utilities for a Python callable object that invokes a Rust function.
2
3use crate::err::{PyErr, PyResult};
4use crate::exceptions::PyOverflowError;
5use crate::ffi::{self, Py_hash_t};
6use crate::{BoundObject, IntoPyObject, Py, PyAny, Python};
7use core::ffi::c_int;
8
9/// A type which can be the return type of a python C-API callback
10pub trait PyCallbackOutput: Copy + py_callback_output::Sealed {
11    /// The error value to return to python if the callback raised an exception
12    const ERR_VALUE: Self;
13}
14
15/// Seals `PyCallbackOutput` so that types outside PyO3 cannot implement it.
16mod py_callback_output {
17    use core::ffi::c_int;
18
19    use pyo3_ffi::Py_ssize_t;
20
21    use crate::ffi::PyObject;
22
23    pub trait Sealed {}
24
25    impl Sealed for *mut PyObject {}
26    impl Sealed for c_int {}
27    impl Sealed for Py_ssize_t {}
28}
29
30impl PyCallbackOutput for *mut ffi::PyObject {
31    const ERR_VALUE: Self = core::ptr::null_mut();
32}
33
34impl PyCallbackOutput for core::ffi::c_int {
35    const ERR_VALUE: Self = -1;
36}
37
38impl PyCallbackOutput for ffi::Py_ssize_t {
39    const ERR_VALUE: Self = -1;
40}
41
42/// Convert the result of callback function into the appropriate return value.
43pub trait IntoPyCallbackOutput<'py, Target>: into_py_callback_output::Sealed<'py, Target> {
44    fn convert(self, py: Python<'py>) -> PyResult<Target>;
45}
46
47/// Seals `IntoPyCallbackOutput` so that types outside PyO3 cannot implement it.
48mod into_py_callback_output {
49    use pyo3_ffi::Py_hash_t;
50
51    use crate::{
52        ffi,
53        impl_::callback::{HashCallbackOutput, IntoPyCallbackOutput, WrappingCastTo},
54        IntoPyObject, Py, PyAny, PyErr,
55    };
56
57    pub trait Sealed<'py, Target> {}
58
59    impl<'py, T: IntoPyObject<'py>> Sealed<'py, *mut ffi::PyObject> for T {}
60    impl<'py, T: IntoPyCallbackOutput<'py, U>, E: Into<PyErr>, U> Sealed<'py, U> for Result<T, E> {}
61    impl Sealed<'_, Self> for *mut ffi::PyObject {}
62    impl Sealed<'_, core::ffi::c_int> for () {}
63    impl Sealed<'_, core::ffi::c_int> for bool {}
64    impl Sealed<'_, ()> for () {}
65    impl Sealed<'_, ffi::Py_ssize_t> for usize {}
66    impl Sealed<'_, bool> for bool {}
67    impl Sealed<'_, usize> for usize {}
68    impl<'py, T: IntoPyObject<'py>> Sealed<'py, Py<PyAny>> for T {}
69    impl Sealed<'_, Py_hash_t> for HashCallbackOutput {}
70    impl<T: WrappingCastTo<Py_hash_t>> Sealed<'_, HashCallbackOutput> for T {}
71}
72
73impl<'py, T, E, U> IntoPyCallbackOutput<'py, U> for Result<T, E>
74where
75    T: IntoPyCallbackOutput<'py, U>,
76    E: Into<PyErr>,
77{
78    #[inline]
79    fn convert(self, py: Python<'py>) -> PyResult<U> {
80        match self {
81            Ok(v) => v.convert(py),
82            Err(e) => Err(e.into()),
83        }
84    }
85}
86
87impl<'py, T> IntoPyCallbackOutput<'py, *mut ffi::PyObject> for T
88where
89    T: IntoPyObject<'py>,
90{
91    #[inline]
92    fn convert(self, py: Python<'py>) -> PyResult<*mut ffi::PyObject> {
93        self.into_pyobject(py)
94            .map(BoundObject::into_ptr)
95            .map_err(Into::into)
96    }
97}
98
99impl IntoPyCallbackOutput<'_, Self> for *mut ffi::PyObject {
100    #[inline]
101    fn convert(self, _: Python<'_>) -> PyResult<Self> {
102        Ok(self)
103    }
104}
105
106impl IntoPyCallbackOutput<'_, core::ffi::c_int> for () {
107    #[inline]
108    fn convert(self, _: Python<'_>) -> PyResult<core::ffi::c_int> {
109        Ok(0)
110    }
111}
112
113impl IntoPyCallbackOutput<'_, core::ffi::c_int> for bool {
114    #[inline]
115    fn convert(self, _: Python<'_>) -> PyResult<core::ffi::c_int> {
116        Ok(self as c_int)
117    }
118}
119
120impl IntoPyCallbackOutput<'_, ()> for () {
121    #[inline]
122    fn convert(self, _: Python<'_>) -> PyResult<()> {
123        Ok(())
124    }
125}
126
127impl IntoPyCallbackOutput<'_, ffi::Py_ssize_t> for usize {
128    #[inline]
129    fn convert(self, _py: Python<'_>) -> PyResult<ffi::Py_ssize_t> {
130        self.try_into().map_err(|_err| PyOverflowError::new_err(()))
131    }
132}
133
134// Conversion traits needed by pyo3's macros
135
136impl IntoPyCallbackOutput<'_, bool> for bool {
137    #[inline]
138    fn convert(self, _: Python<'_>) -> PyResult<bool> {
139        Ok(self)
140    }
141}
142
143impl IntoPyCallbackOutput<'_, usize> for usize {
144    #[inline]
145    fn convert(self, _: Python<'_>) -> PyResult<usize> {
146        Ok(self)
147    }
148}
149
150impl<'py, T> IntoPyCallbackOutput<'py, Py<PyAny>> for T
151where
152    T: IntoPyObject<'py>,
153{
154    #[inline]
155    fn convert(self, py: Python<'py>) -> PyResult<Py<PyAny>> {
156        self.into_pyobject(py)
157            .map(BoundObject::into_any)
158            .map(BoundObject::unbind)
159            .map_err(Into::into)
160    }
161}
162
163pub trait WrappingCastTo<T>: wrapping_cast_to::Sealed<T> {
164    fn wrapping_cast(self) -> T;
165}
166
167/// Seals `WrappingCastTo` so that types outside PyO3 cannot implement it.
168mod wrapping_cast_to {
169    pub trait Sealed<T> {}
170}
171
172macro_rules! wrapping_cast {
173    ($from:ty, $to:ty) => {
174        impl WrappingCastTo<$to> for $from {
175            #[inline]
176            fn wrapping_cast(self) -> $to {
177                self as $to
178            }
179        }
180        impl wrapping_cast_to::Sealed<$to> for $from {}
181    };
182}
183wrapping_cast!(u8, Py_hash_t);
184wrapping_cast!(u16, Py_hash_t);
185wrapping_cast!(u32, Py_hash_t);
186wrapping_cast!(usize, Py_hash_t);
187wrapping_cast!(u64, Py_hash_t);
188wrapping_cast!(i8, Py_hash_t);
189wrapping_cast!(i16, Py_hash_t);
190wrapping_cast!(i32, Py_hash_t);
191wrapping_cast!(isize, Py_hash_t);
192wrapping_cast!(i64, Py_hash_t);
193
194pub struct HashCallbackOutput(Py_hash_t);
195
196impl IntoPyCallbackOutput<'_, Py_hash_t> for HashCallbackOutput {
197    #[inline]
198    fn convert(self, _py: Python<'_>) -> PyResult<Py_hash_t> {
199        let hash = self.0;
200        if hash == -1 {
201            Ok(-2)
202        } else {
203            Ok(hash)
204        }
205    }
206}
207
208impl<T> IntoPyCallbackOutput<'_, HashCallbackOutput> for T
209where
210    T: WrappingCastTo<Py_hash_t>,
211{
212    #[inline]
213    fn convert(self, _py: Python<'_>) -> PyResult<HashCallbackOutput> {
214        Ok(HashCallbackOutput(self.wrapping_cast()))
215    }
216}
217
218#[doc(hidden)]
219#[inline]
220pub fn convert<'py, T, U>(py: Python<'py>, value: T) -> PyResult<U>
221where
222    T: IntoPyCallbackOutput<'py, U>,
223{
224    value.convert(py)
225}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here