#14; Convert Rust panic to Python SystemError
This commit is contained in:
parent
309182cfe8
commit
ab83b4f4a1
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
90
src/lib.rs
90
src/lib.rs
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue