use libc;
use std::{mem, ptr, io, any, marker};
use std::panic;
use std::ffi::{CString, CStr};
use python::{Python, PythonObject, PyDrop};
use objects::{PyObject, PyTuple, PyDict, PyString, exc};
use conversion::ToPyObject;
use ffi;
use err::{self, PyResult};
#[macro_export]
#[doc(hidden)]
macro_rules! py_method_def {
($name: expr, $flags: expr, $wrap: expr) => {{
static mut METHOD_DEF: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS | $flags,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
METHOD_DEF.ml_name = concat!($name, "\0").as_ptr() as *const _;
METHOD_DEF.ml_meth = Some(
::std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>($wrap)
);
&mut METHOD_DEF
}}
}
#[macro_export]
macro_rules! py_fn {
($py:expr, $f:ident $plist:tt ) => {
py_argparse_parse_plist! { py_fn_impl { $py, $f } $plist }
};
($py:ident, $f:ident $plist:tt -> $ret:ty { $($body:tt)* } ) => {
py_argparse_parse_plist! { py_fn_impl { $py, $f, $ret, { $($body)* } } $plist }
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_fn_impl {
{ $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
unsafe extern "C" fn wrap(
_slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
$crate::_detail::handle_callback(
stringify!($f), $crate::_detail::PyObjectCallbackConverter,
|py| {
py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
[ $( { $pname : $ptype = $detail } )* ]
{
$f(py $(, $pname )* )
})
})
}
unsafe {
$crate::_detail::py_fn_impl($py,
py_method_def!(stringify!($f), 0, wrap))
}
}};
{ $py:ident, $f:ident, $ret:ty, $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
fn $f($py: $crate::Python $( , $pname : $ptype )* ) -> $ret $body
py_fn_impl!($py, $f [ $( { $pname : $ptype = $detail } )* ])
}}
}
pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}
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
where S: ToPyObject
{
type R = *mut ffi::PyObject;
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
val.into_py_object(py).into_object().steal_ptr()
}
#[inline]
fn error_value() -> *mut ffi::PyObject {
ptr::null_mut()
}
}
pub struct PythonObjectCallbackConverter<T>(pub marker::PhantomData<T>);
impl <T, S> CallbackConverter<S> for PythonObjectCallbackConverter<T>
where T: PythonObject,
S: ToPyObject<ObjectType=T>
{
type R = *mut ffi::PyObject;
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
val.into_py_object(py).into_object().steal_ptr()
}
#[inline]
fn error_value() -> *mut ffi::PyObject {
ptr::null_mut()
}
}
pub unsafe fn handle_callback<F, T, C>(location: &str, _c: C, f: F) -> C::R
where F: FnOnce(Python) -> PyResult<T>,
F: panic::UnwindSafe,
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) {
let msg = cstr!("Rust panic");
unsafe {
ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
}
}
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() }
}
}