2015-01-05 16:05:53 +00:00
|
|
|
use std;
|
2015-01-07 00:40:48 +00:00
|
|
|
use python::{PythonObject, Python, ToPythonPointer, PythonObjectDowncastError};
|
|
|
|
use objects::{PyObject, PyType, exc};
|
2015-01-11 03:21:05 +00:00
|
|
|
use objects::oldstyle::PyClass;
|
2015-01-05 16:05:53 +00:00
|
|
|
use ffi;
|
|
|
|
use libc;
|
2015-01-05 15:34:12 +00:00
|
|
|
use conversion::ToPyObject;
|
2015-03-08 14:29:44 +00:00
|
|
|
use std::ffi::CStr;
|
2015-01-05 16:05:53 +00:00
|
|
|
|
|
|
|
/// Represents a python exception that was raised.
|
2015-01-12 02:17:29 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2015-01-05 16:05:53 +00:00
|
|
|
pub struct PyErr<'p> {
|
2015-01-05 15:34:12 +00:00
|
|
|
/// Gets the type of the exception. This should be either a PyClass or a PyType.
|
2015-01-07 00:40:48 +00:00
|
|
|
pub ptype : PyObject<'p>,
|
2015-01-05 15:34:12 +00:00
|
|
|
/// Gets the value of the exception.
|
|
|
|
/// This can be either an instance of ptype,
|
|
|
|
/// a tuple of arguments to be passed to ptype's constructor,
|
|
|
|
/// or a single argument to be passed to ptype's constructor.
|
|
|
|
/// Call PyErr::instance() to get the exception instance in all cases.
|
2015-01-07 00:40:48 +00:00
|
|
|
pub pvalue : Option<PyObject<'p>>,
|
|
|
|
pub ptraceback : Option<PyObject<'p>> // is actually a PyTraceBack
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Represents the result of a python call.
|
|
|
|
pub type PyResult<'p, T> = Result<T, PyErr<'p>>;
|
|
|
|
|
|
|
|
impl <'p> PyErr<'p> {
|
|
|
|
/// Gets whether an error is present in the python interpreter's global state.
|
2015-01-05 15:34:12 +00:00
|
|
|
#[inline]
|
2015-01-05 16:05:53 +00:00
|
|
|
pub fn occurred(_ : Python<'p>) -> bool {
|
|
|
|
unsafe { !ffi::PyErr_Occurred().is_null() }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieves the current error from the python interpreter's global state.
|
|
|
|
/// The error is cleared from the python interpreter.
|
2015-01-07 00:40:48 +00:00
|
|
|
/// If no error is set, returns a SystemError.
|
2015-01-05 16:05:53 +00:00
|
|
|
pub fn fetch(py : Python<'p>) -> PyErr<'p> {
|
|
|
|
unsafe {
|
|
|
|
let mut ptype : *mut ffi::PyObject = std::mem::uninitialized();
|
|
|
|
let mut pvalue : *mut ffi::PyObject = std::mem::uninitialized();
|
|
|
|
let mut ptraceback : *mut ffi::PyObject = std::mem::uninitialized();
|
|
|
|
ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback);
|
2015-01-05 15:34:12 +00:00
|
|
|
PyErr::new_from_ffi_tuple(py, ptype, pvalue, ptraceback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe fn new_from_ffi_tuple(py: Python<'p>, ptype: *mut ffi::PyObject, pvalue: *mut ffi::PyObject, ptraceback: *mut ffi::PyObject) -> PyErr<'p> {
|
|
|
|
// Note: must not panic to ensure all owned pointers get acquired correctly,
|
|
|
|
// and because we mustn't panic in normalize().
|
|
|
|
PyErr {
|
|
|
|
ptype: if ptype.is_null() {
|
2015-01-07 00:40:48 +00:00
|
|
|
py.get_type::<exc::SystemError>().into_object()
|
2015-01-05 15:34:12 +00:00
|
|
|
} else {
|
2015-01-07 00:40:48 +00:00
|
|
|
PyObject::from_owned_ptr(py, ptype)
|
2015-01-05 15:34:12 +00:00
|
|
|
},
|
2015-01-07 00:40:48 +00:00
|
|
|
pvalue: PyObject::from_owned_ptr_opt(py, pvalue),
|
|
|
|
ptraceback: PyObject::from_owned_ptr_opt(py, ptraceback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new PyErr.
|
|
|
|
/// If obj is a python exception instance, the PyErr will use that instance.
|
|
|
|
/// If obj is a python exception type, the PyErr will (lazily) create a new instance of that type
|
|
|
|
/// Otherwise, a TypeError is returned instead.
|
|
|
|
pub fn new<O>(obj: O) -> PyErr<'p> where O: PythonObject<'p> {
|
|
|
|
PyErr::new_from_object(obj.into_object())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_from_object(obj: PyObject<'p>) -> PyErr<'p> {
|
|
|
|
let py = obj.python();
|
2015-01-11 03:21:05 +00:00
|
|
|
if unsafe { ffi::PyExceptionInstance_Check(obj.as_ptr()) } != 0 {
|
2015-01-07 00:40:48 +00:00
|
|
|
PyErr {
|
|
|
|
ptype: unsafe { PyObject::from_borrowed_ptr(py, ffi::PyExceptionInstance_Class(obj.as_ptr())) },
|
|
|
|
pvalue: Some(obj),
|
|
|
|
ptraceback: None
|
|
|
|
}
|
2015-01-11 03:21:05 +00:00
|
|
|
} else if unsafe { ffi::PyExceptionClass_Check(obj.as_ptr()) } != 0 {
|
2015-01-07 00:40:48 +00:00
|
|
|
PyErr {
|
|
|
|
ptype: obj,
|
|
|
|
pvalue: None,
|
|
|
|
ptraceback: None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PyErr {
|
|
|
|
ptype: py.get_type::<exc::TypeError>().into_object(),
|
|
|
|
pvalue: "exceptions must derive from BaseException".to_py_object(py).ok(),
|
|
|
|
ptraceback: None
|
|
|
|
}
|
2015-01-05 15:34:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
/// Construct a new error, with the usual lazy initialization of python exceptions.
|
2015-01-05 15:34:12 +00:00
|
|
|
/// `exc` is the exception type; usually one of the standard exceptions like `PyExc::runtime_error()`.
|
2015-01-07 00:40:48 +00:00
|
|
|
/// `value` is the exception instance, or a tuple of arguments to pass to the exception constructor.
|
|
|
|
#[inline]
|
|
|
|
pub fn new_lazy_init(exc: PyType<'p>, value: Option<PyObject<'p>>) -> PyErr<'p> {
|
2015-01-05 15:34:12 +00:00
|
|
|
PyErr {
|
2015-01-07 00:40:48 +00:00
|
|
|
ptype: exc.into_object(),
|
2015-01-05 15:34:12 +00:00
|
|
|
pvalue: value,
|
|
|
|
ptraceback: None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Print a standard traceback to sys.stderr.
|
|
|
|
pub fn print(self) {
|
|
|
|
self.restore();
|
|
|
|
unsafe { ffi::PyErr_PrintEx(0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Print a standard traceback to sys.stderr.
|
|
|
|
pub fn print_and_set_sys_last_vars(self) {
|
|
|
|
self.restore();
|
|
|
|
unsafe { ffi::PyErr_PrintEx(1) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return true if the current exception matches the exception in `exc`.
|
|
|
|
/// If `exc` is a class object, this also returns `true` when `self` is an instance of a subclass.
|
|
|
|
/// If `exc` is a tuple, all exceptions in the tuple (and recursively in subtuples) are searched for a match.
|
2015-01-06 00:04:25 +00:00
|
|
|
#[inline]
|
2015-01-05 15:34:12 +00:00
|
|
|
pub fn matches(&self, exc: &PyObject) -> bool {
|
|
|
|
unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc.as_ptr()) != 0 }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Normalizes the error. This ensures that the exception value is an instance of the exception type.
|
|
|
|
pub fn normalize(&mut self) {
|
|
|
|
// The normalization helper function involves temporarily moving out of the &mut self,
|
|
|
|
// which requires some unsafe trickery:
|
|
|
|
unsafe {
|
2015-01-07 00:40:48 +00:00
|
|
|
std::ptr::write(self, std::ptr::read(self).into_normalized());
|
2015-01-05 15:34:12 +00:00
|
|
|
}
|
|
|
|
// This is safe as long as normalized() doesn't unwind due to a panic.
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper function for normalizing the error by deconstructing and reconstructing the PyErr.
|
|
|
|
/// Must not panic for safety in normalize()
|
2015-01-07 00:40:48 +00:00
|
|
|
fn into_normalized(self) -> PyErr<'p> {
|
2015-01-05 15:34:12 +00:00
|
|
|
let PyErr { ptype, pvalue, ptraceback } = self;
|
|
|
|
let py = ptype.python();
|
|
|
|
let mut ptype = ptype.steal_ptr();
|
|
|
|
let mut pvalue = pvalue.steal_ptr();
|
|
|
|
let mut ptraceback = ptraceback.steal_ptr();
|
|
|
|
unsafe {
|
|
|
|
ffi::PyErr_NormalizeException(&mut ptype, &mut pvalue, &mut ptraceback);
|
|
|
|
PyErr::new_from_ffi_tuple(py, ptype, pvalue, ptraceback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-06 00:04:25 +00:00
|
|
|
/// Retrieves the exception type.
|
|
|
|
/// If the exception type is an old-style class, returns oldstyle::PyClass.
|
2015-01-07 00:40:48 +00:00
|
|
|
pub fn get_type(&self) -> PyType<'p> {
|
2015-01-11 03:21:05 +00:00
|
|
|
let py = self.ptype.python();
|
2015-01-07 00:40:48 +00:00
|
|
|
match self.ptype.clone().cast_into::<PyType>() {
|
2015-01-06 00:04:25 +00:00
|
|
|
Ok(t) => t,
|
2015-01-11 03:21:05 +00:00
|
|
|
Err(_) =>
|
|
|
|
match self.ptype.cast_as::<PyClass>() {
|
|
|
|
Ok(_) => py.get_type::<PyClass>(),
|
|
|
|
Err(_) => py.None().get_type().clone()
|
|
|
|
}
|
2015-01-06 00:04:25 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-11 03:21:05 +00:00
|
|
|
|
2015-01-05 15:34:12 +00:00
|
|
|
/// Retrieves the exception instance for this error.
|
2015-01-06 00:04:25 +00:00
|
|
|
/// This method takes &mut self because the error might need
|
|
|
|
/// to be normalized in order to create the exception instance.
|
2015-01-07 00:40:48 +00:00
|
|
|
pub fn instance(&mut self) -> PyObject<'p> {
|
2015-01-05 15:34:12 +00:00
|
|
|
self.normalize();
|
|
|
|
match self.pvalue {
|
2015-01-07 00:40:48 +00:00
|
|
|
Some(ref instance) => instance.clone(),
|
2015-01-05 15:34:12 +00:00
|
|
|
None => self.ptype.python().None()
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Restores the error by writing it to the python interpreter's global state.
|
2015-01-07 00:40:48 +00:00
|
|
|
#[inline]
|
2015-01-05 16:05:53 +00:00
|
|
|
pub fn restore(self) {
|
|
|
|
let PyErr { ptype, pvalue, ptraceback } = self;
|
|
|
|
unsafe {
|
|
|
|
ffi::PyErr_Restore(ptype.steal_ptr(), pvalue.steal_ptr(), ptraceback.steal_ptr())
|
|
|
|
}
|
|
|
|
}
|
2015-01-11 03:21:05 +00:00
|
|
|
|
|
|
|
/// Issue a warning message.
|
|
|
|
/// May return a PyErr if warnings-as-errors is enabled.
|
|
|
|
pub fn warn(py: Python<'p>, category: &PyObject, message: &CStr, stacklevel: i32) -> PyResult<'p, ()> {
|
|
|
|
unsafe {
|
|
|
|
error_on_minusone(py, ffi::PyErr_WarnEx(category.as_ptr(), message.as_ptr(), stacklevel as ffi::Py_ssize_t))
|
|
|
|
}
|
|
|
|
}
|
2015-01-05 15:34:12 +00:00
|
|
|
}
|
2015-01-05 16:05:53 +00:00
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
impl <'p> std::error::FromError<PythonObjectDowncastError<'p>> for PyErr<'p> {
|
|
|
|
fn from_error(err: PythonObjectDowncastError<'p>) -> PyErr<'p> {
|
|
|
|
PyErr::new_lazy_init(err.0.get_type::<exc::TypeError>(), None)
|
|
|
|
}
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct PyObject from the result of a python FFI call that returns a new reference (owned pointer).
|
|
|
|
/// Returns Err(PyErr) if the pointer is null.
|
|
|
|
/// Unsafe because the pointer might be invalid.
|
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
pub unsafe fn result_from_owned_ptr(py : Python, p : *mut ffi::PyObject) -> PyResult<PyObject> {
|
2015-01-05 16:05:53 +00:00
|
|
|
if p.is_null() {
|
|
|
|
Err(PyErr::fetch(py))
|
|
|
|
} else {
|
2015-01-07 00:40:48 +00:00
|
|
|
Ok(PyObject::from_owned_ptr(py, p))
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-11 03:21:05 +00:00
|
|
|
pub unsafe fn result_cast_from_owned_ptr<'p, T>(py : Python<'p>, p : *mut ffi::PyObject) -> PyResult<'p, T>
|
|
|
|
where T: ::python::PythonObjectWithCheckedDowncast<'p>
|
|
|
|
{
|
|
|
|
if p.is_null() {
|
|
|
|
Err(PyErr::fetch(py))
|
|
|
|
} else {
|
|
|
|
Ok(try!(PyObject::from_owned_ptr(py, p).cast_into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-05 16:05:53 +00:00
|
|
|
/// Returns Ok if the error code is 0.
|
|
|
|
#[inline]
|
2015-01-04 04:50:28 +00:00
|
|
|
pub fn error_on_nonzero(py : Python, result : libc::c_int) -> PyResult<()> {
|
2015-01-05 16:05:53 +00:00
|
|
|
if result == 0 {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(PyErr::fetch(py))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-04 04:50:28 +00:00
|
|
|
/// Returns Ok if the error code is not -1.
|
|
|
|
#[inline]
|
|
|
|
pub fn error_on_minusone(py : Python, result : libc::c_int) -> PyResult<()> {
|
|
|
|
if result != -1 {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(PyErr::fetch(py))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-04 15:32:52 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2015-01-05 16:02:30 +00:00
|
|
|
use {Python, PyErr};
|
2015-01-07 00:40:48 +00:00
|
|
|
use objects::{PyObject, exc};
|
2015-01-04 15:32:52 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn set_typeerror() {
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
2015-01-07 00:40:48 +00:00
|
|
|
PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None).restore();
|
2015-01-04 15:32:52 +00:00
|
|
|
assert!(PyErr::occurred(py));
|
2015-03-08 14:29:44 +00:00
|
|
|
drop(PyErr::fetch(py));
|
2015-01-04 15:32:52 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-04 04:50:28 +00:00
|
|
|
|
|
|
|
|