Make exceptions proper native types.

This commit is contained in:
David Hewitt 2020-05-24 13:38:27 +01:00
parent 496c626835
commit a7e0c6bfa7
14 changed files with 327 additions and 237 deletions

View File

@ -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`.

View File

@ -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));

View File

@ -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 dont 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 theres 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, doesnt 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");

View File

@ -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")]

View File

@ -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)
}

View File

@ -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;

View File

@ -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);
};
}

View File

@ -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
);

View File

@ -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())
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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()))
}
}
};

View File

@ -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()

View File

@ -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]