Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 50 additions & 3 deletions src/impl_/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@ use crate::{BoundObject, IntoPyObject, Py, PyAny, Python};
use std::ffi::c_int;

/// A type which can be the return type of a python C-API callback
pub trait PyCallbackOutput: Copy {
pub trait PyCallbackOutput: Copy + py_callback_output::Sealed {
/// The error value to return to python if the callback raised an exception
const ERR_VALUE: Self;
}

/// Seals `PyCallbackOutput` so that types outside PyO3 cannot implement it.
mod py_callback_output {
use std::os::raw::c_int;

use pyo3_ffi::Py_ssize_t;

use crate::ffi::PyObject;

pub trait Sealed {}

impl Sealed for *mut PyObject {}
impl Sealed for c_int {}
impl Sealed for Py_ssize_t {}
}

impl PyCallbackOutput for *mut ffi::PyObject {
const ERR_VALUE: Self = std::ptr::null_mut();
}
Expand All @@ -25,10 +40,36 @@ impl PyCallbackOutput for ffi::Py_ssize_t {
}

/// Convert the result of callback function into the appropriate return value.
pub trait IntoPyCallbackOutput<'py, Target> {
pub trait IntoPyCallbackOutput<'py, Target>: into_py_callback_output::Sealed<'py, Target> {
fn convert(self, py: Python<'py>) -> PyResult<Target>;
}

/// Seals `IntoPyCallbackOutput` so that types outside PyO3 cannot implement it.
mod into_py_callback_output {
use pyo3_ffi::Py_hash_t;

use crate::{
ffi,
impl_::callback::{HashCallbackOutput, IntoPyCallbackOutput, WrappingCastTo},
IntoPyObject, Py, PyAny, PyErr,
};

pub trait Sealed<'py, Target> {}

impl<'py, T: IntoPyObject<'py>> Sealed<'py, *mut ffi::PyObject> for T {}
impl<'py, T: IntoPyCallbackOutput<'py, U>, E: Into<PyErr>, U> Sealed<'py, U> for Result<T, E> {}
impl Sealed<'_, Self> for *mut ffi::PyObject {}
impl Sealed<'_, std::ffi::c_int> for () {}
impl Sealed<'_, std::ffi::c_int> for bool {}
impl Sealed<'_, ()> for () {}
impl Sealed<'_, ffi::Py_ssize_t> for usize {}
impl Sealed<'_, bool> for bool {}
impl Sealed<'_, usize> for usize {}
impl<'py, T: IntoPyObject<'py>> Sealed<'py, Py<PyAny>> for T {}
impl Sealed<'_, Py_hash_t> for HashCallbackOutput {}
impl<T: WrappingCastTo<Py_hash_t>> Sealed<'_, HashCallbackOutput> for T {}
}

impl<'py, T, E, U> IntoPyCallbackOutput<'py, U> for Result<T, E>
where
T: IntoPyCallbackOutput<'py, U>,
Expand Down Expand Up @@ -119,10 +160,15 @@ where
}
}

pub trait WrappingCastTo<T> {
pub trait WrappingCastTo<T>: wrapping_cast_to::Sealed<T> {
fn wrapping_cast(self) -> T;
}

/// Seals `WrappingCastTo` so that types outside PyO3 cannot implement it.
mod wrapping_cast_to {
pub trait Sealed<T> {}
}

macro_rules! wrapping_cast {
($from:ty, $to:ty) => {
impl WrappingCastTo<$to> for $from {
Expand All @@ -131,6 +177,7 @@ macro_rules! wrapping_cast {
self as $to
}
}
impl wrapping_cast_to::Sealed<$to> for $from {}
};
}
wrapping_cast!(u8, Py_hash_t);
Expand Down
Loading