From 1451418ee4154c76bb52da344712ca6ec605323d Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 19 Dec 2023 20:06:59 +0000 Subject: [PATCH] Add `Py2` variants of `PyDowncastError` --- src/err/mod.rs | 101 ++++++++++++++++++++-- src/types/any.rs | 26 +++--- tests/ui/invalid_result_conversion.stderr | 4 +- 3 files changed, 107 insertions(+), 24 deletions(-) diff --git a/src/err/mod.rs b/src/err/mod.rs index 88f930cfee4..249225ac39c 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -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>) -> 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>) -> 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> 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> 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(); diff --git a/src/types/any.rs b/src/types/any.rs index f6c5ef1414b..cd8a1e13a7c 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -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(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>> + fn downcast(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>> where T: PyTypeCheck; /// Like `downcast` but takes ownership of `self`. - fn downcast_into(self) -> Result, PyDowncastError<'py>> + fn downcast_into(self) -> Result, PyDowncastIntoError<'py>> where T: PyTypeCheck; @@ -1602,12 +1602,12 @@ pub(crate) trait PyAnyMethods<'py> { /// assert!(any.downcast_exact::().is_ok()); /// }); /// ``` - fn downcast_exact(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>> + fn downcast_exact(&self) -> Result<&Py2<'py, T>, PyDowncastError2<'_, 'py>> where T: PyTypeInfo; /// Like `downcast_exact` but takes ownership of `self`. - fn downcast_into_exact(self) -> Result, PyDowncastError<'py>> + fn downcast_into_exact(self) -> Result, PyDowncastIntoError<'py>> where T: PyTypeInfo; @@ -2036,7 +2036,7 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> { } #[inline] - fn downcast(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>> + fn downcast(&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(self) -> Result, PyDowncastError<'py>> + fn downcast_into(self) -> Result, 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(&self) -> Result<&Py2<'py, T>, PyDowncastError<'py>> + fn downcast_exact(&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(self) -> Result, PyDowncastError<'py>> + fn downcast_into_exact(self) -> Result, 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)) } } diff --git a/tests/ui/invalid_result_conversion.stderr b/tests/ui/invalid_result_conversion.stderr index 47d945832c0..834585ef3f1 100644 --- a/tests/ui/invalid_result_conversion.stderr +++ b/tests/ui/invalid_result_conversion.stderr @@ -9,10 +9,10 @@ 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)