diff --git a/src/err/mod.rs b/src/err/mod.rs index ab39e8cd..5e054449 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -982,6 +982,24 @@ impl PyErrArguments for PyDowncastErrorArguments { } } +/// Python exceptions that can be converted to [`PyErr`]. +/// +/// This is used to implement [`From> for PyErr`]. +/// +/// Users should not need to implement this trait directly. It is implemented automatically in the +/// [`crate::import_exception!`] and [`crate::create_exception!`] macros. +pub trait ToPyErr {} + +impl<'py, T> std::convert::From> for PyErr +where + T: ToPyErr, +{ + #[inline] + fn from(err: Bound<'py, T>) -> PyErr { + PyErr::from_value_bound(err.into_any()) + } +} + /// Convert `PyDowncastError` to Python `TypeError`. impl<'a> std::convert::From> for PyErr { fn from(err: PyDowncastError<'_>) -> PyErr { diff --git a/src/exceptions.rs b/src/exceptions.rs index 07147016..1c952393 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -54,6 +54,8 @@ macro_rules! impl_exception_boilerplate { } } } + + impl $crate::ToPyErr for $name {} }; } @@ -1074,7 +1076,7 @@ mod tests { ); // Restoring should preserve the same error - let e = PyErr::from_value_bound(decode_err.into_any()); + let e: PyErr = decode_err.into(); e.restore(py); assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index 6b39b534..d0c53c4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -308,7 +308,7 @@ pub use crate::conversion::{AsPyPointer, FromPyObject, FromPyPointer, IntoPy, To #[allow(deprecated)] pub use crate::conversion::{PyTryFrom, PyTryInto}; pub use crate::err::{ - DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyErrArguments, PyResult, + DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyErrArguments, PyResult, ToPyErr, }; pub use crate::gil::GILPool; #[cfg(not(PyPy))] diff --git a/src/types/string.rs b/src/types/string.rs index 0a7847d2..4a33236b 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -73,9 +73,7 @@ impl<'a> PyStringData<'a> { match self { Self::Ucs1(data) => match str::from_utf8(data) { Ok(s) => Ok(Cow::Borrowed(s)), - Err(e) => Err(crate::PyErr::from_value_bound( - PyUnicodeDecodeError::new_utf8_bound(py, data, e)?.into_any(), - )), + Err(e) => Err(PyUnicodeDecodeError::new_utf8_bound(py, data, e)?.into()), }, Self::Ucs2(data) => match String::from_utf16(data) { Ok(s) => Ok(Cow::Owned(s)), @@ -83,30 +81,26 @@ impl<'a> PyStringData<'a> { let mut message = e.to_string().as_bytes().to_vec(); message.push(0); - Err(crate::PyErr::from_value_bound( - PyUnicodeDecodeError::new_bound( - py, - CStr::from_bytes_with_nul(b"utf-16\0").unwrap(), - self.as_bytes(), - 0..self.as_bytes().len(), - CStr::from_bytes_with_nul(&message).unwrap(), - )? - .into_any(), - )) + Err(PyUnicodeDecodeError::new_bound( + py, + CStr::from_bytes_with_nul(b"utf-16\0").unwrap(), + self.as_bytes(), + 0..self.as_bytes().len(), + CStr::from_bytes_with_nul(&message).unwrap(), + )? + .into()) } }, Self::Ucs4(data) => match data.iter().map(|&c| std::char::from_u32(c)).collect() { Some(s) => Ok(Cow::Owned(s)), - None => Err(crate::PyErr::from_value_bound( - PyUnicodeDecodeError::new_bound( - py, - CStr::from_bytes_with_nul(b"utf-32\0").unwrap(), - self.as_bytes(), - 0..self.as_bytes().len(), - CStr::from_bytes_with_nul(b"error converting utf-32\0").unwrap(), - )? - .into_any(), - )), + None => Err(PyUnicodeDecodeError::new_bound( + py, + CStr::from_bytes_with_nul(b"utf-32\0").unwrap(), + self.as_bytes(), + 0..self.as_bytes().len(), + CStr::from_bytes_with_nul(b"error converting utf-32\0").unwrap(), + )? + .into()), }, } } diff --git a/tests/ui/invalid_result_conversion.stderr b/tests/ui/invalid_result_conversion.stderr index b3e65517..73ed2cba 100644 --- a/tests/ui/invalid_result_conversion.stderr +++ b/tests/ui/invalid_result_conversion.stderr @@ -5,6 +5,7 @@ error[E0277]: the trait bound `PyErr: From` is not satisfied | ^^^^^^^^^^^^^ the trait `From` is not implemented for `PyErr` | = help: the following other types implement trait `From`: + >> > > > @@ -12,7 +13,6 @@ error[E0277]: the trait bound `PyErr: From` is not satisfied >> >> > - > and $N others = note: required for `MyError` to implement `Into` = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)