2017-05-17 06:43:39 +00:00
|
|
|
/// Utilities for a Python callable object that invokes a Rust function.
|
|
|
|
|
|
|
|
use std::os::raw::c_int;
|
2017-05-25 03:31:51 +00:00
|
|
|
use std::{any, mem, ptr, isize, io, panic};
|
2017-05-17 06:43:39 +00:00
|
|
|
use libc;
|
2017-05-25 03:31:51 +00:00
|
|
|
|
|
|
|
use pyptr::Py;
|
|
|
|
use python::{Python, IntoPythonPointer};
|
2017-05-17 06:43:39 +00:00
|
|
|
use objects::exc;
|
2017-05-25 05:43:07 +00:00
|
|
|
use conversion::IntoPyObject;
|
2017-05-17 06:43:39 +00:00
|
|
|
use ffi::{self, Py_hash_t};
|
|
|
|
use err::{PyErr, PyResult};
|
|
|
|
|
|
|
|
|
|
|
|
pub trait CallbackConverter<S> {
|
|
|
|
type R;
|
|
|
|
|
|
|
|
fn convert(S, Python) -> Self::R;
|
|
|
|
fn error_value() -> Self::R;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PyObjectCallbackConverter;
|
|
|
|
|
|
|
|
impl <S> CallbackConverter<S> for PyObjectCallbackConverter
|
2017-05-25 05:43:07 +00:00
|
|
|
where S: IntoPyObject
|
2017-05-17 06:43:39 +00:00
|
|
|
{
|
|
|
|
type R = *mut ffi::PyObject;
|
|
|
|
|
|
|
|
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
|
2017-05-25 03:31:51 +00:00
|
|
|
val.into_object(py).into_ptr()
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn error_value() -> *mut ffi::PyObject {
|
|
|
|
ptr::null_mut()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-21 05:18:31 +00:00
|
|
|
pub struct BoolCallbackConverter;
|
2017-05-17 06:43:39 +00:00
|
|
|
|
2017-05-21 05:18:31 +00:00
|
|
|
impl CallbackConverter<bool> for BoolCallbackConverter {
|
2017-05-17 06:43:39 +00:00
|
|
|
type R = c_int;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn convert(val: bool, _py: Python) -> c_int {
|
|
|
|
val as c_int
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn error_value() -> c_int {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct LenResultConverter;
|
|
|
|
|
|
|
|
impl CallbackConverter<usize> for LenResultConverter {
|
|
|
|
type R = isize;
|
|
|
|
|
|
|
|
fn convert(val: usize, py: Python) -> isize {
|
|
|
|
if val <= (isize::MAX as usize) {
|
|
|
|
val as isize
|
|
|
|
} else {
|
2017-05-25 03:31:51 +00:00
|
|
|
PyErr::new_lazy_init(py.get_ptype::<exc::OverflowError>(), None).restore(py);
|
2017-05-17 06:43:39 +00:00
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn error_value() -> isize {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub struct UnitCallbackConverter;
|
|
|
|
|
|
|
|
impl CallbackConverter<()> for UnitCallbackConverter {
|
|
|
|
type R = c_int;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn convert(_: (), _: Python) -> c_int {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn error_value() -> c_int {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct IterNextResultConverter;
|
|
|
|
|
|
|
|
impl <T> CallbackConverter<Option<T>>
|
|
|
|
for IterNextResultConverter
|
2017-05-25 05:43:07 +00:00
|
|
|
where T: IntoPyObject
|
2017-05-17 06:43:39 +00:00
|
|
|
{
|
|
|
|
type R = *mut ffi::PyObject;
|
|
|
|
|
|
|
|
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
|
|
|
|
match val {
|
2017-05-25 03:31:51 +00:00
|
|
|
Some(val) => val.into_object(py).into_ptr(),
|
2017-05-17 06:43:39 +00:00
|
|
|
None => unsafe {
|
|
|
|
ffi::PyErr_SetNone(ffi::PyExc_StopIteration);
|
|
|
|
ptr::null_mut()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn error_value() -> *mut ffi::PyObject {
|
|
|
|
ptr::null_mut()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait WrappingCastTo<T> {
|
|
|
|
fn wrapping_cast(self) -> T;
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! wrapping_cast {
|
|
|
|
($from:ty, $to:ty) => {
|
|
|
|
impl WrappingCastTo<$to> for $from {
|
|
|
|
#[inline]
|
|
|
|
fn wrapping_cast(self) -> $to {
|
|
|
|
self as $to
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wrapping_cast!(u8, Py_hash_t);
|
|
|
|
wrapping_cast!(u16, Py_hash_t);
|
|
|
|
wrapping_cast!(u32, Py_hash_t);
|
|
|
|
wrapping_cast!(usize, Py_hash_t);
|
|
|
|
wrapping_cast!(u64, Py_hash_t);
|
|
|
|
wrapping_cast!(i8, Py_hash_t);
|
|
|
|
wrapping_cast!(i16, Py_hash_t);
|
|
|
|
wrapping_cast!(i32, Py_hash_t);
|
|
|
|
wrapping_cast!(isize, Py_hash_t);
|
|
|
|
wrapping_cast!(i64, Py_hash_t);
|
|
|
|
|
|
|
|
pub struct HashConverter;
|
|
|
|
|
|
|
|
impl <T> CallbackConverter<T> for HashConverter
|
|
|
|
where T: WrappingCastTo<Py_hash_t>
|
|
|
|
{
|
|
|
|
type R = Py_hash_t;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn convert(val: T, _py: Python) -> Py_hash_t {
|
|
|
|
let hash = val.wrapping_cast();
|
|
|
|
if hash == -1 {
|
|
|
|
-2
|
|
|
|
} else {
|
|
|
|
hash
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn error_value() -> Py_hash_t {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-25 03:31:51 +00:00
|
|
|
// very unsafe util for callbacks, maybe bug un rust compiler?
|
|
|
|
pub unsafe fn unref<'p, T>(p: Py<'p, T>) -> &Py<T> {
|
|
|
|
{&p as *const _}.as_ref().unwrap()
|
2017-05-22 05:22:45 +00:00
|
|
|
}
|
2017-05-25 05:43:07 +00:00
|
|
|
pub unsafe fn unref_r<'p, T>(p: &'p Py<'p, T>) -> &'p Py<T> {
|
|
|
|
{p as *const _}.as_ref().unwrap()
|
|
|
|
}
|
2017-05-22 05:22:45 +00:00
|
|
|
|
2017-05-25 03:31:51 +00:00
|
|
|
pub unsafe fn handle<'p, F, T, C>(location: &str, _c: C, f: F) -> C::R
|
2017-05-22 05:22:45 +00:00
|
|
|
where F: FnOnce(Python<'p>) -> PyResult<T>,
|
2017-05-17 06:43:39 +00:00
|
|
|
F: panic::UnwindSafe,
|
2017-05-25 05:43:07 +00:00
|
|
|
C: CallbackConverter<T>
|
|
|
|
{
|
|
|
|
let guard = AbortOnDrop(location);
|
|
|
|
let ret = panic::catch_unwind(|| {
|
|
|
|
let py = Python::assume_gil_acquired();
|
|
|
|
match f(py) {
|
|
|
|
Ok(val) => {
|
|
|
|
C::convert(val, py)
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
e.restore(py);
|
|
|
|
C::error_value()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let ret = match ret {
|
|
|
|
Ok(r) => r,
|
|
|
|
Err(ref err) => {
|
|
|
|
handle_panic(Python::assume_gil_acquired(), err);
|
|
|
|
C::error_value()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
mem::forget(guard);
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
|
|
|
pub unsafe fn handle_cb<F, T, C>(location: &str, _c: C, f: F) -> C::R
|
|
|
|
where F: FnOnce(Python) -> PyResult<T>,
|
|
|
|
F: panic::UnwindSafe,
|
2017-05-17 06:43:39 +00:00
|
|
|
C: CallbackConverter<T>
|
|
|
|
{
|
|
|
|
let guard = AbortOnDrop(location);
|
|
|
|
let ret = panic::catch_unwind(|| {
|
|
|
|
let py = Python::assume_gil_acquired();
|
|
|
|
match f(py) {
|
|
|
|
Ok(val) => {
|
|
|
|
C::convert(val, py)
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
e.restore(py);
|
|
|
|
C::error_value()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let ret = match ret {
|
|
|
|
Ok(r) => r,
|
|
|
|
Err(ref err) => {
|
|
|
|
handle_panic(Python::assume_gil_acquired(), err);
|
|
|
|
C::error_value()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
mem::forget(guard);
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_panic(_py: Python, _panic: &any::Any) {
|
|
|
|
unsafe {
|
2017-05-25 03:31:51 +00:00
|
|
|
ffi::PyErr_SetString(ffi::PyExc_SystemError, "Rust panic\0".as_ptr() as *const i8);
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AbortOnDrop<'a>(pub &'a str);
|
|
|
|
|
|
|
|
impl <'a> Drop for AbortOnDrop<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
use std::io::Write;
|
|
|
|
let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
|
|
|
|
unsafe { libc::abort() }
|
|
|
|
}
|
|
|
|
}
|