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, PyObject, Python};
7use std::os::raw::c_int;
8
9/// A type which can be the return type of a python C-API callback
10pub trait PyCallbackOutput: Copy {
11    /// The error value to return to python if the callback raised an exception
12    const ERR_VALUE: Self;
13}
14
15impl PyCallbackOutput for *mut ffi::PyObject {
16    const ERR_VALUE: Self = std::ptr::null_mut();
17}
18
19impl PyCallbackOutput for std::os::raw::c_int {
20    const ERR_VALUE: Self = -1;
21}
22
23impl PyCallbackOutput for ffi::Py_ssize_t {
24    const ERR_VALUE: Self = -1;
25}
26
27/// Convert the result of callback function into the appropriate return value.
28pub trait IntoPyCallbackOutput<'py, Target> {
29    fn convert(self, py: Python<'py>) -> PyResult<Target>;
30}
31
32impl<'py, T, E, U> IntoPyCallbackOutput<'py, U> for Result<T, E>
33where
34    T: IntoPyCallbackOutput<'py, U>,
35    E: Into<PyErr>,
36{
37    #[inline]
38    fn convert(self, py: Python<'py>) -> PyResult<U> {
39        match self {
40            Ok(v) => v.convert(py),
41            Err(e) => Err(e.into()),
42        }
43    }
44}
45
46impl<'py, T> IntoPyCallbackOutput<'py, *mut ffi::PyObject> for T
47where
48    T: IntoPyObject<'py>,
49{
50    #[inline]
51    fn convert(self, py: Python<'py>) -> PyResult<*mut ffi::PyObject> {
52        self.into_pyobject(py)
53            .map(BoundObject::into_ptr)
54            .map_err(Into::into)
55    }
56}
57
58impl IntoPyCallbackOutput<'_, Self> for *mut ffi::PyObject {
59    #[inline]
60    fn convert(self, _: Python<'_>) -> PyResult<Self> {
61        Ok(self)
62    }
63}
64
65impl IntoPyCallbackOutput<'_, std::os::raw::c_int> for () {
66    #[inline]
67    fn convert(self, _: Python<'_>) -> PyResult<std::os::raw::c_int> {
68        Ok(0)
69    }
70}
71
72impl IntoPyCallbackOutput<'_, std::os::raw::c_int> for bool {
73    #[inline]
74    fn convert(self, _: Python<'_>) -> PyResult<std::os::raw::c_int> {
75        Ok(self as c_int)
76    }
77}
78
79impl IntoPyCallbackOutput<'_, ()> for () {
80    #[inline]
81    fn convert(self, _: Python<'_>) -> PyResult<()> {
82        Ok(())
83    }
84}
85
86impl IntoPyCallbackOutput<'_, ffi::Py_ssize_t> for usize {
87    #[inline]
88    fn convert(self, _py: Python<'_>) -> PyResult<ffi::Py_ssize_t> {
89        self.try_into().map_err(|_err| PyOverflowError::new_err(()))
90    }
91}
92
93// Converters needed for `#[pyproto]` implementations
94
95impl IntoPyCallbackOutput<'_, bool> for bool {
96    #[inline]
97    fn convert(self, _: Python<'_>) -> PyResult<bool> {
98        Ok(self)
99    }
100}
101
102impl IntoPyCallbackOutput<'_, usize> for usize {
103    #[inline]
104    fn convert(self, _: Python<'_>) -> PyResult<usize> {
105        Ok(self)
106    }
107}
108
109impl<'py, T> IntoPyCallbackOutput<'py, PyObject> for T
110where
111    T: IntoPyObject<'py>,
112{
113    #[inline]
114    fn convert(self, py: Python<'py>) -> PyResult<PyObject> {
115        self.into_pyobject(py)
116            .map(BoundObject::into_any)
117            .map(BoundObject::unbind)
118            .map_err(Into::into)
119    }
120}
121
122pub trait WrappingCastTo<T> {
123    fn wrapping_cast(self) -> T;
124}
125
126macro_rules! wrapping_cast {
127    ($from:ty, $to:ty) => {
128        impl WrappingCastTo<$to> for $from {
129            #[inline]
130            fn wrapping_cast(self) -> $to {
131                self as $to
132            }
133        }
134    };
135}
136wrapping_cast!(u8, Py_hash_t);
137wrapping_cast!(u16, Py_hash_t);
138wrapping_cast!(u32, Py_hash_t);
139wrapping_cast!(usize, Py_hash_t);
140wrapping_cast!(u64, Py_hash_t);
141wrapping_cast!(i8, Py_hash_t);
142wrapping_cast!(i16, Py_hash_t);
143wrapping_cast!(i32, Py_hash_t);
144wrapping_cast!(isize, Py_hash_t);
145wrapping_cast!(i64, Py_hash_t);
146
147pub struct HashCallbackOutput(Py_hash_t);
148
149impl IntoPyCallbackOutput<'_, Py_hash_t> for HashCallbackOutput {
150    #[inline]
151    fn convert(self, _py: Python<'_>) -> PyResult<Py_hash_t> {
152        let hash = self.0;
153        if hash == -1 {
154            Ok(-2)
155        } else {
156            Ok(hash)
157        }
158    }
159}
160
161impl<T> IntoPyCallbackOutput<'_, HashCallbackOutput> for T
162where
163    T: WrappingCastTo<Py_hash_t>,
164{
165    #[inline]
166    fn convert(self, _py: Python<'_>) -> PyResult<HashCallbackOutput> {
167        Ok(HashCallbackOutput(self.wrapping_cast()))
168    }
169}
170
171#[doc(hidden)]
172#[inline]
173pub fn convert<'py, T, U>(py: Python<'py>, value: T) -> PyResult<U>
174where
175    T: IntoPyCallbackOutput<'py, U>,
176{
177    value.convert(py)
178}