pyo3/src/err.rs

399 lines
14 KiB
Rust
Raw Normal View History

2015-01-05 16:05:53 +00:00
use std;
2017-05-25 03:31:51 +00:00
use std::ffi::CString;
2015-01-05 16:05:53 +00:00
use libc;
2017-05-25 03:31:51 +00:00
use ffi;
use pyptr::{Py, PyPtr};
use python::{ToPythonPointer, Python, IntoPythonPointer};
use objects::{PyObject, PyType, exc};
use typeob::{PyTypeObject};
2017-05-14 21:42:56 +00:00
use conversion::{ToPyObject, ToPyTuple};
2015-01-05 16:05:53 +00:00
/**
Defines a new exception type.
# Syntax
`py_exception!(module, MyError)`
* `module` is the name of the containing module.
* `MyError` is the name of the new exception type.
# Example
```
#[macro_use]
2017-05-13 05:43:17 +00:00
extern crate pyo3;
2017-05-13 05:43:17 +00:00
use pyo3::{Python, PyDict};
py_exception!(mymodule, CustomError);
fn main() {
2017-05-19 04:35:08 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
let ctx = PyDict::new(py);
ctx.set_item(py, "CustomError", py.get_type::<CustomError>()).unwrap();
py.run("assert str(CustomError) == \"<class 'mymodule.CustomError'>\"", None, Some(&ctx)).unwrap();
py.run("assert CustomError('oops').args == ('oops',)", None, Some(&ctx)).unwrap();
}
```
*/
#[macro_export]
macro_rules! py_exception {
($module: ident, $name: ident, $base: ty) => {
2017-05-25 03:31:51 +00:00
pub struct $name;
pyobject_newtype!($name);
impl $name {
pub fn new<'p, T: $crate::ToPyObject>(py: $crate::Python<'p>, args: T) -> $crate::PyErr {
$crate::PyErr::new::<$name, T>(py, args)
}
}
2017-05-19 04:35:08 +00:00
impl $crate::PyTypeObject for $name {
#[inline]
2017-05-19 04:35:08 +00:00
fn type_name() -> &'static str {
"$name"
}
#[inline]
fn type_object(py: $crate::Python) -> $crate::PyType {
unsafe {
2017-05-17 06:43:39 +00:00
static mut type_object: *mut $crate::ffi::PyTypeObject = 0 as *mut $crate::ffi::PyTypeObject;
if type_object.is_null() {
type_object = $crate::PyErr::new_type(
py,
concat!(stringify!($module), ".", stringify!($name)),
Some($crate::PythonObject::into_object(py.get_type::<$base>())),
None).as_type_ptr();
}
$crate::PyType::from_type_ptr(py, type_object)
}
}
}
};
($module: ident, $name: ident) => {
py_exception!($module, $name, $crate::exc::Exception);
}
}
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`.
2017-05-25 03:31:51 +00:00
pub ptype: PyPtr<PyType>,
2015-04-18 22:39:04 +00:00
/// The value of the exception.
///
2015-04-18 22:39:04 +00:00
/// 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.
2017-05-25 03:31:51 +00:00
pub pvalue: Option<PyPtr<PyObject>>,
2015-04-18 22:39:04 +00:00
/// The `PyTraceBack` object associated with the error.
2017-05-25 03:31:51 +00:00
pub ptraceback: Option<PyPtr<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
2017-05-25 03:31:51 +00:00
// Marker type that indicates an error while downcasting
pub struct PyDowncastError<'p>(pub Python<'p>, pub Option<&'p str>);
impl PyErr {
2017-01-27 20:51:56 +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`.
///
/// Example:
/// `return Err(PyErr::new::<exc::TypeError, _>(py, "Error message"));`
pub fn new<T, V>(py: Python, value: V) -> PyErr
2017-05-19 04:35:08 +00:00
where T: PyTypeObject, V: ToPyObject
2017-01-27 20:51:56 +00:00
{
2017-05-25 03:31:51 +00:00
PyErr::new_helper(py, py.get_ptype::<T>(), value.to_object(py).into_pptr())
2017-01-27 20:51:56 +00:00
}
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() }
}
/// Creates a new exception type with the given name, which must be of the form
/// `<module>.<ExceptionName>`, as required by `PyErr_NewException`.
///
/// `base` can be an existing exception type to subclass, or a tuple of classes
/// `dict` specifies an optional dictionary of class variables and methods
2017-05-25 03:31:51 +00:00
/*pub fn new_type(py: Python, name: &str,
base: Option<PyObject>, dict: Option<PyObject>) -> PyType {
let base: *mut ffi::PyObject = match base {
None => ptr::null_mut(),
Some(obj) => obj.steal_ptr()
};
let dict: *mut ffi::PyObject = match dict {
None => ptr::null_mut(),
2017-05-25 03:31:51 +00:00
Some(obj) => obj.into_ptr(),
};
unsafe {
let null_terminated_name = CString::new(name).unwrap();
2017-05-25 03:31:51 +00:00
let ptr: *mut ffi::PyObject = ffi::PyErr_NewException(
null_terminated_name.as_ptr() as *mut c_char, base, dict);
PyObject::from_borrowed_ptr(py, ptr).unchecked_cast_into::<PyType>()
}
2017-05-25 03:31:51 +00:00
}*/
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`.
2017-05-25 03:31:51 +00:00
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)
}
}
2017-05-25 03:31:51 +00:00
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() {
2017-05-25 03:31:51 +00:00
py.get_ptype::<exc::SystemError>()
} else {
Py::<PyType>::from_borrowed_ptr(py, ptype).into_pptr()
2017-05-25 03:31:51 +00:00
},
pvalue: PyObject::from_borrowed_pptr_opt(py, pvalue),
ptraceback: PyObject::from_borrowed_pptr_opt(py, ptraceback)
}
}
2015-06-27 20:45:35 +00:00
2017-05-25 03:31:51 +00:00
fn new_helper(_py: Python, ty: PyPtr<PyType>, value: PyPtr<PyObject>) -> PyErr {
assert!(unsafe { ffi::PyExceptionClass_Check(ty.as_ptr()) } != 0);
PyErr {
2017-05-25 03:31:51 +00:00
ptype: ty,
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.
2017-05-25 03:31:51 +00:00
pub fn from_instance<O>(py: Python, obj: O) -> PyErr where O: ToPyObject {
PyErr::from_instance_helper(py, obj.to_object(py))
}
2015-06-27 20:45:35 +00:00
2017-05-25 03:31:51 +00:00
fn from_instance_helper<'p>(py: Python<'p>, obj: Py<'p, PyObject>) -> PyErr {
2015-01-11 03:21:05 +00:00
if unsafe { ffi::PyExceptionInstance_Check(obj.as_ptr()) } != 0 {
PyErr {
2017-05-25 03:31:51 +00:00
ptype: unsafe { Py::<PyType>::from_borrowed_ptr(
py, ffi::PyExceptionInstance_Class(obj.as_ptr())).into_pptr() },
pvalue: Some(obj.into_pptr()),
ptraceback: None
}
2015-01-11 03:21:05 +00:00
} else if unsafe { ffi::PyExceptionClass_Check(obj.as_ptr()) } != 0 {
PyErr {
2017-05-25 03:31:51 +00:00
ptype: unsafe { Py::<PyType>::unchecked_downcast_from(obj).into_pptr() },
pvalue: None,
ptraceback: None
}
} else {
PyErr {
2017-05-25 03:31:51 +00:00
ptype: py.get_ptype::<exc::TypeError>(),
pvalue: Some("exceptions must derive from BaseException".to_object(py).into_pptr()),
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.
2017-01-27 20:51:56 +00:00
/// `exc` is the exception type; usually one of the standard exceptions like `py.get_type::<exc::RuntimeError>()`.
/// `value` is the exception instance, or a tuple of arguments to pass to the exception constructor.
#[inline]
2017-05-25 03:31:51 +00:00
pub fn new_lazy_init(exc: PyPtr<PyType>, value: Option<PyPtr<PyObject>>) -> PyErr {
2015-01-05 15:34:12 +00:00
PyErr {
2017-05-25 03:31:51 +00:00
ptype: exc,
2015-01-05 15:34:12 +00:00
pvalue: value,
ptraceback: None
}
}
2017-04-30 04:31:35 +00:00
/// Construct a new error, with the usual lazy initialization of Python exceptions.
/// `exc` is the exception type; usually one of the standard exceptions like `py.get_type::<exc::RuntimeError>()`.
/// `args` is the a tuple of arguments to pass to the exception constructor.
#[inline]
2017-05-25 03:31:51 +00:00
pub fn new_err<'p, A>(py: Python<'p>, exc: Py<'p, PyType>, args: A) -> PyErr
where A: 'p + ToPyTuple
2017-04-30 04:31:35 +00:00
{
2017-05-25 03:31:51 +00:00
let exc = exc.clone_ref();
let pval = args.to_py_tuple(py);
2017-04-30 04:31:35 +00:00
PyErr {
2017-05-25 03:31:51 +00:00
ptype: exc.into_pptr(),
pvalue: Some(pval.into_object().into_pptr()),
2017-04-30 04:31:35 +00:00
ptraceback: None
}
}
2015-01-05 15:34:12 +00:00
/// 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.
pub fn matches<T>(&self, py: Python, exc: T) -> bool
where T: ToPyObject
{
exc.with_borrowed_ptr(py, |exc| unsafe {
ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc) != 0
})
2015-01-05 15:34:12 +00:00
}
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.
}
2015-01-05 15:34:12 +00:00
/// 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;
2017-05-25 03:31:51 +00:00
let mut ptype = ptype.into_ptr();
let mut pvalue = pvalue.into_ptr();
let mut ptraceback = ptraceback.into_ptr();
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.
2017-05-25 03:31:51 +00:00
pub fn get_type<'p>(&self, py: Python<'p>) -> Py<'p, PyType> {
self.ptype.as_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.
2017-05-25 03:31:51 +00:00
pub fn instance<'p>(&mut self, py: Python<'p>) -> Py<'p, PyObject> {
self.normalize(py);
2015-01-05 15:34:12 +00:00
match self.pvalue {
2017-05-25 03:31:51 +00:00
Some(ref instance) => instance.as_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]
2017-05-25 03:31:51 +00:00
pub fn restore(self, _py: Python) {
2015-01-05 16:05:53 +00:00
let PyErr { ptype, pvalue, ptraceback } = self;
unsafe {
2017-05-25 03:31:51 +00:00
ffi::PyErr_Restore(ptype.into_ptr(), pvalue.into_ptr(), ptraceback.into_ptr())
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
2017-05-25 03:31:51 +00:00
/*impl PyClone for PyErr {
fn clone_ref(&self, py: Python) -> PyErr {
PyErr {
ptype: self.ptype.clone_ref(py),
pvalue: self.pvalue.clone_ref(py),
ptraceback: self.ptraceback.clone_ref(py)
}
}
2017-05-25 03:31:51 +00:00
}*/
/// Converts `PyDowncastError` to Python `TypeError`.
impl <'p> std::convert::From<PyDowncastError<'p>> for PyErr {
fn from(err: PyDowncastError<'p>) -> PyErr {
PyErr::new_lazy_init(err.0.get_ptype::<exc::TypeError>(), None)
}
}
2017-05-25 03:31:51 +00:00
impl <'p> std::fmt::Debug for PyDowncastError<'p> {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.write_str("PyDowncastError")
}
2015-01-05 16:05:53 +00:00
}
2017-04-06 17:09:32 +00:00
/// Convert PyErr to io::Error
impl std::convert::From<PyErr> for std::io::Error {
fn from(err: PyErr) -> Self {
std::io::Error::new(
std::io::ErrorKind::Other, format!("Python exception: {:?}", err))
}
}
pub fn panic_after_error(_py: Python) -> ! {
2015-04-18 22:39:04 +00:00
unsafe { ffi::PyErr_Print(); }
panic!("Python API called failed");
}
2015-01-04 04:50:28 +00:00
/// Returns Ok if the error code is not -1.
#[inline]
2017-05-25 03:31:51 +00:00
pub fn error_on_minusone(py: Python, result: libc::c_int) -> PyResult<()> {
2015-01-04 04:50:28 +00:00
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() {
2017-05-25 03:31:51 +00:00
let gil = Python::acqduire_gil();
2015-01-04 15:32:52 +00:00
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
}
}