Skip to content

Commit ee54132

Browse files
authored
Merge pull request #3671 from davidhewitt/dh/downcast-error-split
Add `Py2` variants of `PyDowncastError`
2 parents 54ba6e8 + 1451418 commit ee54132

File tree

3 files changed

+107
-24
lines changed

3 files changed

+107
-24
lines changed

src/err/mod.rs

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use crate::instance::Py2;
12
use crate::panic::PanicException;
23
use crate::type_object::PyTypeInfo;
4+
use crate::types::any::PyAnyMethods;
35
use crate::types::{PyTraceback, PyType};
46
use crate::{
57
exceptions::{self, PyBaseException},
@@ -61,6 +63,42 @@ impl<'a> PyDowncastError<'a> {
6163
}
6264
}
6365

66+
/// Error that indicates a failure to convert a PyAny to a more specific Python type.
67+
#[derive(Debug)]
68+
pub(crate) struct PyDowncastError2<'a, 'py> {
69+
from: &'a Py2<'py, PyAny>,
70+
to: Cow<'static, str>,
71+
}
72+
73+
impl<'a, 'py> PyDowncastError2<'a, 'py> {
74+
/// Create a new `PyDowncastError` representing a failure to convert the object
75+
/// `from` into the type named in `to`.
76+
pub fn new(from: &'a Py2<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
77+
PyDowncastError2 {
78+
from,
79+
to: to.into(),
80+
}
81+
}
82+
}
83+
84+
/// Error that indicates a failure to convert a PyAny to a more specific Python type.
85+
#[derive(Debug)]
86+
pub(crate) struct PyDowncastIntoError<'py> {
87+
from: Py2<'py, PyAny>,
88+
to: Cow<'static, str>,
89+
}
90+
91+
impl<'py> PyDowncastIntoError<'py> {
92+
/// Create a new `PyDowncastIntoError` representing a failure to convert the object
93+
/// `from` into the type named in `to`.
94+
pub fn new(from: Py2<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
95+
PyDowncastIntoError {
96+
from,
97+
to: to.into(),
98+
}
99+
}
100+
}
101+
64102
impl PyErr {
65103
/// Creates a new PyErr of type `T`.
66104
///
@@ -773,18 +811,63 @@ impl<'a> std::error::Error for PyDowncastError<'a> {}
773811

774812
impl<'a> std::fmt::Display for PyDowncastError<'a> {
775813
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
776-
write!(
777-
f,
778-
"'{}' object cannot be converted to '{}'",
779-
self.from
780-
.get_type()
781-
.qualname()
782-
.map_err(|_| std::fmt::Error)?,
783-
self.to
784-
)
814+
display_downcast_error(f, Py2::borrowed_from_gil_ref(&self.from), &self.to)
785815
}
786816
}
787817

818+
/// Convert `PyDowncastError2` to Python `TypeError`.
819+
impl std::convert::From<PyDowncastError2<'_, '_>> for PyErr {
820+
fn from(err: PyDowncastError2<'_, '_>) -> PyErr {
821+
let args = PyDowncastErrorArguments {
822+
from: err.from.get_type().into(),
823+
to: err.to,
824+
};
825+
826+
exceptions::PyTypeError::new_err(args)
827+
}
828+
}
829+
830+
impl std::error::Error for PyDowncastError2<'_, '_> {}
831+
832+
impl std::fmt::Display for PyDowncastError2<'_, '_> {
833+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
834+
display_downcast_error(f, self.from, &self.to)
835+
}
836+
}
837+
838+
/// Convert `PyDowncastIntoError` to Python `TypeError`.
839+
impl std::convert::From<PyDowncastIntoError<'_>> for PyErr {
840+
fn from(err: PyDowncastIntoError<'_>) -> PyErr {
841+
let args = PyDowncastErrorArguments {
842+
from: err.from.get_type().into(),
843+
to: err.to,
844+
};
845+
846+
exceptions::PyTypeError::new_err(args)
847+
}
848+
}
849+
850+
impl std::error::Error for PyDowncastIntoError<'_> {}
851+
852+
impl std::fmt::Display for PyDowncastIntoError<'_> {
853+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
854+
display_downcast_error(f, &self.from, &self.to)
855+
}
856+
}
857+
858+
fn display_downcast_error(
859+
f: &mut std::fmt::Formatter<'_>,
860+
from: &Py2<'_, PyAny>,
861+
to: &str,
862+
) -> std::fmt::Result {
863+
write!(
864+
f,
865+
"'{}' object cannot be converted to '{}'",
866+
from.get_type().qualname().map_err(|_| std::fmt::Error)?,
867+
to
868+
)
869+
}
870+
788871
pub fn panic_after_error(_py: Python<'_>) -> ! {
789872
unsafe {
790873
ffi::PyErr_Print();

src/types/any.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::class::basic::CompareOp;
22
use crate::conversion::{AsPyPointer, FromPyObject, IntoPy, ToPyObject};
3-
use crate::err::{PyDowncastError, PyErr, PyResult};
3+
use crate::err::{PyDowncastError, PyDowncastError2, PyDowncastIntoError, PyErr, PyResult};
44
use crate::exceptions::{PyAttributeError, PyTypeError};
55
use crate::ffi_ptr_ext::FfiPtrExt;
66
use crate::instance::Py2;
@@ -1563,12 +1563,12 @@ pub(crate) trait PyAnyMethods<'py> {
15631563
/// })
15641564
/// # }
15651565
/// ```
1566-
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
1566+
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
15671567
where
15681568
T: PyTypeCheck;
15691569

15701570
/// Like `downcast` but takes ownership of `self`.
1571-
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
1571+
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
15721572
where
15731573
T: PyTypeCheck;
15741574

@@ -1602,12 +1602,12 @@ pub(crate) trait PyAnyMethods<'py> {
16021602
/// assert!(any.downcast_exact::<PyBool>().is_ok());
16031603
/// });
16041604
/// ```
1605-
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
1605+
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
16061606
where
16071607
T: PyTypeInfo;
16081608

16091609
/// Like `downcast_exact` but takes ownership of `self`.
1610-
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
1610+
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
16111611
where
16121612
T: PyTypeInfo;
16131613

@@ -2036,54 +2036,54 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
20362036
}
20372037

20382038
#[inline]
2039-
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
2039+
fn downcast<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
20402040
where
20412041
T: PyTypeCheck,
20422042
{
20432043
if T::type_check(self.as_gil_ref()) {
20442044
// Safety: type_check is responsible for ensuring that the type is correct
20452045
Ok(unsafe { self.downcast_unchecked() })
20462046
} else {
2047-
Err(PyDowncastError::new(self.clone().into_gil_ref(), T::NAME))
2047+
Err(PyDowncastError2::new(self, T::NAME))
20482048
}
20492049
}
20502050

20512051
#[inline]
2052-
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
2052+
fn downcast_into<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
20532053
where
20542054
T: PyTypeCheck,
20552055
{
20562056
if T::type_check(self.as_gil_ref()) {
20572057
// Safety: type_check is responsible for ensuring that the type is correct
20582058
Ok(unsafe { self.downcast_into_unchecked() })
20592059
} else {
2060-
Err(PyDowncastError::new(self.clone().into_gil_ref(), T::NAME))
2060+
Err(PyDowncastIntoError::new(self, T::NAME))
20612061
}
20622062
}
20632063

20642064
#[inline]
2065-
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>>
2065+
fn downcast_exact<T>(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>>
20662066
where
20672067
T: PyTypeInfo,
20682068
{
20692069
if self.is_exact_instance_of::<T>() {
20702070
// Safety: is_exact_instance_of is responsible for ensuring that the type is correct
20712071
Ok(unsafe { self.downcast_unchecked() })
20722072
} else {
2073-
Err(PyDowncastError::new(self.clone().into_gil_ref(), T::NAME))
2073+
Err(PyDowncastError2::new(self, T::NAME))
20742074
}
20752075
}
20762076

20772077
#[inline]
2078-
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastError<'py>>
2078+
fn downcast_into_exact<T>(self) -> Result<Py2<'py, T>, PyDowncastIntoError<'py>>
20792079
where
20802080
T: PyTypeInfo,
20812081
{
20822082
if self.is_exact_instance_of::<T>() {
20832083
// Safety: is_exact_instance_of is responsible for ensuring that the type is correct
20842084
Ok(unsafe { self.downcast_into_unchecked() })
20852085
} else {
2086-
Err(PyDowncastError::new(self.into_gil_ref(), T::NAME))
2086+
Err(PyDowncastIntoError::new(self, T::NAME))
20872087
}
20882088
}
20892089

tests/ui/invalid_result_conversion.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied
99
<PyErr as From<PyBorrowMutError>>
1010
<PyErr as From<std::io::Error>>
1111
<PyErr as From<PyDowncastError<'a>>>
12+
<PyErr as From<pyo3::err::PyDowncastError2<'_, '_>>>
13+
<PyErr as From<pyo3::err::PyDowncastIntoError<'_>>>
1214
<PyErr as From<NulError>>
1315
<PyErr as From<IntoStringError>>
14-
<PyErr as From<FromUtf8Error>>
15-
<PyErr as From<FromUtf16Error>>
1616
and $N others
1717
= note: required for `MyError` to implement `Into<PyErr>`
1818
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)