diff --git a/Cargo.toml b/Cargo.toml index a135e767..cbf98b7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ build = "build.rs" [dependencies] libc = "0.2" num = "0.1" -abort_on_panic = "1.0" # These features are both optional, but you must pick one to # indicate which python ffi you are trying to bind to. diff --git a/src/function.rs b/src/function.rs index a79f3559..ef17088d 100644 --- a/src/function.rs +++ b/src/function.rs @@ -16,8 +16,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use std::{mem, ptr}; -use python::{Python, PythonObject}; +use libc; +use std::{mem, ptr, io, any}; +use std::ffi::{CString, CStr}; +use python::{Python, PythonObject, PyDrop}; use objects::{PyObject, PyTuple, PyDict, PyString, exc}; use conversion::ToPyObject; use ffi; @@ -135,11 +137,17 @@ macro_rules! py_fn_impl { }} } -pub unsafe fn handle_callback(_location: &str, f: F) -> *mut ffi::PyObject - where F: FnOnce(Python) -> PyResult, +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())) +} + +#[cfg(feature="nightly")] +pub unsafe fn handle_callback(location: &str, f: F) -> *mut ffi::PyObject + where F: FnOnce(Python) -> PyResult, F: ::std::panic::RecoverSafe, T: ToPyObject { - abort_on_panic!({ + let guard = AbortOnDrop(location); + let ret = ::std::panic::recover(|| { let py = Python::assume_gil_acquired(); match f(py) { Ok(val) => { @@ -150,11 +158,51 @@ pub unsafe fn handle_callback(_location: &str, f: F) -> *mut ffi::PyObject 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 { - err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut())) +fn handle_panic(_py: Python, _panic: &any::Any) -> *mut ffi::PyObject { + 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(location: &str, f: F) -> *mut ffi::PyObject + where F: FnOnce(Python) -> PyResult, + 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 diff --git a/src/lib.rs b/src/lib.rs index e5514b62..f8819193 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ const_fn, // for GILProtected::new (#24111) 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 @@ -82,9 +83,6 @@ extern crate libc; -#[macro_use] -extern crate abort_on_panic; - #[cfg(feature="python27-sys")] extern crate python27_sys as ffi; @@ -107,7 +105,7 @@ pub type Py_hash_t = libc::c_long; #[allow(non_camel_case_types)] pub type Py_hash_t = ffi::Py_hash_t; -use std::ptr; +use std::{ptr, mem}; /// Constructs a `&'static CStr` literal. macro_rules! cstr( @@ -150,9 +148,8 @@ pub mod _detail { pub mod libc { pub use ::libc::c_char; } - pub use abort_on_panic::PanicGuard; 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 @@ -222,24 +219,29 @@ pub unsafe fn py_module_initializer_impl( name: *const libc::c_char, init: fn(Python, &PyModule) -> PyResult<()> ) { - abort_on_panic!({ - let py = Python::assume_gil_acquired(); - ffi::PyEval_InitThreads(); - let module = ffi::Py_InitModule(name, ptr::null_mut()); - if module.is_null() { return; } + let guard = function::AbortOnDrop("py_module_initializer"); + let py = Python::assume_gil_acquired(); + ffi::PyEval_InitThreads(); + let module = ffi::Py_InitModule(name, ptr::null_mut()); + if module.is_null() { + mem::forget(guard); + return; + } - let module = match PyObject::from_borrowed_ptr(py, module).cast_into::(py) { - Ok(m) => m, - Err(e) => { - PyErr::from(e).restore(py); - return; - } - }; - match init(py, &module) { - Ok(()) => (), - Err(e) => e.restore(py) + let module = match PyObject::from_borrowed_ptr(py, module).cast_into::(py) { + Ok(m) => m, + Err(e) => { + PyErr::from(e).restore(py); + mem::forget(guard); + return; } - }) + }; + let ret = match init(py, &module) { + Ok(()) => (), + Err(e) => e.restore(py) + }; + mem::forget(guard); + ret } #[macro_export] @@ -279,25 +281,31 @@ pub unsafe fn py_module_initializer_impl( def: *mut ffi::PyModuleDef, init: fn(Python, &PyModule) -> PyResult<()> ) -> *mut ffi::PyObject { - abort_on_panic!({ - let py = Python::assume_gil_acquired(); - ffi::PyEval_InitThreads(); - let module = ffi::PyModule_Create(def); - if module.is_null() { return module; } + let guard = function::PanicOnDrop("py_module_initializer"); + let py = Python::assume_gil_acquired(); + ffi::PyEval_InitThreads(); + let module = ffi::PyModule_Create(def); + if module.is_null() { + mem::forget(guard); + return module; + } - let module = match PyObject::from_owned_ptr(py, module).cast_into::(py) { - Ok(m) => m, - Err(e) => { - PyErr::from(e).restore(py); - return ptr::null_mut(); - } - }; - match init(py, &module) { - Ok(()) => module.into_object().steal_ptr(), - Err(e) => { - e.restore(py); - return ptr::null_mut(); - } + let module = match PyObject::from_owned_ptr(py, module).cast_into::(py) { + Ok(m) => m, + Err(e) => { + PyErr::from(e).restore(py); + mem::forget(guard); + 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 } + diff --git a/src/py_class/slots.rs b/src/py_class/slots.rs index 6d723c8d..7247e889 100644 --- a/src/py_class/slots.rs +++ b/src/py_class/slots.rs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. use ffi; +use std::mem; use python::Python; #[macro_export] @@ -51,10 +52,11 @@ macro_rules! py_class_type_object_dynamic_init { pub unsafe extern "C" fn tp_dealloc_callback(obj: *mut ffi::PyObject) where T: super::BaseObject { - abort_on_panic!({ - let py = Python::assume_gil_acquired(); - T::dealloc(py, obj) - }); + let guard = ::function::AbortOnDrop("Cannot unwind out of tp_dealloc"); + let py = Python::assume_gil_acquired(); + let r = T::dealloc(py, obj); + mem::forget(guard); + r } #[macro_export] diff --git a/tests/test_class.rs b/tests/test_class.rs index 62e8612f..06ef889a 100644 --- a/tests/test_class.rs +++ b/tests/test_class.rs @@ -30,6 +30,40 @@ fn empty_class_with_new() { assert!(typeobj.call(py, NoArgs, None).unwrap().cast_into::(py).is_ok()); } +#[test] +fn new_with_one_arg() { + py_class!(class C |py| { + data _data: i32; + def __new__(_cls, arg: i32) -> PyResult { + C::create_instance(py, arg) + } + }); + + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + let obj = typeobj.call(py, (42,), None).unwrap().cast_into::(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::create_instance(py, arg1, arg2) + } + }); + + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + let obj = typeobj.call(py, (10, 20), None).unwrap().cast_into::(py).unwrap(); + assert_eq!(*obj._data1(py), 10); + assert_eq!(*obj._data2(py), 20); +} + #[test] #[allow(dead_code)] fn data_is_dropped() {