Add `Py2` variants of `PyDowncastError`

This commit is contained in:
David Hewitt 2023-12-19 20:06:59 +00:00
parent e727640ef3
commit 1451418ee4
3 changed files with 107 additions and 24 deletions

View File

@ -1,5 +1,7 @@
use crate::instance::Py2;
use crate::panic::PanicException;
use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::{PyTraceback, PyType};
use crate::{
exceptions::{self, PyBaseException},
@ -61,6 +63,42 @@ impl<'a> PyDowncastError<'a> {
}
}
/// Error that indicates a failure to convert a PyAny to a more specific Python type.
#[derive(Debug)]
pub(crate) struct PyDowncastError2<'a, 'py> {
from: &'a Py2<'py, PyAny>,
to: Cow<'static, str>,
}
impl<'a, 'py> PyDowncastError2<'a, 'py> {
/// Create a new `PyDowncastError` representing a failure to convert the object
/// `from` into the type named in `to`.
pub fn new(from: &'a Py2<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
PyDowncastError2 {
from,
to: to.into(),
}
}
}
/// Error that indicates a failure to convert a PyAny to a more specific Python type.
#[derive(Debug)]
pub(crate) struct PyDowncastIntoError<'py> {
from: Py2<'py, PyAny>,
to: Cow<'static, str>,
}
impl<'py> PyDowncastIntoError<'py> {
/// Create a new `PyDowncastIntoError` representing a failure to convert the object
/// `from` into the type named in `to`.
pub fn new(from: Py2<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
PyDowncastIntoError {
from,
to: to.into(),
}
}
}
impl PyErr {
/// Creates a new PyErr of type `T`.
///
@ -773,18 +811,63 @@ impl<'a> std::error::Error for PyDowncastError<'a> {}
impl<'a> std::fmt::Display for PyDowncastError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"'{}' object cannot be converted to '{}'",
self.from
.get_type()
.qualname()
.map_err(|_| std::fmt::Error)?,
self.to
)
display_downcast_error(f, Py2::borrowed_from_gil_ref(&self.from), &self.to)
}
}
/// Convert `PyDowncastError2` to Python `TypeError`.
impl std::convert::From<PyDowncastError2<'_, '_>> for PyErr {
fn from(err: PyDowncastError2<'_, '_>) -> PyErr {
let args = PyDowncastErrorArguments {
from: err.from.get_type().into(),
to: err.to,
};
exceptions::PyTypeError::new_err(args)
}
}
impl std::error::Error for PyDowncastError2<'_, '_> {}
impl std::fmt::Display for PyDowncastError2<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
display_downcast_error(f, self.from, &self.to)
}
}
/// Convert `PyDowncastIntoError` to Python `TypeError`.
impl std::convert::From<PyDowncastIntoError<'_>> for PyErr {
fn from(err: PyDowncastIntoError<'_>) -> PyErr {
let args = PyDowncastErrorArguments {
from: err.from.get_type().into(),
to: err.to,
};
exceptions::PyTypeError::new_err(args)
}
}
impl std::error::Error for PyDowncastIntoError<'_> {}
impl std::fmt::Display for PyDowncastIntoError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
display_downcast_error(f, &self.from, &self.to)
}
}
fn display_downcast_error(
f: &mut std::fmt::Formatter<'_>,
from: &Py2<'_, PyAny>,
to: &str,
) -> std::fmt::Result {
write!(
f,
"'{}' object cannot be converted to '{}'",
from.get_type().qualname().map_err(|_| std::fmt::Error)?,
to
)
}
pub fn panic_after_error(_py: Python<'_>) -> ! {
unsafe {
ffi::PyErr_Print();

View File

@ -1,6 +1,6 @@
use crate::class::basic::CompareOp;
use crate::conversion::{AsPyPointer, FromPyObject, IntoPy, ToPyObject};
use crate::err::{PyDowncastError, PyErr, PyResult};
use crate::err::{PyDowncastError, PyDowncastError2, PyDowncastIntoError, PyErr, PyResult};
use crate::exceptions::{PyAttributeError, PyTypeError};
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::instance::Py2;
@ -1563,12 +1563,12 @@ pub(crate) trait PyAnyMethods<'py> {
/// })
/// # }
/// ```
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
where
T: PyTypeCheck;
/// Like `downcast` but takes ownership of `self`.
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
where
T: PyTypeCheck;
@ -1602,12 +1602,12 @@ pub(crate) trait PyAnyMethods<'py> {
/// assert!(any.downcast_exact::<PyBool>().is_ok());
/// });
/// ```
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
where
T: PyTypeInfo;
/// Like `downcast_exact` but takes ownership of `self`.
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
where
T: PyTypeInfo;
@ -2036,7 +2036,7 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
}
#[inline]
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
where
T: PyTypeCheck,
{
@ -2044,12 +2044,12 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
// Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_unchecked() })
} else {
Err(PyDowncastError::new(self.clone().into_gil_ref(), T::NAME))
Err(PyDowncastError2::new(self, T::NAME))
}
}
#[inline]
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
where
T: PyTypeCheck,
{
@ -2057,12 +2057,12 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
// Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_into_unchecked() })
} else {
Err(PyDowncastError::new(self.clone().into_gil_ref(), T::NAME))
Err(PyDowncastIntoError::new(self, T::NAME))
}
}
#[inline]
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
where
T: PyTypeInfo,
{
@ -2070,12 +2070,12 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
// Safety: is_exact_instance_of is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_unchecked() })
} else {
Err(PyDowncastError::new(self.clone().into_gil_ref(), T::NAME))
Err(PyDowncastError2::new(self, T::NAME))
}
}
#[inline]
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
where
T: PyTypeInfo,
{
@ -2083,7 +2083,7 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
// Safety: is_exact_instance_of is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_into_unchecked() })
} else {
Err(PyDowncastError::new(self.into_gil_ref(), T::NAME))
Err(PyDowncastIntoError::new(self, T::NAME))
}
}

View File

@ -9,10 +9,10 @@ error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied
<PyErr as From<PyBorrowMutError>>
<PyErr as From<std::io::Error>>
<PyErr as From<PyDowncastError<'a>>>
<PyErr as From<pyo3::err::PyDowncastError2<'_, '_>>>
<PyErr as From<pyo3::err::PyDowncastIntoError<'_>>>
<PyErr as From<NulError>>
<PyErr as From<IntoStringError>>
<PyErr as From<FromUtf8Error>>
<PyErr as From<FromUtf16Error>>
and $N others
= note: required for `MyError` to implement `Into<PyErr>`
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)