Make exceptions proper native types.
This commit is contained in:
parent
496c626835
commit
a7e0c6bfa7
|
@ -198,13 +198,12 @@ impl Names {
|
|||
# let gil = Python::acquire_gil();
|
||||
# let py = gil.python();
|
||||
# let names = PyCell::new(py, Names::new()).unwrap();
|
||||
# let borrow_mut_err = py.get_type::<pyo3::pycell::PyBorrowMutError>();
|
||||
# pyo3::py_run!(py, names borrow_mut_err, r"
|
||||
# pyo3::py_run!(py, names, r"
|
||||
# try:
|
||||
# names.merge(names)
|
||||
# assert False, 'Unreachable'
|
||||
# except RuntimeError as e:
|
||||
# isinstance(e, borrow_mut_err)
|
||||
# assert str(e) == 'Already borrowed'
|
||||
# ");
|
||||
```
|
||||
`Names` has a `merge` method, which takes `&mut self` and another argument of type `&mut Self`.
|
||||
|
|
22
src/err.rs
22
src/err.rs
|
@ -344,13 +344,17 @@ impl PyErr {
|
|||
///
|
||||
/// This method takes `mut self` because the error might need
|
||||
/// to be normalized in order to create the exception instance.
|
||||
fn instance(mut self, py: Python) -> Py<exceptions::BaseException> {
|
||||
pub fn instance(mut self, py: Python) -> &exceptions::BaseException {
|
||||
self.normalize(py);
|
||||
let r = match self.pvalue {
|
||||
PyErrValue::Value(ref instance) => instance.clone_ref(py).extract(py),
|
||||
_ => Err(PyDowncastError.into()),
|
||||
};
|
||||
r.expect("Normalized error instance should be a BaseException")
|
||||
match self.pvalue {
|
||||
PyErrValue::Value(ref instance) => {
|
||||
let any: &PyAny = unsafe { py.from_owned_ptr(instance.clone_ref(py).into_ptr()) };
|
||||
any.downcast()
|
||||
.expect("Normalized error instance should be a BaseException")
|
||||
}
|
||||
PyErrValue::None => panic!("This exception is not an instance"),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the error back to the Python interpreter's global state.
|
||||
|
@ -437,7 +441,7 @@ impl FromPy<PyErr> for PyObject {
|
|||
|
||||
impl FromPy<PyErr> for Py<exceptions::BaseException> {
|
||||
fn from_py(other: PyErr, py: Python) -> Self {
|
||||
other.instance(py)
|
||||
other.instance(py).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,7 +462,7 @@ impl<'a> IntoPy<PyObject> for &'a PyErr {
|
|||
/// Convert `PyDowncastError` to Python `TypeError`.
|
||||
impl std::convert::From<PyDowncastError> for PyErr {
|
||||
fn from(_err: PyDowncastError) -> PyErr {
|
||||
exceptions::TypeError.into()
|
||||
exceptions::TypeError::py_err(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -607,7 +611,7 @@ mod tests {
|
|||
fn set_typeerror() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let err: PyErr = exceptions::TypeError.into();
|
||||
let err: PyErr = exceptions::TypeError::py_err(());
|
||||
err.restore(py);
|
||||
assert!(PyErr::occurred(py));
|
||||
drop(PyErr::fetch(py));
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
//! Exception types defined by Python.
|
||||
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::ffi;
|
||||
use crate::type_object::PyTypeObject;
|
||||
use crate::types::{PyAny, PyTuple};
|
||||
use crate::Python;
|
||||
use crate::{AsPyPointer, ToPyObject};
|
||||
use crate::{AsPyRef, Py, PyDowncastError, PyTryFrom};
|
||||
use crate::type_object::PySizedLayout;
|
||||
use crate::{ffi, AsPyPointer, PyResult, Python};
|
||||
use std::ffi::CStr;
|
||||
use std::ops;
|
||||
use std::os::raw::c_char;
|
||||
|
@ -17,25 +13,64 @@ use std::os::raw::c_char;
|
|||
#[macro_export]
|
||||
macro_rules! impl_exception_boilerplate {
|
||||
($name: ident) => {
|
||||
impl ::std::convert::From<$name> for $crate::PyErr {
|
||||
impl std::convert::From<$name> for $crate::PyErr {
|
||||
fn from(_err: $name) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, _>(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ::std::convert::Into<$crate::PyResult<T>> for $name {
|
||||
impl<T> std::convert::Into<$crate::PyResult<T>> for $name {
|
||||
fn into(self) -> $crate::PyResult<T> {
|
||||
$crate::PyErr::new::<$name, _>(()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn py_err<T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<Self, T>(args)
|
||||
pub fn py_err<V: $crate::ToPyObject + 'static>(args: V) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, V>(args)
|
||||
}
|
||||
pub fn into<R, V: $crate::ToPyObject + 'static>(args: V) -> $crate::PyResult<R> {
|
||||
$crate::PyErr::new::<$name, V>(args).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into<R, T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyResult<R> {
|
||||
$crate::PyErr::new::<Self, T>(args).into()
|
||||
impl std::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use $crate::AsPyPointer;
|
||||
let py_type_name =
|
||||
unsafe { std::ffi::CStr::from_ptr((*(*self.as_ptr()).ob_type).tp_name) };
|
||||
let type_name = py_type_name.to_string_lossy();
|
||||
f.debug_struct(&*type_name)
|
||||
// TODO: print out actual fields!
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use $crate::AsPyPointer;
|
||||
let py_type_name =
|
||||
unsafe { std::ffi::CStr::from_ptr((*(*self.as_ptr()).ob_type).tp_name) };
|
||||
let type_name = py_type_name.to_string_lossy();
|
||||
write!(f, "{}", type_name)?;
|
||||
if let Ok(s) = self.str() {
|
||||
write!(f, ": {}", &s.to_string_lossy())
|
||||
} else {
|
||||
write!(f, ": <exception str() failed>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for $name {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
unsafe {
|
||||
use $crate::{AsPyPointer, PyNativeType};
|
||||
let cause: &$crate::exceptions::BaseException = self
|
||||
.py()
|
||||
.from_owned_ptr_or_opt($crate::ffi::PyException_GetCause(self.as_ptr()))?;
|
||||
|
||||
Some(cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -75,24 +110,35 @@ macro_rules! impl_exception_boilerplate {
|
|||
#[macro_export]
|
||||
macro_rules! import_exception {
|
||||
($module: expr, $name: ident) => {
|
||||
#[repr(transparent)]
|
||||
#[allow(non_camel_case_types)] // E.g. `socket.herror`
|
||||
pub struct $name;
|
||||
pub struct $name($crate::PyAny);
|
||||
|
||||
$crate::impl_exception_boilerplate!($name);
|
||||
|
||||
$crate::import_exception_type_object!($module, $name);
|
||||
};
|
||||
}
|
||||
$crate::pyobject_native_type_core!(
|
||||
$name,
|
||||
$crate::ffi::PyBaseExceptionObject,
|
||||
*$name::type_object_raw($crate::Python::assume_gil_acquired()),
|
||||
Some(stringify!($module)),
|
||||
$name::check
|
||||
);
|
||||
|
||||
/// `impl $crate::type_object::PyTypeObject for $name` where `$name` is an
|
||||
/// exception defined in Python code.
|
||||
#[macro_export]
|
||||
macro_rules! import_exception_type_object {
|
||||
($module: expr, $name: ident) => {
|
||||
unsafe impl $crate::type_object::PyTypeObject for $name {
|
||||
fn type_object(py: $crate::Python) -> &$crate::types::PyType {
|
||||
impl $name {
|
||||
/// Check if a python object is an instance of this exception.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid pointer to a Python object
|
||||
unsafe fn check(ptr: *mut $crate::ffi::PyObject) -> $crate::libc::c_int {
|
||||
$crate::ffi::PyObject_TypeCheck(
|
||||
ptr,
|
||||
Self::type_object_raw($crate::Python::assume_gil_acquired()) as *mut _
|
||||
)
|
||||
}
|
||||
|
||||
fn type_object_raw(py: $crate::Python) -> *mut $crate::ffi::PyTypeObject {
|
||||
use $crate::once_cell::GILOnceCell;
|
||||
use $crate::AsPyRef;
|
||||
use $crate::AsPyPointer;
|
||||
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
|
||||
GILOnceCell::new();
|
||||
|
||||
|
@ -111,7 +157,7 @@ macro_rules! import_exception_type_object {
|
|||
cls.extract()
|
||||
.expect("Imported exception should be a type object")
|
||||
})
|
||||
.as_ref(py)
|
||||
.as_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -158,8 +204,9 @@ macro_rules! import_exception_type_object {
|
|||
#[macro_export]
|
||||
macro_rules! create_exception {
|
||||
($module: ident, $name: ident, $base: ty) => {
|
||||
#[repr(transparent)]
|
||||
#[allow(non_camel_case_types)] // E.g. `socket.herror`
|
||||
pub struct $name;
|
||||
pub struct $name($crate::PyAny);
|
||||
|
||||
$crate::impl_exception_boilerplate!($name);
|
||||
|
||||
|
@ -172,10 +219,29 @@ macro_rules! create_exception {
|
|||
#[macro_export]
|
||||
macro_rules! create_exception_type_object {
|
||||
($module: ident, $name: ident, $base: ty) => {
|
||||
unsafe impl $crate::type_object::PyTypeObject for $name {
|
||||
fn type_object(py: $crate::Python) -> &$crate::types::PyType {
|
||||
$crate::pyobject_native_type_core!(
|
||||
$name,
|
||||
$crate::ffi::PyBaseExceptionObject,
|
||||
*$name::type_object_raw($crate::Python::assume_gil_acquired()),
|
||||
Some(stringify!($module)),
|
||||
$name::check
|
||||
);
|
||||
|
||||
impl $name {
|
||||
/// Check if a python object is an instance of this exception.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid pointer to a Python object
|
||||
unsafe fn check(ptr: *mut $crate::ffi::PyObject) -> $crate::libc::c_int {
|
||||
$crate::ffi::PyObject_TypeCheck(
|
||||
ptr,
|
||||
Self::type_object_raw($crate::Python::assume_gil_acquired()) as *mut _
|
||||
)
|
||||
}
|
||||
|
||||
fn type_object_raw(py: $crate::Python) -> *mut $crate::ffi::PyTypeObject {
|
||||
use $crate::once_cell::GILOnceCell;
|
||||
use $crate::AsPyRef;
|
||||
use $crate::AsPyPointer;
|
||||
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
|
||||
GILOnceCell::new();
|
||||
|
||||
|
@ -192,83 +258,45 @@ macro_rules! create_exception_type_object {
|
|||
.as_ptr() as *mut $crate::ffi::PyObject,
|
||||
)
|
||||
})
|
||||
.as_ref(py)
|
||||
.as_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_native_exception (
|
||||
($name:ident, $exc_name:ident) => (
|
||||
pub struct $name;
|
||||
($name:ident, $exc_name:ident, $layout:path) => (
|
||||
pub struct $name($crate::PyAny);
|
||||
|
||||
impl std::convert::From<$name> for PyErr {
|
||||
fn from(_err: $name) -> PyErr {
|
||||
PyErr::new::<$name, _>(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::Into<$crate::PyResult<T>> for $name {
|
||||
fn into(self) -> $crate::PyResult<T> {
|
||||
PyErr::new::<$name, _>(()).into()
|
||||
}
|
||||
}
|
||||
$crate::impl_exception_boilerplate!($name);
|
||||
|
||||
impl $name {
|
||||
pub fn py_err<V: ToPyObject + 'static>(args: V) -> PyErr {
|
||||
PyErr::new::<$name, V>(args)
|
||||
}
|
||||
pub fn into<R, V: ToPyObject + 'static>(args: V) -> PyResult<R> {
|
||||
PyErr::new::<$name, V>(args).into()
|
||||
/// Check if a python object is an instance of this exception.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid pointer to a Python object
|
||||
unsafe fn check(ptr: *mut $crate::ffi::PyObject) -> $crate::libc::c_int {
|
||||
ffi::PyObject_TypeCheck(ptr, ffi::$exc_name as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl PyTypeObject for $name {
|
||||
fn type_object(py: $crate::Python) -> &$crate::types::PyType {
|
||||
unsafe { py.from_borrowed_ptr(ffi::$exc_name) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> PyTryFrom<'v> for $name {
|
||||
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
|
||||
unsafe {
|
||||
let value = value.into();
|
||||
if ffi::PyObject_TypeCheck(value.as_ptr(), ffi::$exc_name as *mut _) != 0 {
|
||||
Ok(PyTryFrom::try_from_unchecked(value))
|
||||
} else {
|
||||
Err(PyDowncastError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
|
||||
unsafe {
|
||||
let value = value.into();
|
||||
if (*value.as_ptr()).ob_type == ffi::$exc_name as *mut _ {
|
||||
Ok(PyTryFrom::try_from_unchecked(value))
|
||||
} else {
|
||||
Err(PyDowncastError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
|
||||
&*(value.into().as_ptr() as *const _)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPyPointer for $name {
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
return self as *const _ as *const _ as *mut ffi::PyObject;
|
||||
}
|
||||
}
|
||||
$crate::pyobject_native_type_core!($name, $layout, *(ffi::$exc_name as *mut ffi::PyTypeObject), Some("builtins"), $name::check);
|
||||
);
|
||||
($name:ident, $exc_name:ident) => (
|
||||
impl_native_exception!($name, $exc_name, ffi::PyBaseExceptionObject);
|
||||
)
|
||||
);
|
||||
|
||||
impl PySizedLayout<BaseException> for ffi::PyBaseExceptionObject {}
|
||||
|
||||
impl_native_exception!(BaseException, PyExc_BaseException);
|
||||
impl_native_exception!(Exception, PyExc_Exception);
|
||||
impl_native_exception!(StopAsyncIteration, PyExc_StopAsyncIteration);
|
||||
impl_native_exception!(StopIteration, PyExc_StopIteration);
|
||||
impl_native_exception!(
|
||||
StopIteration,
|
||||
PyExc_StopIteration,
|
||||
ffi::PyStopIterationObject
|
||||
);
|
||||
impl_native_exception!(GeneratorExit, PyExc_GeneratorExit);
|
||||
impl_native_exception!(ArithmeticError, PyExc_ArithmeticError);
|
||||
impl_native_exception!(LookupError, PyExc_LookupError);
|
||||
|
@ -278,7 +306,7 @@ impl_native_exception!(AttributeError, PyExc_AttributeError);
|
|||
impl_native_exception!(BufferError, PyExc_BufferError);
|
||||
impl_native_exception!(EOFError, PyExc_EOFError);
|
||||
impl_native_exception!(FloatingPointError, PyExc_FloatingPointError);
|
||||
impl_native_exception!(OSError, PyExc_OSError);
|
||||
impl_native_exception!(OSError, PyExc_OSError, ffi::PyOSErrorObject);
|
||||
impl_native_exception!(ImportError, PyExc_ImportError);
|
||||
|
||||
#[cfg(Py_3_6)]
|
||||
|
@ -293,13 +321,13 @@ impl_native_exception!(OverflowError, PyExc_OverflowError);
|
|||
impl_native_exception!(RuntimeError, PyExc_RuntimeError);
|
||||
impl_native_exception!(RecursionError, PyExc_RecursionError);
|
||||
impl_native_exception!(NotImplementedError, PyExc_NotImplementedError);
|
||||
impl_native_exception!(SyntaxError, PyExc_SyntaxError);
|
||||
impl_native_exception!(SyntaxError, PyExc_SyntaxError, ffi::PySyntaxErrorObject);
|
||||
impl_native_exception!(ReferenceError, PyExc_ReferenceError);
|
||||
impl_native_exception!(SystemError, PyExc_SystemError);
|
||||
impl_native_exception!(SystemExit, PyExc_SystemExit);
|
||||
impl_native_exception!(SystemExit, PyExc_SystemExit, ffi::PySystemExitObject);
|
||||
impl_native_exception!(TypeError, PyExc_TypeError);
|
||||
impl_native_exception!(UnboundLocalError, PyExc_UnboundLocalError);
|
||||
impl_native_exception!(UnicodeError, PyExc_UnicodeError);
|
||||
impl_native_exception!(UnicodeError, PyExc_UnicodeError, ffi::PyUnicodeErrorObject);
|
||||
impl_native_exception!(UnicodeDecodeError, PyExc_UnicodeDecodeError);
|
||||
impl_native_exception!(UnicodeEncodeError, PyExc_UnicodeEncodeError);
|
||||
impl_native_exception!(UnicodeTranslateError, PyExc_UnicodeTranslateError);
|
||||
|
@ -376,72 +404,6 @@ impl StopIteration {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BaseException {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
// Sneaky: we don’t really need a GIL lock here as nothing should be able to just mutate
|
||||
// the "type" of an object, right? RIGHT???
|
||||
//
|
||||
// let gil = Python::acquire_gil();
|
||||
// let _py = gil.python();
|
||||
let py_type_name = unsafe { CStr::from_ptr((*(*self.as_ptr()).ob_type).tp_name) };
|
||||
let type_name = py_type_name.to_string_lossy();
|
||||
f.debug_struct(&*type_name)
|
||||
// TODO: print out actual fields!
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BaseException {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let py_type_name = unsafe { CStr::from_ptr((*(*self.as_ptr()).ob_type).tp_name) };
|
||||
let type_name = py_type_name.to_string_lossy();
|
||||
write!(f, "{}", type_name)?;
|
||||
let py_self: Py<PyAny> = unsafe { Py::from_borrowed_ptr(self.as_ptr()) };
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
if let Ok(s) = crate::ObjectProtocol::str(&*py_self.as_ref(py)) {
|
||||
write!(f, ": {}", &s.to_string_lossy())
|
||||
} else {
|
||||
write!(f, ": <exception str() failed>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for BaseException {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
unsafe {
|
||||
// Returns either `None` or an instance of an exception.
|
||||
let cause_object = ffi::PyException_GetCause(self.as_ptr());
|
||||
if cause_object == ffi::Py_None() {
|
||||
None
|
||||
} else {
|
||||
// FIXME: PyException_GetCause returns a new reference to the cause object.
|
||||
//
|
||||
// While we know that `self` is "immutable" (`&self`!) it is also true that between
|
||||
// now and when the return value is actually read the GIL could be unlocked and
|
||||
// then concurrent threads could modify `self` to change its `__cause__`.
|
||||
//
|
||||
// If this was not a possibility, we could just `DECREF` here, instead, now, we
|
||||
// must return a `&Py<BaseException>` instead… but we cannot do that because
|
||||
// nothing is storing such a thing anywhere and thus we cannot take a reference to
|
||||
// that…
|
||||
//
|
||||
// The only way to make this function to work sanely, without leaking, is to ensure
|
||||
// that between a call to `Error::source` and drop of the reference there’s no
|
||||
// possible way for for the object to be modified. Even if we had a way to prevent
|
||||
// GIL from unlocking, people could modify the object through a different
|
||||
// reference to the Exception.
|
||||
//
|
||||
// Sounds unsound, doesn’t it?
|
||||
//
|
||||
// ffi::Py_DECREF(cause_object);
|
||||
Some(&*(cause_object as *const _ as *const BaseException))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Exceptions defined in `asyncio` module
|
||||
pub mod asyncio {
|
||||
import_exception!(asyncio, CancelledError);
|
||||
|
@ -464,7 +426,7 @@ pub mod socket {
|
|||
mod test {
|
||||
use crate::exceptions::Exception;
|
||||
use crate::types::{IntoPyDict, PyDict};
|
||||
use crate::{AsPyPointer, FromPy, Py, PyErr, Python};
|
||||
use crate::{AsPyPointer, PyErr, Python};
|
||||
use std::error::Error;
|
||||
use std::fmt::Write;
|
||||
|
||||
|
@ -476,7 +438,7 @@ mod test {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let err: PyErr = gaierror.into();
|
||||
let err: PyErr = gaierror::py_err(());
|
||||
let socket = py
|
||||
.import("socket")
|
||||
.map_err(|e| e.print(py))
|
||||
|
@ -500,7 +462,7 @@ mod test {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let err: PyErr = MessageError.into();
|
||||
let err: PyErr = MessageError::py_err(());
|
||||
let email = py
|
||||
.import("email")
|
||||
.map_err(|e| e.print(py))
|
||||
|
@ -550,11 +512,11 @@ mod test {
|
|||
let mut out = String::new();
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let result = py
|
||||
let err = py
|
||||
.run("raise Exception('banana')", None, None)
|
||||
.expect_err("raising should have given us an error");
|
||||
let convert = Py::<super::BaseException>::from_py(result, py);
|
||||
write!(&mut out, "{}", convert).expect("successful format");
|
||||
.expect_err("raising should have given us an error")
|
||||
.instance(py);
|
||||
write!(&mut out, "{}", err).expect("successful format");
|
||||
assert_eq!(out, "Exception: banana");
|
||||
}
|
||||
|
||||
|
@ -563,19 +525,19 @@ mod test {
|
|||
let mut out = String::new();
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let result = py
|
||||
let err = py
|
||||
.run(
|
||||
"raise Exception('banana') from TypeError('peach')",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.expect_err("raising should have given us an error");
|
||||
let convert = Py::<super::BaseException>::from_py(result, py);
|
||||
write!(&mut out, "{}", convert).expect("successful format");
|
||||
.expect_err("raising should have given us an error")
|
||||
.instance(py);
|
||||
write!(&mut out, "{}", err).expect("successful format");
|
||||
assert_eq!(out, "Exception: banana");
|
||||
out.clear();
|
||||
let convert_ref: &super::BaseException =
|
||||
unsafe { &*(convert.as_ptr() as *const _ as *const _) };
|
||||
unsafe { &*(err.as_ptr() as *const _ as *const _) };
|
||||
let source = convert_ref.source().expect("cause should exist");
|
||||
write!(&mut out, "{}", source).expect("successful format");
|
||||
assert_eq!(out, "TypeError: peach");
|
||||
|
|
|
@ -6,6 +6,77 @@ use crate::ffi::pyport::Py_ssize_t;
|
|||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyBaseExceptionObject {
|
||||
pub ob_base: PyObject,
|
||||
pub dict: *mut PyObject,
|
||||
pub args: *mut PyObject,
|
||||
pub traceback: *mut PyObject,
|
||||
pub context: *mut PyObject,
|
||||
pub cause: *mut PyObject,
|
||||
pub suppress_context: char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PySyntaxErrorObject {
|
||||
pub exception_base: PyBaseExceptionObject,
|
||||
pub msg: *mut PyObject,
|
||||
pub filename: *mut PyObject,
|
||||
pub lineno: *mut PyObject,
|
||||
pub offset: *mut PyObject,
|
||||
pub text: *mut PyObject,
|
||||
pub print_file_and_line: *mut PyObject,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyImportErrorObject {
|
||||
pub exception_base: PyBaseExceptionObject,
|
||||
pub msg: *mut PyObject,
|
||||
pub name: *mut PyObject,
|
||||
pub path: *mut PyObject,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyUnicodeErrorObject {
|
||||
pub exception_base: PyBaseExceptionObject,
|
||||
pub encoding: *mut PyObject,
|
||||
pub object: *mut PyObject,
|
||||
pub start: Py_ssize_t,
|
||||
pub end: Py_ssize_t,
|
||||
pub reason: *mut PyObject,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PySystemExitObject {
|
||||
pub exception_base: PyBaseExceptionObject,
|
||||
pub code: *mut PyObject,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyOSErrorObject {
|
||||
pub exception_base: PyBaseExceptionObject,
|
||||
pub myerrno: *mut PyObject,
|
||||
pub strerror: *mut PyObject,
|
||||
pub filename: *mut PyObject,
|
||||
pub filename2: *mut PyObject,
|
||||
#[cfg(windows)]
|
||||
pub winerror: *mut PyObject,
|
||||
pub written: Py_ssize_t,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyStopIterationObject {
|
||||
pub exception_base: PyBaseExceptionObject,
|
||||
pub value: *mut PyObject,
|
||||
}
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyErr_SetNone")]
|
||||
|
|
|
@ -30,7 +30,7 @@ thread_local! {
|
|||
/// 1) for performance
|
||||
/// 2) PyGILState_Check always returns 1 if the sub-interpreter APIs have ever been called,
|
||||
/// which could lead to incorrect conclusions that the GIL is held.
|
||||
fn gil_is_acquired() -> bool {
|
||||
pub(crate) fn gil_is_acquired() -> bool {
|
||||
GIL_COUNT.try_with(|c| c.get() > 0).unwrap_or(false)
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ pub unsafe trait PyNativeType: Sized {
|
|||
///
|
||||
/// Technically, it is a safe wrapper around `NonNull<ffi::PyObject>` with
|
||||
/// specified type information.
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct Py<T>(NonNull<ffi::PyObject>, PhantomData<T>);
|
||||
|
||||
|
@ -378,6 +377,34 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Py<T> can be used as an error when T is an Error.
|
||||
///
|
||||
/// However for GIL lifetime reasons, cause() cannot be implemented for Py<T>.
|
||||
/// Use .as_ref() to get the GIL-scoped error if you need to inspect the cause.
|
||||
impl<T> std::error::Error for Py<T>
|
||||
where
|
||||
T: std::error::Error + PyTypeInfo,
|
||||
T::AsRefTarget: std::fmt::Display
|
||||
{ }
|
||||
|
||||
impl<T> std::fmt::Display for Py<T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
T::AsRefTarget: std::fmt::Display
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
std::fmt::Display::fmt(self.as_ref(py), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Py<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_tuple("Py").field(&self.0.as_ptr()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Py;
|
||||
|
|
|
@ -25,8 +25,14 @@ macro_rules! private_impl {
|
|||
}
|
||||
|
||||
macro_rules! pyo3_exception {
|
||||
($name: ident, $base: ty) => {
|
||||
($doc: expr, $name: ident, $base: ty) => {
|
||||
#[doc = $doc]
|
||||
#[repr(transparent)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name($crate::PyAny);
|
||||
|
||||
$crate::impl_exception_boilerplate!($name);
|
||||
|
||||
$crate::create_exception_type_object!(pyo3_runtime, $name, $base);
|
||||
};
|
||||
}
|
||||
|
|
20
src/panic.rs
20
src/panic.rs
|
@ -1,12 +1,14 @@
|
|||
use crate::exceptions::BaseException;
|
||||
|
||||
/// The exception raised when Rust code called from Python panics.
|
||||
///
|
||||
/// Like SystemExit, this exception is derived from BaseException so that
|
||||
/// it will typically propagate all the way through the stack and cause the
|
||||
/// Python interpreter to exit.
|
||||
pub struct PanicException {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
pyo3_exception!(PanicException, BaseException);
|
||||
pyo3_exception!(
|
||||
"
|
||||
The exception raised when Rust code called from Python panics.
|
||||
|
||||
Like SystemExit, this exception is derived from BaseException so that
|
||||
it will typically propagate all the way through the stack and cause the
|
||||
Python interpreter to exit.
|
||||
",
|
||||
PanicException,
|
||||
BaseException
|
||||
);
|
|
@ -1,6 +1,7 @@
|
|||
//! Includes `PyCell` implementation.
|
||||
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
|
||||
use crate::pyclass::{PyClass, PyClassThreadChecker};
|
||||
use crate::exceptions::RuntimeError;
|
||||
use crate::pyclass_init::PyClassInitializer;
|
||||
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
|
||||
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
|
||||
|
@ -705,6 +706,12 @@ impl fmt::Display for PyBorrowError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<PyBorrowError> for PyErr {
|
||||
fn from(other: PyBorrowError) -> Self {
|
||||
RuntimeError::py_err(other.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned by [`PyCell::try_borrow_mut`](struct.PyCell.html#method.try_borrow_mut).
|
||||
///
|
||||
/// In Python, you can catch this error by `except RuntimeError`.
|
||||
|
@ -724,5 +731,8 @@ impl fmt::Display for PyBorrowMutError {
|
|||
}
|
||||
}
|
||||
|
||||
pyo3_exception!(PyBorrowError, crate::exceptions::RuntimeError);
|
||||
pyo3_exception!(PyBorrowMutError, crate::exceptions::RuntimeError);
|
||||
impl From<PyBorrowMutError> for PyErr {
|
||||
fn from(other: PyBorrowMutError) -> Self {
|
||||
RuntimeError::py_err(other.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::conversion::{
|
|||
use crate::err::{PyDowncastError, PyErr, PyResult};
|
||||
use crate::exceptions::TypeError;
|
||||
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
|
||||
use crate::{err, ffi, Py, PyNativeType, PyObject, Python};
|
||||
use crate::{err, ffi, Py, PyNativeType, PyObject};
|
||||
use libc::c_int;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::cmp::Ordering;
|
||||
|
@ -65,6 +65,8 @@ pyobject_native_type_convert!(
|
|||
|
||||
pyobject_native_type_extract!(PyAny);
|
||||
|
||||
pyobject_native_type_fmt!(PyAny);
|
||||
|
||||
impl PyAny {
|
||||
/// Convert this PyAny to a concrete Python type.
|
||||
pub fn downcast<T>(&self) -> Result<&T, PyDowncastError>
|
||||
|
|
|
@ -31,7 +31,7 @@ macro_rules! pyobject_native_type_named (
|
|||
impl<$($type_param,)*> ::std::convert::AsRef<$crate::PyAny> for $name {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &$crate::PyAny {
|
||||
unsafe { &*(self.as_ptr() as *const $crate::PyAny) }
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ macro_rules! pyobject_native_type_named (
|
|||
|
||||
#[inline]
|
||||
fn deref(&self) -> &$crate::PyAny {
|
||||
unsafe { &*(self.as_ptr() as *const $crate::PyAny) }
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,29 +66,44 @@ macro_rules! pyobject_native_type_named (
|
|||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type {
|
||||
macro_rules! pyobject_native_type_core {
|
||||
($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
unsafe impl $crate::type_object::PyLayout<$name> for $layout {}
|
||||
impl $crate::type_object::PySizedLayout<$name> for $layout {}
|
||||
impl $crate::derive_utils::PyBaseTypeUtils for $name {
|
||||
type Dict = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type LayoutAsBase = $crate::pycell::PyCellBase<$name>;
|
||||
type BaseNativeType = $name;
|
||||
type ThreadChecker = $crate::pyclass::ThreadCheckerStub<$crate::PyObject>;
|
||||
}
|
||||
pyobject_native_type_named!($name $(,$type_param)*);
|
||||
pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*);
|
||||
pyobject_native_type_extract!($name $(,$type_param)*);
|
||||
$crate::pyobject_native_type_named!($name $(,$type_param)*);
|
||||
$crate::pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*);
|
||||
$crate::pyobject_native_type_extract!($name $(,$type_param)*);
|
||||
|
||||
impl<'a, $($type_param,)*> ::std::convert::From<&'a $name> for &'a $crate::PyAny {
|
||||
fn from(ob: &'a $name) -> Self {
|
||||
unsafe{&*(ob as *const $name as *const $crate::PyAny)}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_sized {
|
||||
($name: ty, $layout: path $(,$type_param: ident)*) => {
|
||||
impl $crate::type_object::PySizedLayout<$name> for $layout {}
|
||||
impl<'a, $($type_param,)*> $crate::derive_utils::PyBaseTypeUtils for $name {
|
||||
type Dict = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type LayoutAsBase = $crate::pycell::PyCellBase<$name>;
|
||||
type BaseNativeType = $name;
|
||||
type ThreadChecker = $crate::pyclass::ThreadCheckerStub<$crate::PyObject>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type {
|
||||
($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
$crate::pyobject_native_type_core!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*);
|
||||
$crate::pyobject_native_type_sized!($name, $layout $(,$type_param)*);
|
||||
$crate::pyobject_native_type_fmt!($name $(,$type_param)*);
|
||||
};
|
||||
($name: ty, $layout: path, $typeobject: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
pyobject_native_type! {
|
||||
$crate::pyobject_native_type! {
|
||||
$name, $layout, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*
|
||||
}
|
||||
};
|
||||
|
@ -97,20 +112,12 @@ macro_rules! pyobject_native_type {
|
|||
#[macro_export]
|
||||
macro_rules! pyobject_native_var_type {
|
||||
($name: ty, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
unsafe impl $crate::type_object::PyLayout<$name> for $crate::ffi::PyObject {}
|
||||
pyobject_native_type_named!($name $(,$type_param)*);
|
||||
pyobject_native_type_convert!($name, $crate::ffi::PyObject,
|
||||
$typeobject, $module, $checkfunction $(,$type_param)*);
|
||||
pyobject_native_type_extract!($name $(,$type_param)*);
|
||||
|
||||
impl<'a, $($type_param,)*> ::std::convert::From<&'a $name> for &'a $crate::PyAny {
|
||||
fn from(ob: &'a $name) -> Self {
|
||||
unsafe{&*(ob as *const $name as *const $crate::PyAny)}
|
||||
}
|
||||
}
|
||||
$crate::pyobject_native_type_core!(
|
||||
$name, $crate::ffi::PyObject, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*);
|
||||
$crate::pyobject_native_type_fmt!($name $(,$type_param)*);
|
||||
};
|
||||
($name: ty, $typeobject: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
pyobject_native_var_type! {
|
||||
$crate::pyobject_native_var_type! {
|
||||
$name, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*
|
||||
}
|
||||
};
|
||||
|
@ -118,6 +125,7 @@ macro_rules! pyobject_native_var_type {
|
|||
|
||||
// NOTE: This macro is not included in pyobject_native_type_convert!
|
||||
// because rust-numpy has a special implementation.
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_extract {
|
||||
($name: ty $(,$type_param: ident)*) => {
|
||||
impl<'py, $($type_param,)*> $crate::FromPyObject<'py> for &'py $name {
|
||||
|
@ -136,7 +144,7 @@ macro_rules! pyobject_native_type_convert(
|
|||
type Type = ();
|
||||
type BaseType = $crate::PyAny;
|
||||
type Layout = $layout;
|
||||
type BaseLayout = ffi::PyObject;
|
||||
type BaseLayout = $crate::ffi::PyObject;
|
||||
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;
|
||||
type AsRefTarget = Self;
|
||||
|
||||
|
@ -144,7 +152,7 @@ macro_rules! pyobject_native_type_convert(
|
|||
const MODULE: Option<&'static str> = $module;
|
||||
|
||||
#[inline]
|
||||
fn type_object_raw(_py: Python) -> *mut $crate::ffi::PyTypeObject {
|
||||
fn type_object_raw(_py: $crate::Python) -> *mut $crate::ffi::PyTypeObject {
|
||||
// Create a very short lived mutable reference and directly
|
||||
// cast it to a pointer: no mutable references can be aliasing
|
||||
// because we hold the GIL.
|
||||
|
@ -166,7 +174,12 @@ macro_rules! pyobject_native_type_convert(
|
|||
unsafe { $crate::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_fmt(
|
||||
($name: ty $(,$type_param: ident)*) => {
|
||||
impl<$($type_param,)*> ::std::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter)
|
||||
-> Result<(), ::std::fmt::Error>
|
||||
|
|
|
@ -39,7 +39,8 @@ macro_rules! int_fits_larger_int {
|
|||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
let val: $larger_type = obj.extract()?;
|
||||
<$rust_type>::try_from(val).map_err(|_| exceptions::OverflowError.into())
|
||||
<$rust_type>::try_from(val)
|
||||
.map_err(|e| exceptions::OverflowError::py_err(e.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -143,7 +144,8 @@ macro_rules! int_fits_c_long {
|
|||
val
|
||||
}
|
||||
}?;
|
||||
<$rust_type>::try_from(val).map_err(|_| exceptions::OverflowError.into())
|
||||
<$rust_type>::try_from(val)
|
||||
.map_err(|e| exceptions::OverflowError::py_err(e.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -185,10 +185,6 @@ mod test {
|
|||
fn test_to_str_surrogate() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
assert!(
|
||||
!crate::PyErr::occurred(py),
|
||||
"test must begin without exceptions"
|
||||
);
|
||||
let obj: PyObject = py.eval(r#"'\ud800'"#, None, None).unwrap().into();
|
||||
let py_string = <PyString as PyTryFrom>::try_from(obj.as_ref(py)).unwrap();
|
||||
assert!(py_string.to_str().is_err());
|
||||
|
@ -208,10 +204,6 @@ mod test {
|
|||
fn test_to_string_lossy() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
assert!(
|
||||
!crate::PyErr::occurred(py),
|
||||
"test must begin without exceptions"
|
||||
);
|
||||
let obj: PyObject = py
|
||||
.eval(r#"'🐈 Hello \ud800World'"#, None, None)
|
||||
.unwrap()
|
||||
|
|
|
@ -100,7 +100,7 @@ fn mutation_fails() {
|
|||
let e = py
|
||||
.run("obj.base_set(lambda: obj.sub_set_and_ret(1))", global, None)
|
||||
.unwrap_err();
|
||||
assert!(e.is_instance::<pyo3::pycell::PyBorrowMutError>(py))
|
||||
assert_eq!(&e.instance(py).to_string(), "RuntimeError: Already borrowed")
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
|
|
Loading…
Reference in New Issue