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/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/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 fde8929547a..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,11 +344,16 @@ 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 { + pub fn instance(mut self, py: Python) -> &exceptions::PyBaseException { self.normalize(py); match self.pvalue { - PyErrValue::Value(ref instance) => instance.clone_ref(py), - _ => py.None(), + 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!(), } } @@ -430,28 +435,34 @@ impl std::fmt::Debug for PyErr { impl FromPy for PyObject { fn from_py(other: PyErr, py: Python) -> Self { - other.instance(py) + other.instance(py).into() + } +} + +impl FromPy for Py { + fn from_py(other: PyErr, py: Python) -> Self { + other.instance(py).into() } } 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() } } /// Convert `PyDowncastError` to Python `TypeError`. impl std::convert::From for PyErr { fn from(_err: PyDowncastError) -> PyErr { - exceptions::TypeError.into() + exceptions::PyTypeError::py_err(()) } } @@ -504,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!()) } - io::ErrorKind::TimedOut => PyErr::from_value::(err_value!()), - _ => PyErr::from_value::(err_value!()), + _ => PyErr::from_value::(err_value!()), } } } @@ -538,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)) } } @@ -556,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 { @@ -600,7 +619,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::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 a277189cec9..5e739057a34 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -2,12 +2,9 @@ //! Exception types defined by Python. -use crate::err::{PyErr, PyResult}; -use crate::ffi; -use crate::type_object::PyTypeObject; +use crate::type_object::PySizedLayout; use crate::types::{PyAny, PyTuple}; -use crate::Python; -use crate::{AsPyPointer, ToPyObject}; +use crate::{ffi, AsPyPointer, PyResult, Python}; use std::ffi::CStr; use std::ops; use std::os::raw::c_char; @@ -16,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, ": ") + } + } + } - pub fn into(args: T) -> $crate::PyResult { - $crate::PyErr::new::(args).into() + impl std::error::Error for $name { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + unsafe { + use $crate::{AsPyPointer, PyNativeType}; + let cause: &$crate::exceptions::PyBaseException = self + .py() + .from_owned_ptr_or_opt($crate::ffi::PyException_GetCause(self.as_ptr()))?; + + Some(cause) + } } } }; @@ -74,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(); @@ -110,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 _ } } }; @@ -124,16 +171,16 @@ macro_rules! import_exception_type_object { /// /// * `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(); @@ -157,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); @@ -171,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(); @@ -191,105 +258,195 @@ 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, $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 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() - } - } 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() - } - } - unsafe impl PyTypeObject for $name { - fn type_object(py: $crate::Python) -> &$crate::types::PyType { - unsafe { py.from_borrowed_ptr(ffi::$exc_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 { + ffi::PyObject_TypeCheck(ptr, ffi::$exc_name as *mut _) } } + + $crate::pyobject_native_type_core!($name, $layout, *(ffi::$exc_name as *mut ffi::PyTypeObject), Some("builtins"), $name::check); ); + ($name:ident, $legacy_name:ident, $exc_name:ident) => ( + impl_native_exception!($name, $legacy_name, $exc_name, 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!(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); -impl_native_exception!(ImportError, PyExc_ImportError); +impl PySizedLayout for ffi::PyBaseExceptionObject {} + +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!(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); -impl_native_exception!(ReferenceError, PyExc_ReferenceError); -impl_native_exception!(SystemError, PyExc_SystemError); -impl_native_exception!(SystemExit, PyExc_SystemExit); -impl_native_exception!(TypeError, PyExc_TypeError); -impl_native_exception!(UnboundLocalError, PyExc_UnboundLocalError); -impl_native_exception!(UnicodeError, PyExc_UnicodeError); -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, @@ -317,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, @@ -327,7 +484,7 @@ impl UnicodeDecodeError { } } -impl StopIteration { +impl PyStopIteration { pub fn stop_iteration(_py: Python, args: &PyTuple) { unsafe { ffi::PyErr_SetObject( @@ -358,9 +515,11 @@ pub mod socket { #[cfg(test)] mod test { - use crate::exceptions::Exception; + use crate::exceptions::PyException; use crate::types::{IntoPyDict, PyDict}; - use crate::{PyErr, Python}; + use crate::{AsPyPointer, PyErr, Python}; + use std::error::Error; + use std::fmt::Write; import_exception!(socket, gaierror); import_exception!(email.errors, MessageError); @@ -370,7 +529,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)) @@ -394,7 +553,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)) @@ -419,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(); @@ -438,4 +597,42 @@ mod test { ) .unwrap(); } + + #[test] + fn native_exception_display() { + let mut out = String::new(); + let gil = Python::acquire_gil(); + let py = gil.python(); + let err = py + .run("raise Exception('banana')", None, None) + .expect_err("raising should have given us an error") + .instance(py); + write!(&mut out, "{}", err).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 err = py + .run( + "raise Exception('banana') from TypeError('peach')", + None, + None, + ) + .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::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"); + assert_eq!(out, "TypeError: peach"); + let source_source = source.source(); + assert!(source_source.is_none(), "source_source should be None"); + } } 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..3052288a4ed 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,35 @@ 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..4884278d4c8 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,12 +1,13 @@ -use crate::exceptions::BaseException; +use crate::exceptions::PyBaseException; -/// 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!( + " + The exception raised when Rust code called from Python panics. -pyo3_exception!(PanicException, BaseException); + 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, + PyBaseException +); diff --git a/src/pycell.rs b/src/pycell.rs index 1a74c6506e8..64efea72fba 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,5 +1,6 @@ //! Includes `PyCell` implementation. use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; +use crate::exceptions::PyRuntimeError; use crate::pyclass::{PyClass, PyClassThreadChecker}; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; @@ -705,6 +706,12 @@ impl fmt::Display for PyBorrowError { } } +impl From for PyErr { + fn from(other: PyBorrowError) -> Self { + PyRuntimeError::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 { + 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 5132183733a..19465e17f9d 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -3,9 +3,9 @@ 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, 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> @@ -161,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/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/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 254953c7820..e3cc3aceec8 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::PyOverflowError::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::PyOverflowError::py_err(e.to_string())) } } }; @@ -549,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)); } } @@ -567,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] @@ -577,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 1ec72302a49..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!(e.is_instance::(py)) + 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); }