pyo3/err/
downcast_error.rs

1#![allow(deprecated)]
2
3use std::borrow::Cow;
4
5use crate::{
6    exceptions,
7    types::{PyAnyMethods, PyStringMethods, PyType, PyTypeMethods},
8    Borrowed, Bound, IntoPyObject, Py, PyAny, PyErr, PyErrArguments, Python,
9};
10
11/// Error that indicates a failure to convert a PyAny to a more specific Python type.
12#[derive(Debug)]
13#[deprecated(since = "0.27.0", note = "replaced with `CastError`")]
14pub struct DowncastError<'a, 'py> {
15    from: Borrowed<'a, 'py, PyAny>,
16    to: Cow<'static, str>,
17}
18
19impl<'a, 'py> DowncastError<'a, 'py> {
20    /// Create a new `DowncastError` representing a failure to convert the object
21    /// `from` into the type named in `to`.
22    pub fn new(from: &'a Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
23        Self {
24            from: from.as_borrowed(),
25            to: to.into(),
26        }
27    }
28}
29
30/// Error that indicates a failure to convert a PyAny to a more specific Python type.
31#[derive(Debug)]
32#[deprecated(since = "0.27.0", note = "replaced with `CastIntoError`")]
33pub struct DowncastIntoError<'py> {
34    from: Bound<'py, PyAny>,
35    to: Cow<'static, str>,
36}
37
38impl<'py> DowncastIntoError<'py> {
39    /// Create a new `DowncastIntoError` representing a failure to convert the object
40    /// `from` into the type named in `to`.
41    pub fn new(from: Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
42        Self {
43            from,
44            to: to.into(),
45        }
46    }
47
48    /// Consumes this `DowncastIntoError` and returns the original object, allowing continued
49    /// use of it after a failed conversion.
50    ///
51    /// See [`cast_into`][Bound::cast_into] for an example.
52    pub fn into_inner(self) -> Bound<'py, PyAny> {
53        self.from
54    }
55}
56
57struct DowncastErrorArguments {
58    from: Py<PyType>,
59    to: Cow<'static, str>,
60}
61
62impl PyErrArguments for DowncastErrorArguments {
63    fn arguments(self, py: Python<'_>) -> Py<PyAny> {
64        let from = self.from.bind(py).qualname();
65        let from = from
66            .as_ref()
67            .map(|name| name.to_string_lossy())
68            .unwrap_or(Cow::Borrowed("<failed to extract type name>"));
69        format!("'{}' object cannot be converted to '{}'", from, self.to)
70            .into_pyobject(py)
71            .unwrap()
72            .into_any()
73            .unbind()
74    }
75}
76
77/// Convert `CastError` to Python `TypeError`.
78impl std::convert::From<DowncastError<'_, '_>> for PyErr {
79    fn from(err: DowncastError<'_, '_>) -> PyErr {
80        let args = DowncastErrorArguments {
81            from: err.from.get_type().into(),
82            to: err.to,
83        };
84
85        exceptions::PyTypeError::new_err(args)
86    }
87}
88
89impl std::error::Error for DowncastError<'_, '_> {}
90
91impl std::fmt::Display for DowncastError<'_, '_> {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
93        display_downcast_error(f, &self.from, &self.to)
94    }
95}
96
97/// Convert `DowncastIntoError` to Python `TypeError`.
98impl std::convert::From<DowncastIntoError<'_>> for PyErr {
99    fn from(err: DowncastIntoError<'_>) -> PyErr {
100        let args = DowncastErrorArguments {
101            from: err.from.get_type().into(),
102            to: err.to,
103        };
104
105        exceptions::PyTypeError::new_err(args)
106    }
107}
108
109impl std::error::Error for DowncastIntoError<'_> {}
110
111impl std::fmt::Display for DowncastIntoError<'_> {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
113        display_downcast_error(f, &self.from, &self.to)
114    }
115}
116
117fn display_downcast_error(
118    f: &mut std::fmt::Formatter<'_>,
119    from: &Bound<'_, PyAny>,
120    to: &str,
121) -> std::fmt::Result {
122    write!(
123        f,
124        "'{}' object cannot be converted to '{}'",
125        from.get_type().qualname().map_err(|_| std::fmt::Error)?,
126        to
127    )
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_display_downcast_error() {
136        Python::attach(|py| {
137            let obj = py.None().into_bound(py);
138            let err = DowncastError::new(&obj, "int");
139            assert_eq!(
140                err.to_string(),
141                "'NoneType' object cannot be converted to 'int'"
142            );
143        })
144    }
145
146    #[test]
147    fn test_display_downcast_into_error() {
148        Python::attach(|py| {
149            let obj = py.None().into_bound(py);
150            let err = DowncastIntoError::new(obj, "int");
151            assert_eq!(
152                err.to_string(),
153                "'NoneType' object cannot be converted to 'int'"
154            );
155        })
156    }
157
158    #[test]
159    fn test_pyerr_from_downcast_error() {
160        Python::attach(|py| {
161            let obj = py.None().into_bound(py);
162            let err = DowncastError::new(&obj, "int");
163            let py_err: PyErr = err.into();
164            assert_eq!(
165                py_err.to_string(),
166                "TypeError: 'NoneType' object cannot be converted to 'int'"
167            );
168        })
169    }
170
171    #[test]
172    fn test_pyerr_from_downcast_into_error() {
173        Python::attach(|py| {
174            let obj = py.None().into_bound(py);
175            let err = DowncastIntoError::new(obj, "int");
176            let py_err: PyErr = err.into();
177            assert_eq!(
178                py_err.to_string(),
179                "TypeError: 'NoneType' object cannot be converted to 'int'"
180            );
181        })
182    }
183}