pyo3/src/err.rs

326 lines
12 KiB
Rust
Raw Normal View History

2015-04-19 03:22:03 +00:00
// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2015-01-05 16:05:53 +00:00
use std;
use python::{PythonObject, ToPythonPointer, Python, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone};
use objects::{PyObject, PyType, exc};
2015-05-23 21:52:38 +00:00
#[cfg(feature="python27-sys")]
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-06-27 21:49:53 +00:00
use std::ffi::CString;
2015-01-05 16:05:53 +00:00
2015-06-27 20:45:35 +00:00
/// Represents a Python exception that was raised.
#[derive(Debug)]
pub struct PyErr {
2015-04-18 22:39:04 +00:00
/// The type of the exception. This should be either a `PyClass` or a `PyType`.
pub ptype : PyObject,
2015-04-18 22:39:04 +00:00
/// 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.
pub pvalue : Option<PyObject>,
2015-04-18 22:39:04 +00:00
/// The `PyTraceBack` object associated with the error.
pub ptraceback : Option<PyObject>
2015-01-05 16:05:53 +00:00
}
2015-06-27 20:45:35 +00:00
/// Represents the result of a Python call.
pub type PyResult<T> = Result<T, PyErr>;
2015-01-05 16:05:53 +00:00
impl PyErr {
2015-06-27 20:45:35 +00:00
/// Gets whether an error is present in the Python interpreter's global state.
2015-01-05 15:34:12 +00:00
#[inline]
pub fn occurred(_ : Python) -> bool {
2015-01-05 16:05:53 +00:00
unsafe { !ffi::PyErr_Occurred().is_null() }
}
2015-06-27 20:45:35 +00:00
/// Retrieves the current error from the Python interpreter's global state.
/// The error is cleared from the Python interpreter.
2015-04-18 22:39:04 +00:00
/// If no error is set, returns a `SystemError`.
pub fn fetch(py : Python) -> PyErr {
2015-01-05 16:05:53 +00:00
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, ptype: *mut ffi::PyObject, pvalue: *mut ffi::PyObject, ptraceback: *mut ffi::PyObject) -> PyErr {
2015-01-05 15:34:12 +00:00
// 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() {
py.get_type::<exc::SystemError>().into_object()
2015-01-05 15:34:12 +00:00
} else {
PyObject::from_owned_ptr(py, ptype)
2015-01-05 15:34:12 +00:00
},
pvalue: PyObject::from_owned_ptr_opt(py, pvalue),
ptraceback: PyObject::from_owned_ptr_opt(py, ptraceback)
}
}
2015-06-27 20:45:35 +00:00
/// Creates a new PyErr of type `T`.
///
/// `value` can be:
/// * `NoArgs`: the exception instance will be created using python `T()`
/// * a tuple: the exception instance will be created using python `T(*tuple)`
/// * any other value: the exception instance will be created using python `T(value)`
///
/// Panics if `T` is not a python class derived from `BaseException`.
pub fn new<T, V>(py: Python, value: V) -> PyErr
where T: PythonObjectWithTypeObject, V: ToPyObject
{
PyErr::new_helper(py.get_type::<T>(), value.to_py_object(py).into_object())
}
fn new_helper(ty: PyType, value: PyObject) -> PyErr {
assert!(unsafe { ffi::PyExceptionClass_Check(ty.as_object().as_ptr()) } != 0);
PyErr {
ptype: ty.into_object(),
pvalue: Some(value),
ptraceback: None
}
}
/// Creates a new PyErr.
2015-04-18 22:39:04 +00:00
///
/// `obj` must be an Python exception instance, the PyErr will use that instance.
2015-06-27 20:45:35 +00:00
/// If `obj` is a Python exception type object, the PyErr will (lazily) create a new instance of that type.
/// Otherwise, a `TypeError` is created instead.
pub fn from_instance<O>(obj: O, py: Python) -> PyErr where O: PythonObject {
PyErr::from_instance_helper(obj.into_object(), py)
}
2015-06-27 20:45:35 +00:00
fn from_instance_helper(obj: PyObject, py: Python) -> PyErr {
2015-01-11 03:21:05 +00:00
if unsafe { ffi::PyExceptionInstance_Check(obj.as_ptr()) } != 0 {
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 {
PyErr {
ptype: obj,
pvalue: None,
ptraceback: None
}
} else {
PyErr {
ptype: py.get_type::<exc::TypeError>().into_object(),
2015-05-23 21:52:38 +00:00
pvalue: Some("exceptions must derive from BaseException".to_py_object(py).into_object()),
ptraceback: None
}
2015-01-05 15:34:12 +00:00
}
}
2015-06-27 20:45:35 +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()`.
/// `value` is the exception instance, or a tuple of arguments to pass to the exception constructor.
#[inline]
pub fn new_lazy_init(exc: PyType, value: Option<PyObject>) -> PyErr {
2015-01-05 15:34:12 +00:00
PyErr {
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, py: Python) {
self.restore(py);
2015-01-05 15:34:12 +00:00
unsafe { ffi::PyErr_PrintEx(0) }
}
/// Print a standard traceback to sys.stderr.
pub fn print_and_set_sys_last_vars(self, py: Python) {
self.restore(py);
2015-01-05 15:34:12 +00:00
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]
pub fn matches(&self, exc: &PyObject, _py: Python) -> bool {
2015-01-05 15:34:12 +00:00
unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc.as_ptr()) != 0 }
}
2015-06-27 20:45:35 +00:00
2015-01-05 15:34:12 +00:00
/// Normalizes the error. This ensures that the exception value is an instance of the exception type.
pub fn normalize(&mut self, py: Python) {
2015-01-05 15:34:12 +00:00
// The normalization helper function involves temporarily moving out of the &mut self,
// which requires some unsafe trickery:
unsafe {
std::ptr::write(self, std::ptr::read(self).into_normalized(py));
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()
fn into_normalized(self, py: Python) -> PyErr {
2015-01-05 15:34:12 +00:00
let PyErr { ptype, pvalue, ptraceback } = self;
let mut ptype = ptype.steal_ptr();
let mut pvalue = pvalue.steal_ptr(py);
let mut ptraceback = ptraceback.steal_ptr(py);
2015-01-05 15:34:12 +00:00
unsafe {
ffi::PyErr_NormalizeException(&mut ptype, &mut pvalue, &mut ptraceback);
PyErr::new_from_ffi_tuple(py, ptype, pvalue, ptraceback)
}
}
2015-06-27 20:45:35 +00:00
2015-01-06 00:04:25 +00:00
/// Retrieves the exception type.
2015-06-27 20:45:35 +00:00
///
2015-04-18 22:39:04 +00:00
/// If the exception type is an old-style class, returns `oldstyle::PyClass`.
2015-05-23 21:52:38 +00:00
#[cfg(feature="python27-sys")]
pub fn get_type(&self, py: Python) -> PyType {
match self.ptype.cast_as::<PyType>(py) {
Ok(t) => t.clone_ref(py),
2015-01-11 03:21:05 +00:00
Err(_) =>
match self.ptype.cast_as::<PyClass>(py) {
2015-01-11 03:21:05 +00:00
Ok(_) => py.get_type::<PyClass>(),
Err(_) => py.None().get_type().clone_ref(py)
2015-01-11 03:21:05 +00:00
}
2015-01-06 00:04:25 +00:00
}
}
2015-06-27 20:45:35 +00:00
2015-05-23 21:52:38 +00:00
/// Retrieves the exception type.
#[cfg(not(feature="python27-sys"))]
pub fn get_type(&self, py: Python) -> PyType {
match self.ptype.cast_as::<PyType>(py) {
Ok(t) => t.clone_ref(py),
Err(_) => py.None().get_type().clone_ref(py)
2015-05-23 21:52:38 +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-04-18 22:39:04 +00:00
/// This method takes `&mut self` because the error might need
2015-01-06 00:04:25 +00:00
/// to be normalized in order to create the exception instance.
pub fn instance(&mut self, py: Python) -> PyObject {
self.normalize(py);
2015-01-05 15:34:12 +00:00
match self.pvalue {
Some(ref instance) => instance.clone_ref(py),
None => py.None()
2015-01-05 16:05:53 +00:00
}
}
2015-06-27 20:45:35 +00:00
/// Writes the error back to the Python interpreter's global state.
2015-04-18 22:39:04 +00:00
/// This is the opposite of `PyErr::fetch()`.
#[inline]
pub fn restore(self, py: Python) {
2015-01-05 16:05:53 +00:00
let PyErr { ptype, pvalue, ptraceback } = self;
unsafe {
ffi::PyErr_Restore(ptype.steal_ptr(), pvalue.steal_ptr(py), ptraceback.steal_ptr(py))
2015-01-05 16:05:53 +00:00
}
}
2015-06-27 20:45:35 +00:00
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, category: &PyObject, message: &str, stacklevel: i32) -> PyResult<()> {
2015-04-18 22:39:04 +00:00
let message = CString::new(message).unwrap();
2015-01-11 03:21:05 +00:00
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-06-27 20:45:35 +00:00
/// Converts `PythonObjectDowncastError` to Python `TypeError`.
impl <'p> std::convert::From<PythonObjectDowncastError<'p>> for PyErr {
fn from(err: PythonObjectDowncastError<'p>) -> PyErr {
PyErr::new_lazy_init(err.0.get_type::<exc::TypeError>(), None)
}
2015-01-05 16:05:53 +00:00
}
2015-06-27 20:45:35 +00:00
/// Construct PyObject from the result of a Python FFI call that returns a new reference (owned pointer).
2015-04-18 22:39:04 +00:00
/// Returns `Err(PyErr)` if the pointer is `null`.
2015-01-05 16:05:53 +00:00
/// Unsafe because the pointer might be invalid.
#[inline]
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 {
Ok(PyObject::from_owned_ptr(py, p))
2015-01-05 16:05:53 +00:00
}
}
2015-06-27 21:49:53 +00:00
fn panic_after_error(_py: Python) -> ! {
2015-04-18 22:39:04 +00:00
unsafe { ffi::PyErr_Print(); }
panic!("Python API called failed");
}
#[inline]
pub unsafe fn from_owned_ptr_or_panic(py : Python, p : *mut ffi::PyObject) -> PyObject {
if p.is_null() {
2015-04-18 22:39:04 +00:00
panic_after_error(py);
} else {
PyObject::from_owned_ptr(py, p)
}
}
pub unsafe fn result_cast_from_owned_ptr<T>(py : Python, p : *mut ffi::PyObject) -> PyResult<T>
where T: ::python::PythonObjectWithCheckedDowncast
2015-01-11 03:21:05 +00:00
{
if p.is_null() {
Err(PyErr::fetch(py))
} else {
Ok(try!(PyObject::from_owned_ptr(py, p).cast_into(py)))
2015-01-11 03:21:05 +00:00
}
}
pub unsafe fn cast_from_owned_ptr_or_panic<T>(py : Python, p : *mut ffi::PyObject) -> T
where T: ::python::PythonObjectWithCheckedDowncast
2015-04-18 22:39:04 +00:00
{
if p.is_null() {
panic_after_error(py);
} else {
PyObject::from_owned_ptr(py, p).cast_into(py).unwrap()
2015-04-18 22:39:04 +00:00
}
}
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-06-27 21:49:53 +00:00
use objects::exc;
2015-01-04 15:32:52 +00:00
#[test]
fn set_typeerror() {
let gil = Python::acquire_gil();
let py = gil.python();
PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None).restore(py);
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