#14; Convert Rust panic to Python SystemError

This commit is contained in:
Daniel Grunwald 2016-03-12 01:31:06 +01:00
parent 309182cfe8
commit ab83b4f4a1
5 changed files with 145 additions and 54 deletions

View File

@ -27,7 +27,6 @@ build = "build.rs"
[dependencies] [dependencies]
libc = "0.2" libc = "0.2"
num = "0.1" num = "0.1"
abort_on_panic = "1.0"
# These features are both optional, but you must pick one to # These features are both optional, but you must pick one to
# indicate which python ffi you are trying to bind to. # indicate which python ffi you are trying to bind to.

View File

@ -16,8 +16,10 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use std::{mem, ptr}; use libc;
use python::{Python, PythonObject}; use std::{mem, ptr, io, any};
use std::ffi::{CString, CStr};
use python::{Python, PythonObject, PyDrop};
use objects::{PyObject, PyTuple, PyDict, PyString, exc}; use objects::{PyObject, PyTuple, PyDict, PyString, exc};
use conversion::ToPyObject; use conversion::ToPyObject;
use ffi; use ffi;
@ -135,11 +137,17 @@ macro_rules! py_fn_impl {
}} }}
} }
pub unsafe fn handle_callback<F, T>(_location: &str, f: F) -> *mut ffi::PyObject pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
where F: FnOnce(Python) -> PyResult<T>, err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}
#[cfg(feature="nightly")]
pub unsafe fn handle_callback<F, T>(location: &str, f: F) -> *mut ffi::PyObject
where F: FnOnce(Python) -> PyResult<T>, F: ::std::panic::RecoverSafe,
T: ToPyObject T: ToPyObject
{ {
abort_on_panic!({ let guard = AbortOnDrop(location);
let ret = ::std::panic::recover(|| {
let py = Python::assume_gil_acquired(); let py = Python::assume_gil_acquired();
match f(py) { match f(py) {
Ok(val) => { Ok(val) => {
@ -150,11 +158,51 @@ pub unsafe fn handle_callback<F, T>(_location: &str, f: F) -> *mut ffi::PyObject
ptr::null_mut() ptr::null_mut()
} }
} }
}) });
let ret = match ret {
Ok(r) => r,
Err(ref err) => handle_panic(Python::assume_gil_acquired(), err)
};
mem::forget(guard);
ret
} }
pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject { fn handle_panic(_py: Python, _panic: &any::Any) -> *mut ffi::PyObject {
err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut())) let msg = cstr!("Rust panic");
unsafe {
ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
}
ptr::null_mut()
}
#[cfg(not(feature="nightly"))]
pub unsafe fn handle_callback<F, T>(location: &str, f: F) -> *mut ffi::PyObject
where F: FnOnce(Python) -> PyResult<T>,
T: ToPyObject
{
let guard = AbortOnDrop(location);
let py = Python::assume_gil_acquired();
let ret = match f(py) {
Ok(val) => {
val.into_py_object(py).into_object().steal_ptr()
}
Err(e) => {
e.restore(py);
ptr::null_mut()
}
};
mem::forget(guard);
ret
}
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() }
}
} }
// Tests for this file are in tests/test_function.rs // Tests for this file are in tests/test_function.rs

View File

@ -24,6 +24,7 @@
const_fn, // for GILProtected::new (#24111) const_fn, // for GILProtected::new (#24111)
shared, // for std::ptr::Shared (#27730) shared, // for std::ptr::Shared (#27730)
recover, // for converting panics to python exceptions (#27719)
))] ))]
#![allow(unused_imports)] // because some imports are only necessary with python 2.x or 3.x #![allow(unused_imports)] // because some imports are only necessary with python 2.x or 3.x
@ -82,9 +83,6 @@
extern crate libc; extern crate libc;
#[macro_use]
extern crate abort_on_panic;
#[cfg(feature="python27-sys")] #[cfg(feature="python27-sys")]
extern crate python27_sys as ffi; extern crate python27_sys as ffi;
@ -107,7 +105,7 @@ pub type Py_hash_t = libc::c_long;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type Py_hash_t = ffi::Py_hash_t; pub type Py_hash_t = ffi::Py_hash_t;
use std::ptr; use std::{ptr, mem};
/// Constructs a `&'static CStr` literal. /// Constructs a `&'static CStr` literal.
macro_rules! cstr( macro_rules! cstr(
@ -150,9 +148,8 @@ pub mod _detail {
pub mod libc { pub mod libc {
pub use ::libc::c_char; pub use ::libc::c_char;
} }
pub use abort_on_panic::PanicGuard;
pub use err::from_owned_ptr_or_panic; pub use err::from_owned_ptr_or_panic;
pub use function::{handle_callback, py_fn_impl}; pub use function::{handle_callback, py_fn_impl, AbortOnDrop};
} }
/// Expands to an `extern "C"` function that allows Python to load /// Expands to an `extern "C"` function that allows Python to load
@ -222,24 +219,29 @@ pub unsafe fn py_module_initializer_impl(
name: *const libc::c_char, name: *const libc::c_char,
init: fn(Python, &PyModule) -> PyResult<()> init: fn(Python, &PyModule) -> PyResult<()>
) { ) {
abort_on_panic!({ let guard = function::AbortOnDrop("py_module_initializer");
let py = Python::assume_gil_acquired(); let py = Python::assume_gil_acquired();
ffi::PyEval_InitThreads(); ffi::PyEval_InitThreads();
let module = ffi::Py_InitModule(name, ptr::null_mut()); let module = ffi::Py_InitModule(name, ptr::null_mut());
if module.is_null() { return; } if module.is_null() {
mem::forget(guard);
return;
}
let module = match PyObject::from_borrowed_ptr(py, module).cast_into::<PyModule>(py) { let module = match PyObject::from_borrowed_ptr(py, module).cast_into::<PyModule>(py) {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
PyErr::from(e).restore(py); PyErr::from(e).restore(py);
return; mem::forget(guard);
} return;
};
match init(py, &module) {
Ok(()) => (),
Err(e) => e.restore(py)
} }
}) };
let ret = match init(py, &module) {
Ok(()) => (),
Err(e) => e.restore(py)
};
mem::forget(guard);
ret
} }
#[macro_export] #[macro_export]
@ -279,25 +281,31 @@ pub unsafe fn py_module_initializer_impl(
def: *mut ffi::PyModuleDef, def: *mut ffi::PyModuleDef,
init: fn(Python, &PyModule) -> PyResult<()> init: fn(Python, &PyModule) -> PyResult<()>
) -> *mut ffi::PyObject { ) -> *mut ffi::PyObject {
abort_on_panic!({ let guard = function::PanicOnDrop("py_module_initializer");
let py = Python::assume_gil_acquired(); let py = Python::assume_gil_acquired();
ffi::PyEval_InitThreads(); ffi::PyEval_InitThreads();
let module = ffi::PyModule_Create(def); let module = ffi::PyModule_Create(def);
if module.is_null() { return module; } if module.is_null() {
mem::forget(guard);
return module;
}
let module = match PyObject::from_owned_ptr(py, module).cast_into::<PyModule>(py) { let module = match PyObject::from_owned_ptr(py, module).cast_into::<PyModule>(py) {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
PyErr::from(e).restore(py); PyErr::from(e).restore(py);
return ptr::null_mut(); mem::forget(guard);
} return ptr::null_mut();
};
match init(py, &module) {
Ok(()) => module.into_object().steal_ptr(),
Err(e) => {
e.restore(py);
return ptr::null_mut();
}
} }
}) };
let ret = match init(py, &module) {
Ok(()) => module.into_object().steal_ptr(),
Err(e) => {
e.restore(py);
ptr::null_mut()
}
};
mem::forget(guard);
ret
} }

View File

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use ffi; use ffi;
use std::mem;
use python::Python; use python::Python;
#[macro_export] #[macro_export]
@ -51,10 +52,11 @@ macro_rules! py_class_type_object_dynamic_init {
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject) pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
where T: super::BaseObject where T: super::BaseObject
{ {
abort_on_panic!({ let guard = ::function::AbortOnDrop("Cannot unwind out of tp_dealloc");
let py = Python::assume_gil_acquired(); let py = Python::assume_gil_acquired();
T::dealloc(py, obj) let r = T::dealloc(py, obj);
}); mem::forget(guard);
r
} }
#[macro_export] #[macro_export]

View File

@ -30,6 +30,40 @@ fn empty_class_with_new() {
assert!(typeobj.call(py, NoArgs, None).unwrap().cast_into::<Empty>(py).is_ok()); assert!(typeobj.call(py, NoArgs, None).unwrap().cast_into::<Empty>(py).is_ok());
} }
#[test]
fn new_with_one_arg() {
py_class!(class C |py| {
data _data: i32;
def __new__(_cls, arg: i32) -> PyResult<C> {
C::create_instance(py, arg)
}
});
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<C>();
let obj = typeobj.call(py, (42,), None).unwrap().cast_into::<C>(py).unwrap();
assert_eq!(*obj._data(py), 42);
}
#[test]
fn new_with_two_args() {
py_class!(class C |py| {
data _data1: i32;
data _data2: i32;
def __new__(_cls, arg1: i32, arg2: i32) -> PyResult<C> {
C::create_instance(py, arg1, arg2)
}
});
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<C>();
let obj = typeobj.call(py, (10, 20), None).unwrap().cast_into::<C>(py).unwrap();
assert_eq!(*obj._data1(py), 10);
assert_eq!(*obj._data2(py), 20);
}
#[test] #[test]
#[allow(dead_code)] #[allow(dead_code)]
fn data_is_dropped() { fn data_is_dropped() {