From 496c626835fbd6b1dda7de2b573705ad7400b2c2 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Mon, 9 Dec 2019 23:55:15 +0200 Subject: [PATCH 1/3] Proto #1: Exception instances as Py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prototype implements use of Py as the instance to use for exception instances. These instances integrate reasonably well with the Rust’s standard error mechanisms by implementing the `Display` and `Error` traits. These error types can also be stored into e.g. `failure::Fail`s or other error types as a cause of some greater error. --- src/err.rs | 21 ++++--- src/exceptions.rs | 146 +++++++++++++++++++++++++++++++++++++++++++- src/types/string.rs | 8 +++ 3 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/err.rs b/src/err.rs index fde8929547a..cc3cb231ccd 100644 --- a/src/err.rs +++ b/src/err.rs @@ -344,12 +344,13 @@ 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) -> PyObject { + fn instance(mut self, py: Python) -> Py { self.normalize(py); - match self.pvalue { - PyErrValue::Value(ref instance) => instance.clone_ref(py), - _ => py.None(), - } + 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") } /// Writes the error back to the Python interpreter's global state. @@ -429,6 +430,12 @@ impl std::fmt::Debug for PyErr { } impl FromPy for PyObject { + fn from_py(other: PyErr, py: Python) -> Self { + other.instance(py).into() + } +} + +impl FromPy for Py { fn from_py(other: PyErr, py: Python) -> Self { other.instance(py) } @@ -437,14 +444,14 @@ impl FromPy for PyObject { impl ToPyObject for PyErr { fn to_object(&self, py: Python) -> PyObject { let err = self.clone_ref(py); - err.instance(py) + err.instance(py).into() } } impl<'a> IntoPy for &'a PyErr { fn into_py(self, py: Python) -> PyObject { let err = self.clone_ref(py); - err.instance(py) + err.instance(py).into() } } diff --git a/src/exceptions.rs b/src/exceptions.rs index a277189cec9..3c01dfe1a9b 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -8,6 +8,7 @@ use crate::type_object::PyTypeObject; use crate::types::{PyAny, PyTuple}; use crate::Python; use crate::{AsPyPointer, ToPyObject}; +use crate::{AsPyRef, Py, PyDowncastError, PyTryFrom}; use std::ffi::CStr; use std::ops; use std::os::raw::c_char; @@ -206,11 +207,13 @@ macro_rules! impl_native_exception ( PyErr::new::<$name, _>(()) } } + impl std::convert::Into<$crate::PyResult> for $name { fn into(self) -> $crate::PyResult { PyErr::new::<$name, _>(()).into() } } + impl $name { pub fn py_err(args: V) -> PyErr { PyErr::new::<$name, V>(args) @@ -219,11 +222,46 @@ macro_rules! impl_native_exception ( PyErr::new::<$name, V>(args).into() } } + 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>(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>(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>(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; + } + } ); ); @@ -338,6 +376,72 @@ 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 = 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, ": ") + } + } +} + +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` 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); @@ -360,7 +464,9 @@ pub mod socket { mod test { use crate::exceptions::Exception; use crate::types::{IntoPyDict, PyDict}; - use crate::{PyErr, Python}; + use crate::{AsPyPointer, FromPy, Py, PyErr, Python}; + use std::error::Error; + use std::fmt::Write; import_exception!(socket, gaierror); import_exception!(email.errors, MessageError); @@ -438,4 +544,42 @@ mod test { ) .unwrap(); } + + #[test] + fn native_exception_display() { + let mut out = String::new(); + let gil = Python::acquire_gil(); + let py = gil.python(); + let result = py + .run("raise Exception('banana')", None, None) + .expect_err("raising should have given us an error"); + let convert = Py::::from_py(result, py); + write!(&mut out, "{}", convert).expect("successful format"); + assert_eq!(out, "Exception: banana"); + } + + #[test] + fn native_exception_chain() { + let mut out = String::new(); + let gil = Python::acquire_gil(); + let py = gil.python(); + let result = py + .run( + "raise Exception('banana') from TypeError('peach')", + None, + None, + ) + .expect_err("raising should have given us an error"); + let convert = Py::::from_py(result, py); + write!(&mut out, "{}", convert).expect("successful format"); + assert_eq!(out, "Exception: banana"); + out.clear(); + let convert_ref: &super::BaseException = + unsafe { &*(convert.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"); + let source_source = source.source(); + assert!(source_source.is_none(), "source_source should be None"); + } } diff --git a/src/types/string.rs b/src/types/string.rs index 825cf49061c..bcee2ad2ab1 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -185,6 +185,10 @@ 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 = ::try_from(obj.as_ref(py)).unwrap(); assert!(py_string.to_str().is_err()); @@ -204,6 +208,10 @@ 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() From a7e0c6bfa7156f0aed7f63398494667291d5a6db Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 24 May 2020 13:38:27 +0100 Subject: [PATCH 2/3] Make exceptions proper native types. --- guide/src/migration.md | 5 +- src/err.rs | 22 +-- src/exceptions.rs | 302 +++++++++++++++++--------------------- src/ffi/pyerrors.rs | 71 +++++++++ src/gil.rs | 2 +- src/instance.rs | 29 +++- src/internal_tricks.rs | 8 +- src/panic.rs | 20 +-- src/pycell.rs | 14 +- src/types/any.rs | 4 +- src/types/mod.rs | 67 +++++---- src/types/num.rs | 6 +- src/types/string.rs | 8 - tests/test_inheritance.rs | 2 +- 14 files changed, 325 insertions(+), 235 deletions(-) diff --git a/guide/src/migration.md b/guide/src/migration.md index 004e20ce04e..f1516950db5 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -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::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`. diff --git a/src/err.rs b/src/err.rs index cc3cb231ccd..b1ad5ad5270 100644 --- a/src/err.rs +++ b/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 { + 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 for PyObject { impl FromPy for Py { fn from_py(other: PyErr, py: Python) -> Self { - other.instance(py) + other.instance(py).into() } } @@ -458,7 +462,7 @@ impl<'a> IntoPy for &'a PyErr { /// Convert `PyDowncastError` to Python `TypeError`. impl std::convert::From 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)); diff --git a/src/exceptions.rs b/src/exceptions.rs index 3c01dfe1a9b..c2ffa860c21 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -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 ::std::convert::Into<$crate::PyResult> for $name { + impl std::convert::Into<$crate::PyResult> for $name { fn into(self) -> $crate::PyResult { $crate::PyErr::new::<$name, _>(()).into() } } impl $name { - pub fn py_err(args: T) -> $crate::PyErr { - $crate::PyErr::new::(args) + pub fn py_err(args: V) -> $crate::PyErr { + $crate::PyErr::new::<$name, V>(args) + } + pub fn into(args: V) -> $crate::PyResult { + $crate::PyErr::new::<$name, V>(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, ": ") + } } + } + + 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()))?; - pub fn into(args: T) -> $crate::PyResult { - $crate::PyErr::new::(args).into() + 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 std::convert::Into<$crate::PyResult> for $name { - fn into(self) -> $crate::PyResult { - PyErr::new::<$name, _>(()).into() - } - } + $crate::impl_exception_boilerplate!($name); impl $name { - pub fn py_err(args: V) -> PyErr { - PyErr::new::<$name, V>(args) - } - pub fn into(args: V) -> PyResult { - 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>(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>(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>(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 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 = 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, ": ") - } - } -} - -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` 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::::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::::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"); diff --git a/src/ffi/pyerrors.rs b/src/ffi/pyerrors.rs index f61233cf871..c73b9e4f5b3 100644 --- a/src/ffi/pyerrors.rs +++ b/src/ffi/pyerrors.rs @@ -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")] diff --git a/src/gil.rs b/src/gil.rs index eceeff8b8ed..d7ce24a9df5 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -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) } diff --git a/src/instance.rs b/src/instance.rs index 97b6335b7e2..dc612428c87 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -42,7 +42,6 @@ pub unsafe trait PyNativeType: Sized { /// /// Technically, it is a safe wrapper around `NonNull` with /// specified type information. -#[derive(Debug)] #[repr(transparent)] pub struct Py(NonNull, PhantomData); @@ -378,6 +377,34 @@ where } } +/// Py can be used as an error when T is an Error. +/// +/// However for GIL lifetime reasons, cause() cannot be implemented for Py. +/// Use .as_ref() to get the GIL-scoped error if you need to inspect the cause. +impl std::error::Error for Py +where + T: std::error::Error + PyTypeInfo, + T::AsRefTarget: std::fmt::Display +{ } + +impl std::fmt::Display for Py +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 std::fmt::Debug for Py { + 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; diff --git a/src/internal_tricks.rs b/src/internal_tricks.rs index a538816552e..d13fed50d08 100644 --- a/src/internal_tricks.rs +++ b/src/internal_tricks.rs @@ -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); }; } diff --git a/src/panic.rs b/src/panic.rs index b5952a4a57a..f6096606914 100644 --- a/src/panic.rs +++ b/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 +); \ No newline at end of file diff --git a/src/pycell.rs b/src/pycell.rs index 1a74c6506e8..88052687e5f 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -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 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 for PyErr { + fn from(other: PyBorrowMutError) -> Self { + RuntimeError::py_err(other.to_string()) + } +} diff --git a/src/types/any.rs b/src/types/any.rs index 5132183733a..999ad3582a5 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -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(&self) -> Result<&T, PyDowncastError> diff --git a/src/types/mod.rs b/src/types/mod.rs index fafc358038d..797d8613c29 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -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 {} + $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 $crate::derive_utils::PyBaseTypeUtils for $name { + 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>; } - 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)*); + } +} - 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 { + ($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; 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> diff --git a/src/types/num.rs b/src/types/num.rs index 254953c7820..9b2e786a5e4 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -39,7 +39,8 @@ macro_rules! int_fits_larger_int { impl<'source> FromPyObject<'source> for $rust_type { fn extract(obj: &'source PyAny) -> PyResult { 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())) } } }; diff --git a/src/types/string.rs b/src/types/string.rs index bcee2ad2ab1..825cf49061c 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -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 = ::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() diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 1ec72302a49..d63d91549af 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -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::(py)) + assert_eq!(&e.instance(py).to_string(), "RuntimeError: Already borrowed") } #[pyclass] From 4ed9748b45db7d47784b39e6550295f9cecb8892 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sat, 4 Jul 2020 16:55:26 +0100 Subject: [PATCH 3/3] Rename exceptions to PyException etc; reintroduce deprecated ones --- CHANGELOG.md | 1 + examples/rustapi_module/src/dict_iter.rs | 4 +- guide/src/exception.md | 34 ++-- src/buffer.rs | 14 +- src/callback.rs | 4 +- src/class/basic.rs | 2 +- src/class/iter.rs | 2 +- src/class/macros.rs | 4 +- src/class/pyasync.rs | 2 +- src/class/sequence.rs | 4 +- src/derive_utils.rs | 4 +- src/err.rs | 74 +++---- src/exceptions.rs | 239 ++++++++++++++++------- src/instance.rs | 7 +- src/panic.rs | 7 +- src/pycell.rs | 6 +- src/python.rs | 4 +- src/types/any.rs | 4 +- src/types/bytearray.rs | 2 +- src/types/module.rs | 4 +- src/types/num.rs | 10 +- src/types/sequence.rs | 2 +- src/types/tuple.rs | 2 +- tests/test_arithmetics.rs | 24 +-- tests/test_buffer_protocol.rs | 6 +- tests/test_class_attributes.rs | 2 +- tests/test_dunder.rs | 22 +-- tests/test_exceptions.rs | 2 +- tests/test_inheritance.rs | 5 +- tests/test_mapping.rs | 6 +- tests/test_methods.rs | 14 +- tests/test_pyfunction.rs | 2 +- tests/test_sequence.rs | 12 +- 33 files changed, 319 insertions(+), 212 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index defe8cebedd..3c3d0a886c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `Python::with_gil` for executing a closure with the Python GIL. [#1037](https://github.com/PyO3/pyo3/pull/1037) ### Changed +- Exception types have been renamed from e.g. `RuntimeError` to `PyRuntimeError`, and are now only accessible by `&T` or `Py` similar to other Python-native types. The old names continue to exist but are deprecated. [#1024](https://github.com/PyO3/pyo3/pull/1024) - Correct FFI definitions `Py_SetProgramName` and `Py_SetPythonHome` to take `*const` argument instead of `*mut`. [#1021](https://github.com/PyO3/pyo3/pull/1021) - Rename `PyString::to_string` to `to_str`, change return type `Cow` to `&str`. [#1023](https://github.com/PyO3/pyo3/pull/1023) - Correct FFI definition `_PyLong_AsByteArray` `*mut c_uchar` argument instead of `*const c_uchar`. [#1029](https://github.com/PyO3/pyo3/pull/1029) diff --git a/examples/rustapi_module/src/dict_iter.rs b/examples/rustapi_module/src/dict_iter.rs index 1911ac63b5c..daca9676a48 100644 --- a/examples/rustapi_module/src/dict_iter.rs +++ b/examples/rustapi_module/src/dict_iter.rs @@ -1,4 +1,4 @@ -use pyo3::exceptions::RuntimeError; +use pyo3::exceptions::PyRuntimeError; use pyo3::prelude::*; use pyo3::types::PyDict; @@ -33,7 +33,7 @@ impl DictSize { if seen == self.expected { Ok(seen) } else { - Err(PyErr::new::(format!( + Err(PyErr::new::(format!( "Expected {} iterations - performed {}", self.expected, seen ))) diff --git a/guide/src/exception.md b/guide/src/exception.md index 7ae20d95513..b69c782c332 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -7,7 +7,7 @@ You can use the [`create_exception!`] macro to define a new exception type: ```rust use pyo3::create_exception; -create_exception!(module, MyError, pyo3::exceptions::Exception); +create_exception!(module, MyError, pyo3::exceptions::PyException); ``` * `module` is the name of the containing module. @@ -19,9 +19,9 @@ For example: use pyo3::prelude::*; use pyo3::create_exception; use pyo3::types::IntoPyDict; -use pyo3::exceptions::Exception; +use pyo3::exceptions::PyException; -create_exception!(mymodule, CustomError, Exception); +create_exception!(mymodule, CustomError, PyException); fn main() { let gil = Python::acquire_gil(); @@ -39,12 +39,12 @@ To raise an exception, first you need to obtain an exception type and construct ```rust use pyo3::{Python, PyErr}; -use pyo3::exceptions; +use pyo3::exceptions::PyTypeError; fn main() { let gil = Python::acquire_gil(); let py = gil.python(); - PyErr::new::("Error").restore(py); + PyErr::new::("Error").restore(py); assert!(PyErr::occurred(py)); drop(PyErr::fetch(py)); } @@ -65,12 +65,12 @@ has a corresponding Rust type, exceptions defined by [`create_exception!`] and [ have Rust types as well. ```rust -# use pyo3::exceptions; +# use pyo3::exceptions::PyValueError; # use pyo3::prelude::*; # fn check_for_error() -> bool {false} fn my_func(arg: PyObject) -> PyResult<()> { if check_for_error() { - Err(exceptions::ValueError::py_err("argument is wrong")) + Err(PyValueError::py_err("argument is wrong")) } else { Ok(()) } @@ -101,13 +101,13 @@ method to do the actual work. To check the type of an exception, you can simply do: ```rust -# use pyo3::exceptions; +# use pyo3::exceptions::PyTypeError; # use pyo3::prelude::*; # fn main() { # let gil = Python::acquire_gil(); # let py = gil.python(); -# let err = exceptions::TypeError::py_err(()); -err.is_instance::(py); +# let err = PyTypeError::py_err(()); +err.is_instance::(py); # } ``` @@ -132,7 +132,8 @@ trait can be implemented. In that case, actual exception argument creation is de until a `Python` object is available. ```rust -# use pyo3::{exceptions, PyErr, PyResult}; +# use pyo3::{PyErr, PyResult}; +# use pyo3::exceptions::PyOSError; # use std::error::Error; # use std::fmt; # @@ -152,7 +153,7 @@ until a `Python` object is available. # } impl std::convert::From for PyErr { fn from(err: CustomIOError) -> PyErr { - exceptions::OSError::py_err(err.to_string()) + PyOSError::py_err(err.to_string()) } } @@ -181,14 +182,15 @@ The code snippet above will raise a `ValueError` in Python if `String::parse()` ## Using exceptions defined in Python code It is possible to use an exception defined in Python code as a native Rust type. -The `import_exception!` macro allows importing a specific exception class and defines a zero-sized Rust type +The `import_exception!` macro allows importing a specific exception class and defines a Rust type for that exception. ```rust use pyo3::prelude::*; -use pyo3::import_exception; -import_exception!(io, UnsupportedOperation); +mod io { + pyo3::import_exception!(io, UnsupportedOperation); +} fn tell(file: PyObject) -> PyResult { use pyo3::exceptions::*; @@ -197,7 +199,7 @@ fn tell(file: PyObject) -> PyResult { let py = gil.python(); match file.call_method0(py, "tell") { - Err(_) => Err(UnsupportedOperation::py_err("not supported: tell")), + Err(_) => Err(io::UnsupportedOperation::py_err("not supported: tell")), Ok(x) => x.extract::(py), } } diff --git a/src/buffer.rs b/src/buffer.rs index 3f0b3f128fe..4cbb16e1109 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -156,10 +156,12 @@ pub unsafe trait Element: Copy { fn validate(b: &ffi::Py_buffer) -> PyResult<()> { // shape and stride information must be provided when we use PyBUF_FULL_RO if b.shape.is_null() { - return Err(exceptions::BufferError::py_err("Shape is Null")); + return Err(exceptions::PyBufferError::py_err("Shape is Null")); } if b.strides.is_null() { - return Err(exceptions::BufferError::py_err("PyBuffer: Strides is Null")); + return Err(exceptions::PyBufferError::py_err( + "PyBuffer: Strides is Null", + )); } Ok(()) } @@ -188,7 +190,7 @@ impl PyBuffer { { Ok(buf) } else { - Err(exceptions::BufferError::py_err( + Err(exceptions::PyBufferError::py_err( "Incompatible type as buffer", )) } @@ -439,7 +441,7 @@ impl PyBuffer { fn copy_to_slice_impl(&self, py: Python, target: &mut [T], fort: u8) -> PyResult<()> { if mem::size_of_val(target) != self.len_bytes() { - return Err(exceptions::BufferError::py_err( + return Err(exceptions::PyBufferError::py_err( "Slice length does not match buffer length.", )); } @@ -526,7 +528,7 @@ impl PyBuffer { return buffer_readonly_error(); } if mem::size_of_val(source) != self.len_bytes() { - return Err(exceptions::BufferError::py_err( + return Err(exceptions::PyBufferError::py_err( "Slice length does not match buffer length.", )); } @@ -562,7 +564,7 @@ impl PyBuffer { #[inline(always)] fn buffer_readonly_error() -> PyResult<()> { - Err(exceptions::BufferError::py_err( + Err(exceptions::PyBufferError::py_err( "Cannot write to read-only buffer.", )) } diff --git a/src/callback.rs b/src/callback.rs index 0220436f65a..7dbd1aff822 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -3,7 +3,7 @@ //! Utilities for a Python callable object that invokes a Rust function. use crate::err::PyResult; -use crate::exceptions::OverflowError; +use crate::exceptions::PyOverflowError; use crate::ffi::{self, Py_hash_t}; use crate::IntoPyPointer; use crate::{IntoPy, PyObject, Python}; @@ -85,7 +85,7 @@ impl IntoPyCallbackOutput for usize { if self <= (isize::MAX as usize) { Ok(self as isize) } else { - Err(OverflowError::py_err(())) + Err(PyOverflowError::py_err(())) } } } diff --git a/src/class/basic.rs b/src/class/basic.rs index f7dd4a54a15..efb45212d05 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -260,7 +260,7 @@ where ffi::Py_NE => Ok(CompareOp::Ne), ffi::Py_GT => Ok(CompareOp::Gt), ffi::Py_GE => Ok(CompareOp::Ge), - _ => Err(PyErr::new::( + _ => Err(PyErr::new::( "tp_richcompare called with invalid comparison operator", )), } diff --git a/src/class/iter.rs b/src/class/iter.rs index ccf84044559..0818feb0a44 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -112,7 +112,7 @@ impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterNextOutput { fn convert(self, _py: Python) -> PyResult<*mut ffi::PyObject> { match self { IterNextOutput::Yield(o) => Ok(o.into_ptr()), - IterNextOutput::Return(opt) => Err(crate::exceptions::StopIteration::py_err((opt,))), + IterNextOutput::Return(opt) => Err(crate::exceptions::PyStopIteration::py_err((opt,))), } } } diff --git a/src/class/macros.rs b/src/class/macros.rs index c56fe97ed9f..fcb20a0a9ec 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -302,7 +302,7 @@ macro_rules! py_func_set { let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf); if value.is_null() { - Err($crate::PyErr::new::( + Err($crate::PyErr::new::( format!( "Subscript deletion not supported by {:?}", stringify!($generic) @@ -338,7 +338,7 @@ macro_rules! py_func_del { .extract()?; slf.try_borrow_mut()?.$fn_del(name).convert(py) } else { - Err(PyErr::new::( + Err(PyErr::new::( "Subscript assignment not supported", )) } diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index 7986adf69bc..82548835c7f 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -119,7 +119,7 @@ impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterANextOutput { match self { IterANextOutput::Yield(o) => Ok(o.into_ptr()), IterANextOutput::Return(opt) => { - Err(crate::exceptions::StopAsyncIteration::py_err((opt,))) + Err(crate::exceptions::PyStopAsyncIteration::py_err((opt,))) } } } diff --git a/src/class/sequence.rs b/src/class/sequence.rs index e85edf02db5..a9c62f03d0b 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -223,7 +223,7 @@ mod sq_ass_item_impl { let slf = py.from_borrowed_ptr::>(slf); if value.is_null() { - return Err(PyErr::new::(format!( + return Err(PyErr::new::(format!( "Item deletion is not supported by {:?}", stringify!(T) ))); @@ -256,7 +256,7 @@ mod sq_ass_item_impl { if value.is_null() { crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into())) } else { - Err(PyErr::new::(format!( + Err(PyErr::new::(format!( "Item assignment not supported by {:?}", stringify!(T) ))) diff --git a/src/derive_utils.rs b/src/derive_utils.rs index c10c172cf18..2a736d7ebcd 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -5,7 +5,7 @@ //! Functionality for the code generated by the derive backend use crate::err::{PyErr, PyResult}; -use crate::exceptions::TypeError; +use crate::exceptions::PyTypeError; use crate::instance::PyNativeType; use crate::pyclass::{PyClass, PyClassThreadChecker}; use crate::types::{PyAny, PyDict, PyModule, PyTuple}; @@ -43,7 +43,7 @@ pub fn parse_fn_args<'p>( let nargs = args.len(); let mut used_args = 0; macro_rules! raise_error { - ($s: expr $(,$arg:expr)*) => (return Err(TypeError::py_err(format!( + ($s: expr $(,$arg:expr)*) => (return Err(PyTypeError::py_err(format!( concat!("{} ", $s), fname.unwrap_or("function") $(,$arg)* )))) } diff --git a/src/err.rs b/src/err.rs index b1ad5ad5270..839ca0c5e92 100644 --- a/src/err.rs +++ b/src/err.rs @@ -75,14 +75,14 @@ impl PyErr { /// /// Example: /// ```ignore - /// return Err(PyErr::new::("Error message")); + /// return Err(PyErr::new::("Error message")); /// ``` /// /// In most cases, you can use a concrete exception's constructors instead: /// the example is equivalent to /// ```ignore - /// return Err(exceptions::TypeError::py_err("Error message")); - /// return exceptions::TypeError::into("Error message"); + /// return Err(exceptions::PyTypeError::py_err("Error message")); + /// return exceptions::PyTypeError::into("Error message"); /// ``` pub fn new(value: V) -> PyErr where @@ -105,7 +105,7 @@ impl PyErr { /// Constructs a new error, with the usual lazy initialization of Python exceptions. /// /// `exc` is the exception type; usually one of the standard exceptions - /// like `exceptions::RuntimeError`. + /// like `exceptions::PyRuntimeError`. /// `args` is the a tuple of arguments to pass to the exception constructor. pub fn from_type(exc: &PyType, args: A) -> PyErr where @@ -161,7 +161,7 @@ impl PyErr { } } else { PyErr { - ptype: exceptions::TypeError::type_object(obj.py()).into(), + ptype: exceptions::PyTypeError::type_object(obj.py()).into(), pvalue: PyErrValue::ToObject(Box::new("exceptions must derive from BaseException")), ptraceback: None, } @@ -257,7 +257,7 @@ impl PyErr { }; let ptype = if ptype.is_null() { - ::type_object(py).into() + ::type_object(py).into() } else { Py::from_owned_ptr(py, ptype) }; @@ -344,7 +344,7 @@ impl PyErr { /// /// This method takes `mut self` because the error might need /// to be normalized in order to create the exception instance. - pub fn instance(mut self, py: Python) -> &exceptions::BaseException { + pub fn instance(mut self, py: Python) -> &exceptions::PyBaseException { self.normalize(py); match self.pvalue { PyErrValue::Value(ref instance) => { @@ -439,7 +439,7 @@ impl FromPy for PyObject { } } -impl FromPy for Py { +impl FromPy for Py { fn from_py(other: PyErr, py: Python) -> Self { other.instance(py).into() } @@ -462,7 +462,7 @@ impl<'a> IntoPy for &'a PyErr { /// Convert `PyDowncastError` to Python `TypeError`. impl std::convert::From for PyErr { fn from(_err: PyDowncastError) -> PyErr { - exceptions::TypeError::py_err(()) + exceptions::PyTypeError::py_err(()) } } @@ -515,28 +515,30 @@ impl std::convert::From for PyErr { } match err.kind() { io::ErrorKind::BrokenPipe => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } io::ErrorKind::ConnectionRefused => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } io::ErrorKind::ConnectionAborted => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } io::ErrorKind::ConnectionReset => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } io::ErrorKind::Interrupted => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } io::ErrorKind::NotFound => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } io::ErrorKind::WouldBlock => { - PyErr::from_value::(err_value!()) + PyErr::from_value::(err_value!()) } - io::ErrorKind::TimedOut => PyErr::from_value::(err_value!()), - _ => PyErr::from_value::(err_value!()), + io::ErrorKind::TimedOut => { + PyErr::from_value::(err_value!()) + } + _ => PyErr::from_value::(err_value!()), } } } @@ -549,7 +551,7 @@ impl PyErrArguments for io::Error { impl std::convert::From> for PyErr { fn from(err: std::io::IntoInnerError) -> PyErr { - PyErr::from_value::(PyErrValue::from_err_args(err)) + PyErr::from_value::(PyErrValue::from_err_args(err)) } } @@ -567,22 +569,28 @@ impl PyErrArguments for std::convert::Infallible { impl std::convert::From for PyErr { fn from(_: std::convert::Infallible) -> PyErr { - PyErr::new::("Infalliable!") + PyErr::new::("Infalliable!") } } -impl_to_pyerr!(std::array::TryFromSliceError, exceptions::ValueError); -impl_to_pyerr!(std::num::ParseIntError, exceptions::ValueError); -impl_to_pyerr!(std::num::ParseFloatError, exceptions::ValueError); -impl_to_pyerr!(std::num::TryFromIntError, exceptions::ValueError); -impl_to_pyerr!(std::str::ParseBoolError, exceptions::ValueError); -impl_to_pyerr!(std::ffi::IntoStringError, exceptions::UnicodeDecodeError); -impl_to_pyerr!(std::ffi::NulError, exceptions::ValueError); -impl_to_pyerr!(std::str::Utf8Error, exceptions::UnicodeDecodeError); -impl_to_pyerr!(std::string::FromUtf8Error, exceptions::UnicodeDecodeError); -impl_to_pyerr!(std::string::FromUtf16Error, exceptions::UnicodeDecodeError); -impl_to_pyerr!(std::char::DecodeUtf16Error, exceptions::UnicodeDecodeError); -impl_to_pyerr!(std::net::AddrParseError, exceptions::ValueError); +impl_to_pyerr!(std::array::TryFromSliceError, exceptions::PyValueError); +impl_to_pyerr!(std::num::ParseIntError, exceptions::PyValueError); +impl_to_pyerr!(std::num::ParseFloatError, exceptions::PyValueError); +impl_to_pyerr!(std::num::TryFromIntError, exceptions::PyValueError); +impl_to_pyerr!(std::str::ParseBoolError, exceptions::PyValueError); +impl_to_pyerr!(std::ffi::IntoStringError, exceptions::PyUnicodeDecodeError); +impl_to_pyerr!(std::ffi::NulError, exceptions::PyValueError); +impl_to_pyerr!(std::str::Utf8Error, exceptions::PyUnicodeDecodeError); +impl_to_pyerr!(std::string::FromUtf8Error, exceptions::PyUnicodeDecodeError); +impl_to_pyerr!( + std::string::FromUtf16Error, + exceptions::PyUnicodeDecodeError +); +impl_to_pyerr!( + std::char::DecodeUtf16Error, + exceptions::PyUnicodeDecodeError +); +impl_to_pyerr!(std::net::AddrParseError, exceptions::PyValueError); pub fn panic_after_error(_py: Python) -> ! { unsafe { @@ -611,7 +619,7 @@ mod tests { fn set_typeerror() { let gil = Python::acquire_gil(); let py = gil.python(); - let err: PyErr = exceptions::TypeError::py_err(()); + let err: PyErr = exceptions::PyTypeError::py_err(()); err.restore(py); assert!(PyErr::occurred(py)); drop(PyErr::fetch(py)); diff --git a/src/exceptions.rs b/src/exceptions.rs index c2ffa860c21..5e739057a34 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -2,8 +2,8 @@ //! Exception types defined by Python. -use crate::types::{PyAny, PyTuple}; use crate::type_object::PySizedLayout; +use crate::types::{PyAny, PyTuple}; use crate::{ffi, AsPyPointer, PyResult, Python}; use std::ffi::CStr; use std::ops; @@ -65,7 +65,7 @@ macro_rules! impl_exception_boilerplate { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { unsafe { use $crate::{AsPyPointer, PyNativeType}; - let cause: &$crate::exceptions::BaseException = self + let cause: &$crate::exceptions::PyBaseException = self .py() .from_owned_ptr_or_opt($crate::ffi::PyException_GetCause(self.as_ptr()))?; @@ -132,7 +132,7 @@ macro_rules! import_exception { 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 _ + Self::type_object_raw($crate::Python::assume_gil_acquired()) as *mut _, ) } @@ -171,16 +171,16 @@ macro_rules! import_exception { /// /// * `module` is the name of the containing module. /// * `MyError` is the name of the new exception type. -/// * `BaseException` is the superclass of `MyError`, usually `pyo3::exceptions::Exception`. +/// * `BaseException` is the superclass of `MyError`, usually `pyo3::exceptions::PyException`. /// /// # Example /// ``` /// use pyo3::prelude::*; /// use pyo3::create_exception; /// use pyo3::types::IntoPyDict; -/// use pyo3::exceptions::Exception; +/// use pyo3::exceptions::PyException; /// -/// create_exception!(mymodule, CustomError, Exception); +/// create_exception!(mymodule, CustomError, PyException); /// /// fn main() { /// let gil = Python::acquire_gil(); @@ -235,7 +235,7 @@ macro_rules! create_exception_type_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 _ + Self::type_object_raw($crate::Python::assume_gil_acquired()) as *mut _, ) } @@ -265,9 +265,12 @@ macro_rules! create_exception_type_object { } macro_rules! impl_native_exception ( - ($name:ident, $exc_name:ident, $layout:path) => ( + ($name:ident, $legacy_name:ident, $exc_name:ident, $layout:path) => ( pub struct $name($crate::PyAny); + #[deprecated(note = "Exceptions now have a `Py` prefix, e.g. `PyException`, `PyTypeError`")] + pub type $legacy_name = $crate::Py<$name>; + $crate::impl_exception_boilerplate!($name); impl $name { @@ -282,80 +285,168 @@ macro_rules! impl_native_exception ( $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); + ($name:ident, $legacy_name:ident, $exc_name:ident) => ( + impl_native_exception!($name, $legacy_name, $exc_name, ffi::PyBaseExceptionObject); ) ); -impl PySizedLayout for ffi::PyBaseExceptionObject {} +impl PySizedLayout for ffi::PyBaseExceptionObject {} -impl_native_exception!(BaseException, PyExc_BaseException); -impl_native_exception!(Exception, PyExc_Exception); -impl_native_exception!(StopAsyncIteration, PyExc_StopAsyncIteration); +impl_native_exception!(PyBaseException, BaseException, PyExc_BaseException); +impl_native_exception!(PyException, Exception, PyExc_Exception); impl_native_exception!( + PyStopAsyncIteration, + StopAsyncIteration, + PyExc_StopAsyncIteration +); +impl_native_exception!( + PyStopIteration, StopIteration, PyExc_StopIteration, ffi::PyStopIterationObject ); -impl_native_exception!(GeneratorExit, PyExc_GeneratorExit); -impl_native_exception!(ArithmeticError, PyExc_ArithmeticError); -impl_native_exception!(LookupError, PyExc_LookupError); - -impl_native_exception!(AssertionError, PyExc_AssertionError); -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, ffi::PyOSErrorObject); -impl_native_exception!(ImportError, PyExc_ImportError); +impl_native_exception!(PyGeneratorExit, GeneratorExit, PyExc_GeneratorExit); +impl_native_exception!(PyArithmeticError, ArithmeticError, PyExc_ArithmeticError); +impl_native_exception!(PyLookupError, LookupError, PyExc_LookupError); + +impl_native_exception!(PyAssertionError, AssertionError, PyExc_AssertionError); +impl_native_exception!(PyAttributeError, AttributeError, PyExc_AttributeError); +impl_native_exception!(PyBufferError, BufferError, PyExc_BufferError); +impl_native_exception!(PyEOFError, EOFError, PyExc_EOFError); +impl_native_exception!( + PyFloatingPointError, + FloatingPointError, + PyExc_FloatingPointError +); +impl_native_exception!(PyOSError, OSError, PyExc_OSError, ffi::PyOSErrorObject); +impl_native_exception!(PyImportError, ImportError, PyExc_ImportError); #[cfg(Py_3_6)] -impl_native_exception!(ModuleNotFoundError, PyExc_ModuleNotFoundError); - -impl_native_exception!(IndexError, PyExc_IndexError); -impl_native_exception!(KeyError, PyExc_KeyError); -impl_native_exception!(KeyboardInterrupt, PyExc_KeyboardInterrupt); -impl_native_exception!(MemoryError, PyExc_MemoryError); -impl_native_exception!(NameError, PyExc_NameError); -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, ffi::PySyntaxErrorObject); -impl_native_exception!(ReferenceError, PyExc_ReferenceError); -impl_native_exception!(SystemError, PyExc_SystemError); -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, ffi::PyUnicodeErrorObject); -impl_native_exception!(UnicodeDecodeError, PyExc_UnicodeDecodeError); -impl_native_exception!(UnicodeEncodeError, PyExc_UnicodeEncodeError); -impl_native_exception!(UnicodeTranslateError, PyExc_UnicodeTranslateError); -impl_native_exception!(ValueError, PyExc_ValueError); -impl_native_exception!(ZeroDivisionError, PyExc_ZeroDivisionError); - -impl_native_exception!(BlockingIOError, PyExc_BlockingIOError); -impl_native_exception!(BrokenPipeError, PyExc_BrokenPipeError); -impl_native_exception!(ChildProcessError, PyExc_ChildProcessError); -impl_native_exception!(ConnectionError, PyExc_ConnectionError); -impl_native_exception!(ConnectionAbortedError, PyExc_ConnectionAbortedError); -impl_native_exception!(ConnectionRefusedError, PyExc_ConnectionRefusedError); -impl_native_exception!(ConnectionResetError, PyExc_ConnectionResetError); -impl_native_exception!(FileExistsError, PyExc_FileExistsError); -impl_native_exception!(FileNotFoundError, PyExc_FileNotFoundError); -impl_native_exception!(InterruptedError, PyExc_InterruptedError); -impl_native_exception!(IsADirectoryError, PyExc_IsADirectoryError); -impl_native_exception!(NotADirectoryError, PyExc_NotADirectoryError); -impl_native_exception!(PermissionError, PyExc_PermissionError); -impl_native_exception!(ProcessLookupError, PyExc_ProcessLookupError); -impl_native_exception!(TimeoutError, PyExc_TimeoutError); - -impl_native_exception!(EnvironmentError, PyExc_EnvironmentError); -impl_native_exception!(IOError, PyExc_IOError); +impl_native_exception!( + PyModuleNotFoundError, + ModuleNotFoundError, + PyExc_ModuleNotFoundError +); + +impl_native_exception!(PyIndexError, IndexError, PyExc_IndexError); +impl_native_exception!(PyKeyError, KeyError, PyExc_KeyError); +impl_native_exception!( + PyKeyboardInterrupt, + KeyboardInterrupt, + PyExc_KeyboardInterrupt +); +impl_native_exception!(PyMemoryError, MemoryError, PyExc_MemoryError); +impl_native_exception!(PyNameError, NameError, PyExc_NameError); +impl_native_exception!(PyOverflowError, OverflowError, PyExc_OverflowError); +impl_native_exception!(PyRuntimeError, RuntimeError, PyExc_RuntimeError); +impl_native_exception!(PyRecursionError, RecursionError, PyExc_RecursionError); +impl_native_exception!( + PyNotImplementedError, + NotImplementedError, + PyExc_NotImplementedError +); +impl_native_exception!( + PySyntaxError, + SyntaxError, + PyExc_SyntaxError, + ffi::PySyntaxErrorObject +); +impl_native_exception!(PyReferenceError, ReferenceError, PyExc_ReferenceError); +impl_native_exception!(PySystemError, SystemError, PyExc_SystemError); +impl_native_exception!( + PySystemExit, + SystemExit, + PyExc_SystemExit, + ffi::PySystemExitObject +); +impl_native_exception!(PyTypeError, TypeError, PyExc_TypeError); +impl_native_exception!( + PyUnboundLocalError, + UnboundLocalError, + PyExc_UnboundLocalError +); +impl_native_exception!( + PyUnicodeError, + UnicodeError, + PyExc_UnicodeError, + ffi::PyUnicodeErrorObject +); +impl_native_exception!( + PyUnicodeDecodeError, + UnicodeDecodeError, + PyExc_UnicodeDecodeError +); +impl_native_exception!( + PyUnicodeEncodeError, + UnicodeEncodeError, + PyExc_UnicodeEncodeError +); +impl_native_exception!( + PyUnicodeTranslateError, + UnicodeTranslateError, + PyExc_UnicodeTranslateError +); +impl_native_exception!(PyValueError, ValueError, PyExc_ValueError); +impl_native_exception!( + PyZeroDivisionError, + ZeroDivisionError, + PyExc_ZeroDivisionError +); + +impl_native_exception!(PyBlockingIOError, BlockingIOError, PyExc_BlockingIOError); +impl_native_exception!(PyBrokenPipeError, BrokenPipeError, PyExc_BrokenPipeError); +impl_native_exception!( + PyChildProcessError, + ChildProcessError, + PyExc_ChildProcessError +); +impl_native_exception!(PyConnectionError, ConnectionError, PyExc_ConnectionError); +impl_native_exception!( + PyConnectionAbortedError, + ConnectionAbortedError, + PyExc_ConnectionAbortedError +); +impl_native_exception!( + PyConnectionRefusedError, + ConnectionRefusedError, + PyExc_ConnectionRefusedError +); +impl_native_exception!( + PyConnectionResetError, + ConnectionResetError, + PyExc_ConnectionResetError +); +impl_native_exception!(PyFileExistsError, FileExistsError, PyExc_FileExistsError); +impl_native_exception!( + PyFileNotFoundError, + FileNotFoundError, + PyExc_FileNotFoundError +); +impl_native_exception!(PyInterruptedError, InterruptedError, PyExc_InterruptedError); +impl_native_exception!( + PyIsADirectoryError, + IsADirectoryError, + PyExc_IsADirectoryError +); +impl_native_exception!( + PyNotADirectoryError, + NotADirectoryError, + PyExc_NotADirectoryError +); +impl_native_exception!(PyPermissionError, PermissionError, PyExc_PermissionError); +impl_native_exception!( + PyProcessLookupError, + ProcessLookupError, + PyExc_ProcessLookupError +); +impl_native_exception!(PyTimeoutError, TimeoutError, PyExc_TimeoutError); + +impl_native_exception!(PyEnvironmentError, EnvironmentError, PyExc_EnvironmentError); +impl_native_exception!(PyIOError, IOError, PyExc_IOError); #[cfg(target_os = "windows")] -impl_native_exception!(WindowsError, PyExc_WindowsError); +impl_native_exception!(PyWindowsError, WindowsError, PyExc_WindowsError); -impl UnicodeDecodeError { +impl PyUnicodeDecodeError { pub fn new_err<'p>( py: Python<'p>, encoding: &CStr, @@ -383,7 +474,7 @@ impl UnicodeDecodeError { err: std::str::Utf8Error, ) -> PyResult<&'p PyAny> { let pos = err.valid_up_to(); - UnicodeDecodeError::new_err( + PyUnicodeDecodeError::new_err( py, CStr::from_bytes_with_nul(b"utf-8\0").unwrap(), input, @@ -393,7 +484,7 @@ impl UnicodeDecodeError { } } -impl StopIteration { +impl PyStopIteration { pub fn stop_iteration(_py: Python, args: &PyTuple) { unsafe { ffi::PyErr_SetObject( @@ -424,7 +515,7 @@ pub mod socket { #[cfg(test)] mod test { - use crate::exceptions::Exception; + use crate::exceptions::PyException; use crate::types::{IntoPyDict, PyDict}; use crate::{AsPyPointer, PyErr, Python}; use std::error::Error; @@ -487,7 +578,7 @@ mod test { #[test] fn custom_exception() { - create_exception!(mymodule, CustomError, Exception); + create_exception!(mymodule, CustomError, PyException); let gil = Python::acquire_gil(); let py = gil.python(); @@ -536,7 +627,7 @@ mod test { write!(&mut out, "{}", err).expect("successful format"); assert_eq!(out, "Exception: banana"); out.clear(); - let convert_ref: &super::BaseException = + let convert_ref: &super::PyBaseException = unsafe { &*(err.as_ptr() as *const _ as *const _) }; let source = convert_ref.source().expect("cause should exist"); write!(&mut out, "{}", source).expect("successful format"); diff --git a/src/instance.rs b/src/instance.rs index dc612428c87..3052288a4ed 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -384,13 +384,14 @@ where impl std::error::Error for Py where T: std::error::Error + PyTypeInfo, - T::AsRefTarget: std::fmt::Display -{ } + T::AsRefTarget: std::fmt::Display, +{ +} impl std::fmt::Display for Py where T: PyTypeInfo, - T::AsRefTarget: std::fmt::Display + T::AsRefTarget: std::fmt::Display, { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let gil = Python::acquire_gil(); diff --git a/src/panic.rs b/src/panic.rs index f6096606914..4884278d4c8 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,5 +1,4 @@ -use crate::exceptions::BaseException; - +use crate::exceptions::PyBaseException; pyo3_exception!( " @@ -10,5 +9,5 @@ pyo3_exception!( Python interpreter to exit. ", PanicException, - BaseException -); \ No newline at end of file + PyBaseException +); diff --git a/src/pycell.rs b/src/pycell.rs index 88052687e5f..64efea72fba 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,7 +1,7 @@ //! Includes `PyCell` implementation. use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; +use crate::exceptions::PyRuntimeError; 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}; @@ -708,7 +708,7 @@ impl fmt::Display for PyBorrowError { impl From for PyErr { fn from(other: PyBorrowError) -> Self { - RuntimeError::py_err(other.to_string()) + PyRuntimeError::py_err(other.to_string()) } } @@ -733,6 +733,6 @@ impl fmt::Display for PyBorrowMutError { impl From for PyErr { fn from(other: PyBorrowMutError) -> Self { - RuntimeError::py_err(other.to_string()) + PyRuntimeError::py_err(other.to_string()) } } diff --git a/src/python.rs b/src/python.rs index 39137769a50..82283dae94d 100644 --- a/src/python.rs +++ b/src/python.rs @@ -101,7 +101,7 @@ impl<'p> Python<'p> { /// # Example /// ``` /// # use pyo3::prelude::*; use pyo3::types::IntoPyDict; use pyo3::wrap_pyfunction; - /// use pyo3::exceptions::RuntimeError; + /// use pyo3::exceptions::PyRuntimeError; /// use std::sync::Arc; /// use std::thread; /// #[pyfunction] @@ -114,7 +114,7 @@ impl<'p> Python<'p> { /// .collect(); /// let mut sum = 0; /// for t in threads { - /// sum += t.join().map_err(|_| PyErr::new::(()))?; + /// sum += t.join().map_err(|_| PyErr::new::(()))?; /// } /// Ok(sum) /// }) diff --git a/src/types/any.rs b/src/types/any.rs index 999ad3582a5..19465e17f9d 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -3,7 +3,7 @@ use crate::conversion::{ AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject, }; use crate::err::{PyDowncastError, PyErr, PyResult}; -use crate::exceptions::TypeError; +use crate::exceptions::PyTypeError; use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType}; use crate::{err, ffi, Py, PyNativeType, PyObject}; use libc::c_int; @@ -163,7 +163,7 @@ impl PyAny { } else if do_compare(other, ffi::Py_GT)? { Ok(Ordering::Greater) } else { - Err(TypeError::py_err( + Err(PyTypeError::py_err( "PyAny::compare(): All comparisons returned false", )) } diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index c468479fe9c..5ddbbee5dd0 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -210,7 +210,7 @@ mod test { let py = gil.python(); if let Err(err) = PyByteArray::from(py, &py.None()) { - assert!(err.is_instance::(py)); + assert!(err.is_instance::(py)); } else { panic!("error"); } diff --git a/src/types/module.rs b/src/types/module.rs index f586ee220d9..1f499d0221d 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -82,7 +82,7 @@ impl PyModule { match self.getattr("__all__") { Ok(idx) => idx.downcast().map_err(PyErr::from), Err(err) => { - if err.is_instance::(self.py()) { + if err.is_instance::(self.py()) { let l = PyList::empty(self.py()); self.setattr("__all__", l).map_err(PyErr::from)?; Ok(l) @@ -101,7 +101,7 @@ impl PyModule { match str::from_utf8(slice) { Ok(s) => Ok(s), Err(e) => Err(PyErr::from_instance( - exceptions::UnicodeDecodeError::new_utf8(self.py(), slice, e)?, + exceptions::PyUnicodeDecodeError::new_utf8(self.py(), slice, e)?, )), } } diff --git a/src/types/num.rs b/src/types/num.rs index 9b2e786a5e4..e3cc3aceec8 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -40,7 +40,7 @@ macro_rules! int_fits_larger_int { fn extract(obj: &'source PyAny) -> PyResult { let val: $larger_type = obj.extract()?; <$rust_type>::try_from(val) - .map_err(|e| exceptions::OverflowError::py_err(e.to_string())) + .map_err(|e| exceptions::PyOverflowError::py_err(e.to_string())) } } }; @@ -145,7 +145,7 @@ macro_rules! int_fits_c_long { } }?; <$rust_type>::try_from(val) - .map_err(|e| exceptions::OverflowError::py_err(e.to_string())) + .map_err(|e| exceptions::PyOverflowError::py_err(e.to_string())) } } }; @@ -551,7 +551,7 @@ mod test { ); let obj = PyObject::from_owned_ptr_or_panic(py, obj); let err = obj.extract::(py).unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance::(py)); } } @@ -569,7 +569,7 @@ mod test { let obj = ("123").to_object(py); let err = obj.extract::<$t>(py).unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance::(py)); } #[test] @@ -579,7 +579,7 @@ mod test { let obj = (12.3).to_object(py); let err = obj.extract::<$t>(py).unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance::(py)); } #[test] diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 97dd819e2fc..dc02da80d29 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -361,7 +361,7 @@ where { let seq = ::try_from(obj)?; if seq.len()? as usize != slice.len() { - return Err(exceptions::BufferError::py_err( + return Err(exceptions::PyBufferError::py_err( "Slice length does not match buffer length.", )); } diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 39c7aeca800..046adf6eef1 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -141,7 +141,7 @@ fn wrong_tuple_length(t: &PyTuple, expected_length: usize) -> PyErr { expected_length, t.len() ); - exceptions::ValueError::py_err(msg) + exceptions::PyValueError::py_err(msg) } macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => { diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index d0d5357d5da..d86783aa64f 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -404,22 +404,22 @@ fn rich_comparisons_python_3_type_error() { let py = gil.python(); let c2 = PyCell::new(py, RichComparisons2 {}).unwrap(); - py_expect_exception!(py, c2, "c2 < c2", TypeError); - py_expect_exception!(py, c2, "c2 < 1", TypeError); - py_expect_exception!(py, c2, "1 < c2", TypeError); - py_expect_exception!(py, c2, "c2 <= c2", TypeError); - py_expect_exception!(py, c2, "c2 <= 1", TypeError); - py_expect_exception!(py, c2, "1 <= c2", TypeError); + py_expect_exception!(py, c2, "c2 < c2", PyTypeError); + py_expect_exception!(py, c2, "c2 < 1", PyTypeError); + py_expect_exception!(py, c2, "1 < c2", PyTypeError); + py_expect_exception!(py, c2, "c2 <= c2", PyTypeError); + py_expect_exception!(py, c2, "c2 <= 1", PyTypeError); + py_expect_exception!(py, c2, "1 <= c2", PyTypeError); py_run!(py, c2, "assert (c2 == c2) == True"); py_run!(py, c2, "assert (c2 == 1) == True"); py_run!(py, c2, "assert (1 == c2) == True"); py_run!(py, c2, "assert (c2 != c2) == False"); py_run!(py, c2, "assert (c2 != 1) == False"); py_run!(py, c2, "assert (1 != c2) == False"); - py_expect_exception!(py, c2, "c2 > c2", TypeError); - py_expect_exception!(py, c2, "c2 > 1", TypeError); - py_expect_exception!(py, c2, "1 > c2", TypeError); - py_expect_exception!(py, c2, "c2 >= c2", TypeError); - py_expect_exception!(py, c2, "c2 >= 1", TypeError); - py_expect_exception!(py, c2, "1 >= c2", TypeError); + py_expect_exception!(py, c2, "c2 > c2", PyTypeError); + py_expect_exception!(py, c2, "c2 > 1", PyTypeError); + py_expect_exception!(py, c2, "1 > c2", PyTypeError); + py_expect_exception!(py, c2, "c2 >= c2", PyTypeError); + py_expect_exception!(py, c2, "c2 >= 1", PyTypeError); + py_expect_exception!(py, c2, "1 >= c2", PyTypeError); } diff --git a/tests/test_buffer_protocol.rs b/tests/test_buffer_protocol.rs index 93903b1fc5c..c7343e19331 100644 --- a/tests/test_buffer_protocol.rs +++ b/tests/test_buffer_protocol.rs @@ -1,6 +1,6 @@ use pyo3::buffer::PyBuffer; use pyo3::class::PyBufferProtocol; -use pyo3::exceptions::BufferError; +use pyo3::exceptions::PyBufferError; use pyo3::ffi; use pyo3::prelude::*; use pyo3::types::IntoPyDict; @@ -21,11 +21,11 @@ struct TestBufferClass { impl PyBufferProtocol for TestBufferClass { fn bf_getbuffer(slf: PyRefMut, view: *mut ffi::Py_buffer, flags: c_int) -> PyResult<()> { if view.is_null() { - return Err(BufferError::py_err("View is null")); + return Err(PyBufferError::py_err("View is null")); } if (flags & ffi::PyBUF_WRITABLE) == ffi::PyBUF_WRITABLE { - return Err(BufferError::py_err("Object is not writable")); + return Err(PyBufferError::py_err("Object is not writable")); } unsafe { diff --git a/tests/test_class_attributes.rs b/tests/test_class_attributes.rs index a2e4fe8264f..752935d76e5 100644 --- a/tests/test_class_attributes.rs +++ b/tests/test_class_attributes.rs @@ -56,7 +56,7 @@ fn class_attributes_are_immutable() { let gil = Python::acquire_gil(); let py = gil.python(); let foo_obj = py.get_type::(); - py_expect_exception!(py, foo_obj, "foo_obj.a = 6", TypeError); + py_expect_exception!(py, foo_obj, "foo_obj.a = 6", PyTypeError); } #[pymethods] diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs index f3799b033fa..7b1f9a52508 100644 --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -2,7 +2,7 @@ use pyo3::class::{ PyAsyncProtocol, PyContextProtocol, PyDescrProtocol, PyIterProtocol, PyMappingProtocol, PyObjectProtocol, PySequenceProtocol, }; -use pyo3::exceptions::{IndexError, ValueError}; +use pyo3::exceptions::{PyIndexError, PyValueError}; use pyo3::prelude::*; use pyo3::types::{IntoPyDict, PyBytes, PySlice, PyType}; use pyo3::{ffi, py_run, AsPyPointer, PyCell}; @@ -42,7 +42,7 @@ fn len() { }, ) .unwrap(); - py_expect_exception!(py, inst, "len(inst)", OverflowError); + py_expect_exception!(py, inst, "len(inst)", PyOverflowError); } #[pyclass] @@ -171,7 +171,7 @@ impl PySequenceProtocol for Sequence { if let Some(s) = self.fields.get(idx) { Ok(s.clone()) } else { - Err(PyErr::new::(())) + Err(PyErr::new::(())) } } @@ -181,7 +181,7 @@ impl PySequenceProtocol for Sequence { *elem = value; Ok(()) } else { - Err(PyErr::new::(())) + Err(PyErr::new::(())) } } } @@ -202,7 +202,7 @@ fn sequence() { assert c[0] == 'H' "# ); - py_expect_exception!(py, c, "c['abc']", TypeError); + py_expect_exception!(py, c, "c['abc']", PyTypeError); } #[pyclass] @@ -256,7 +256,7 @@ fn setitem() { assert_eq!(c.key, 1); assert_eq!(c.val, 2); } - py_expect_exception!(py, c, "del c[1]", NotImplementedError); + py_expect_exception!(py, c, "del c[1]", PyNotImplementedError); } #[pyclass] @@ -282,7 +282,7 @@ fn delitem() { let c = c.borrow(); assert_eq!(c.key, 1); } - py_expect_exception!(py, c, "c[1] = 2", NotImplementedError); + py_expect_exception!(py, c, "c[1] = 2", PyNotImplementedError); } #[pyclass] @@ -354,7 +354,7 @@ fn contains() { let c = Py::new(py, Contains {}).unwrap(); py_run!(py, c, "assert 1 in c"); py_run!(py, c, "assert -1 not in c"); - py_expect_exception!(py, c, "assert 'wrong type' not in c", TypeError); + py_expect_exception!(py, c, "assert 'wrong type' not in c", PyTypeError); } #[pyclass] @@ -376,7 +376,7 @@ impl<'p> PyContextProtocol<'p> for ContextManager { ) -> bool { let gil = GILGuard::acquire(); self.exit_called = true; - ty == Some(gil.python().get_type::()) + ty == Some(gil.python().get_type::()) } } @@ -402,7 +402,7 @@ fn context_manager() { py, c, "with c as x: raise NotImplementedError", - NotImplementedError + PyNotImplementedError ); let c = c.borrow(); assert!(c.exit_called); @@ -438,7 +438,7 @@ impl<'p> PyMappingProtocol<'p> for Test { return Ok("int".into_py(gil.python())); } } - Err(PyErr::new::("error")) + Err(PyErr::new::("error")) } } diff --git a/tests/test_exceptions.rs b/tests/test_exceptions.rs index 20428b3015d..3726dfb7e39 100644 --- a/tests/test_exceptions.rs +++ b/tests/test_exceptions.rs @@ -46,7 +46,7 @@ impl fmt::Display for CustomError { impl std::convert::From for PyErr { fn from(err: CustomError) -> PyErr { - exceptions::OSError::py_err(err.to_string()) + exceptions::PyOSError::py_err(err.to_string()) } } diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index d63d91549af..4c346563e87 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -100,7 +100,10 @@ fn mutation_fails() { let e = py .run("obj.base_set(lambda: obj.sub_set_and_ret(1))", global, None) .unwrap_err(); - assert_eq!(&e.instance(py).to_string(), "RuntimeError: Already borrowed") + assert_eq!( + &e.instance(py).to_string(), + "RuntimeError: Already borrowed" + ) } #[pyclass] diff --git a/tests/test_mapping.rs b/tests/test_mapping.rs index 41bdb029c55..35369e01b4e 100644 --- a/tests/test_mapping.rs +++ b/tests/test_mapping.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use pyo3::exceptions::KeyError; +use pyo3::exceptions::PyKeyError; use pyo3::prelude::*; use pyo3::types::IntoPyDict; use pyo3::types::PyList; @@ -40,7 +40,7 @@ impl PyMappingProtocol for Mapping { self.index .get(&query) .copied() - .ok_or_else(|| KeyError::py_err("unknown key")) + .ok_or_else(|| PyKeyError::py_err("unknown key")) } fn __setitem__(&mut self, key: String, value: usize) { @@ -49,7 +49,7 @@ impl PyMappingProtocol for Mapping { fn __delitem__(&mut self, key: String) -> PyResult<()> { if self.index.remove(&key).is_none() { - KeyError::py_err("unknown key").into() + PyKeyError::py_err("unknown key").into() } else { Ok(()) } diff --git a/tests/test_methods.rs b/tests/test_methods.rs index da473fccc6a..20292fdae49 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -273,7 +273,7 @@ fn meth_args() { py_run!(py, inst, "assert inst.get_default() == 10"); py_run!(py, inst, "assert inst.get_default(100) == 100"); py_run!(py, inst, "assert inst.get_kwarg() == 10"); - py_expect_exception!(py, inst, "inst.get_kwarg(100)", TypeError); + py_expect_exception!(py, inst, "inst.get_kwarg(100)", PyTypeError); py_run!(py, inst, "assert inst.get_kwarg(test=100) == 100"); py_run!(py, inst, "assert inst.get_kwargs() == [(), None]"); py_run!(py, inst, "assert inst.get_kwargs(1,2,3) == [(1,2,3), None]"); @@ -300,9 +300,9 @@ fn meth_args() { "assert inst.get_pos_arg_kw(1, b=2) == [1, (), {'b': 2}]" ); py_run!(py, inst, "assert inst.get_pos_arg_kw(a=1) == [1, (), None]"); - py_expect_exception!(py, inst, "inst.get_pos_arg_kw()", TypeError); - py_expect_exception!(py, inst, "inst.get_pos_arg_kw(1, a=1)", TypeError); - py_expect_exception!(py, inst, "inst.get_pos_arg_kw(b=2)", TypeError); + py_expect_exception!(py, inst, "inst.get_pos_arg_kw()", PyTypeError); + py_expect_exception!(py, inst, "inst.get_pos_arg_kw(1, a=1)", PyTypeError); + py_expect_exception!(py, inst, "inst.get_pos_arg_kw(b=2)", PyTypeError); py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1) == 6"); py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1, 2) == 6"); @@ -311,7 +311,7 @@ fn meth_args() { inst, "assert inst.get_pos_arg_kw_sep1(1, 2, c=13) == 16" ); - py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep1(1, 2, 3)", TypeError); + py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep1(1, 2, 3)", PyTypeError); py_run!(py, inst, "assert inst.get_pos_arg_kw_sep2(1) == 6"); py_run!( @@ -319,10 +319,10 @@ fn meth_args() { inst, "assert inst.get_pos_arg_kw_sep2(1, b=12, c=13) == 26" ); - py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep2(1, 2)", TypeError); + py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep2(1, 2)", PyTypeError); py_run!(py, inst, "assert inst.get_pos_kw(1, b=2) == [1, {'b': 2}]"); - py_expect_exception!(py, inst, "inst.get_pos_kw(1,2)", TypeError); + py_expect_exception!(py, inst, "inst.get_pos_kw(1,2)", PyTypeError); py_run!(py, inst, "assert inst.args_as_vec(1,2,3) == 6"); } diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index 825670f2bc2..e8e95bdf332 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -47,7 +47,7 @@ a = array.array("i", [0, 1, 2, 3]) b = array.array("I", [0, 1, 2, 3]) f(a, b) "#, - BufferError + PyBufferError ); pyo3::py_run!( diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index 1b4ffc4827d..d46edfb3b45 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -1,5 +1,5 @@ use pyo3::class::PySequenceProtocol; -use pyo3::exceptions::{IndexError, ValueError}; +use pyo3::exceptions::{PyIndexError, PyValueError}; use pyo3::prelude::*; use pyo3::types::{IntoPyDict, PyList}; @@ -41,7 +41,7 @@ impl PySequenceProtocol for ByteSequence { self.elements .get(idx as usize) .copied() - .ok_or_else(|| IndexError::py_err("list index out of range")) + .ok_or_else(|| PyIndexError::py_err("list index out of range")) } fn __setitem__(&mut self, idx: isize, value: u8) { @@ -53,7 +53,7 @@ impl PySequenceProtocol for ByteSequence { self.elements.remove(idx as usize); Ok(()) } else { - Err(IndexError::py_err("list index out of range")) + Err(PyIndexError::py_err("list index out of range")) } } @@ -78,7 +78,7 @@ impl PySequenceProtocol for ByteSequence { } Ok(Self { elements }) } else { - Err(ValueError::py_err("invalid repeat count")) + Err(PyValueError::py_err("invalid repeat count")) } } } @@ -242,7 +242,7 @@ impl PySequenceProtocol for OptionList { fn __getitem__(&self, idx: isize) -> PyResult> { match self.items.get(idx as usize) { Some(x) => Ok(*x), - None => Err(PyErr::new::("Index out of bounds")), + None => Err(PyIndexError::py_err("Index out of bounds")), } } } @@ -263,5 +263,5 @@ fn test_option_list_get() { py_assert!(py, list, "list[0] == 1"); py_assert!(py, list, "list[1] == None"); - py_expect_exception!(py, list, "list[2]", IndexError); + py_expect_exception!(py, list, "list[2]", PyIndexError); }