#14; Convert Rust panic to Python SystemError
This commit is contained in:
parent
309182cfe8
commit
ab83b4f4a1
|
@ -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.
|
||||
|
|
|
@ -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<F, T>(_location: &str, f: F) -> *mut ffi::PyObject
|
||||
where F: FnOnce(Python) -> PyResult<T>,
|
||||
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<F, T>(location: &str, f: F) -> *mut ffi::PyObject
|
||||
where F: FnOnce(Python) -> PyResult<T>, 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<F, T>(_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<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
|
||||
|
|
90
src/lib.rs
90
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::<PyModule>(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::<PyModule>(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::<PyModule>(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::<PyModule>(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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T>(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]
|
||||
|
|
|
@ -30,6 +30,40 @@ fn empty_class_with_new() {
|
|||
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]
|
||||
#[allow(dead_code)]
|
||||
fn data_is_dropped() {
|
||||
|
|
Loading…
Reference in New Issue