Skip to content

Commit

Permalink
Add Py2 variants of PyDowncastError
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 19, 2023
1 parent e727640 commit f60f5e5
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 13 deletions.
94 changes: 94 additions & 0 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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`.
///
Expand Down Expand Up @@ -785,6 +823,62 @@ impl<'a> std::fmt::Display for PyDowncastError<'a> {
}
}

/// 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> {
write!(
f,
"'{}' object cannot be converted to '{}'",
self.from
.get_type()
.qualname()
.map_err(|_| std::fmt::Error)?,
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> {
write!(
f,
"'{}' object cannot be converted to '{}'",
self.from
.get_type()
.qualname()
.map_err(|_| std::fmt::Error)?,
self.to
)
}
}

pub fn panic_after_error(_py: Python<'_>) -> ! {
unsafe {
ffi::PyErr_Print();
Expand Down
26 changes: 13 additions & 13 deletions src/types/any.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -2036,54 +2036,54 @@ 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,
{
if T::type_check(self.as_gil_ref()) {
// 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,
{
if T::type_check(self.as_gil_ref()) {
// 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,
{
if self.is_exact_instance_of::<T>() {
// 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,
{
if self.is_exact_instance_of::<T>() {
// 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))
}
}

Expand Down

0 comments on commit f60f5e5

Please sign in to comment.