pyo3/err/
impls.rs

1use crate::{err::PyErrArguments, exceptions, IntoPy, PyErr, PyObject, Python};
2use std::io;
3
4/// Convert `PyErr` to `io::Error`
5impl From<PyErr> for io::Error {
6    fn from(err: PyErr) -> Self {
7        let kind = Python::with_gil(|py| {
8            if err.is_instance_of::<exceptions::PyBrokenPipeError>(py) {
9                io::ErrorKind::BrokenPipe
10            } else if err.is_instance_of::<exceptions::PyConnectionRefusedError>(py) {
11                io::ErrorKind::ConnectionRefused
12            } else if err.is_instance_of::<exceptions::PyConnectionAbortedError>(py) {
13                io::ErrorKind::ConnectionAborted
14            } else if err.is_instance_of::<exceptions::PyConnectionResetError>(py) {
15                io::ErrorKind::ConnectionReset
16            } else if err.is_instance_of::<exceptions::PyInterruptedError>(py) {
17                io::ErrorKind::Interrupted
18            } else if err.is_instance_of::<exceptions::PyFileNotFoundError>(py) {
19                io::ErrorKind::NotFound
20            } else if err.is_instance_of::<exceptions::PyPermissionError>(py) {
21                io::ErrorKind::PermissionDenied
22            } else if err.is_instance_of::<exceptions::PyFileExistsError>(py) {
23                io::ErrorKind::AlreadyExists
24            } else if err.is_instance_of::<exceptions::PyBlockingIOError>(py) {
25                io::ErrorKind::WouldBlock
26            } else if err.is_instance_of::<exceptions::PyTimeoutError>(py) {
27                io::ErrorKind::TimedOut
28            } else {
29                io::ErrorKind::Other
30            }
31        });
32        io::Error::new(kind, err)
33    }
34}
35
36/// Create `PyErr` from `io::Error`
37/// (`OSError` except if the `io::Error` is wrapping a Python exception,
38/// in this case the exception is returned)
39impl From<io::Error> for PyErr {
40    fn from(err: io::Error) -> PyErr {
41        // If the error wraps a Python error we return it
42        if err.get_ref().map_or(false, |e| e.is::<PyErr>()) {
43            return *err.into_inner().unwrap().downcast().unwrap();
44        }
45        match err.kind() {
46            io::ErrorKind::BrokenPipe => exceptions::PyBrokenPipeError::new_err(err),
47            io::ErrorKind::ConnectionRefused => exceptions::PyConnectionRefusedError::new_err(err),
48            io::ErrorKind::ConnectionAborted => exceptions::PyConnectionAbortedError::new_err(err),
49            io::ErrorKind::ConnectionReset => exceptions::PyConnectionResetError::new_err(err),
50            io::ErrorKind::Interrupted => exceptions::PyInterruptedError::new_err(err),
51            io::ErrorKind::NotFound => exceptions::PyFileNotFoundError::new_err(err),
52            io::ErrorKind::PermissionDenied => exceptions::PyPermissionError::new_err(err),
53            io::ErrorKind::AlreadyExists => exceptions::PyFileExistsError::new_err(err),
54            io::ErrorKind::WouldBlock => exceptions::PyBlockingIOError::new_err(err),
55            io::ErrorKind::TimedOut => exceptions::PyTimeoutError::new_err(err),
56            _ => exceptions::PyOSError::new_err(err),
57        }
58    }
59}
60
61impl PyErrArguments for io::Error {
62    fn arguments(self, py: Python<'_>) -> PyObject {
63        self.to_string().into_py(py)
64    }
65}
66
67impl<W> From<io::IntoInnerError<W>> for PyErr {
68    fn from(err: io::IntoInnerError<W>) -> PyErr {
69        err.into_error().into()
70    }
71}
72
73impl<W: Send + Sync> PyErrArguments for io::IntoInnerError<W> {
74    fn arguments(self, py: Python<'_>) -> PyObject {
75        self.into_error().arguments(py)
76    }
77}
78
79impl From<std::convert::Infallible> for PyErr {
80    fn from(_: std::convert::Infallible) -> PyErr {
81        unreachable!()
82    }
83}
84
85macro_rules! impl_to_pyerr {
86    ($err: ty, $pyexc: ty) => {
87        impl PyErrArguments for $err {
88            fn arguments(self, py: Python<'_>) -> PyObject {
89                self.to_string().into_py(py)
90            }
91        }
92
93        impl std::convert::From<$err> for PyErr {
94            fn from(err: $err) -> PyErr {
95                <$pyexc>::new_err(err)
96            }
97        }
98    };
99}
100
101impl_to_pyerr!(std::array::TryFromSliceError, exceptions::PyValueError);
102impl_to_pyerr!(std::num::ParseIntError, exceptions::PyValueError);
103impl_to_pyerr!(std::num::ParseFloatError, exceptions::PyValueError);
104impl_to_pyerr!(std::num::TryFromIntError, exceptions::PyValueError);
105impl_to_pyerr!(std::str::ParseBoolError, exceptions::PyValueError);
106impl_to_pyerr!(std::ffi::IntoStringError, exceptions::PyUnicodeDecodeError);
107impl_to_pyerr!(std::ffi::NulError, exceptions::PyValueError);
108impl_to_pyerr!(std::str::Utf8Error, exceptions::PyUnicodeDecodeError);
109impl_to_pyerr!(std::string::FromUtf8Error, exceptions::PyUnicodeDecodeError);
110impl_to_pyerr!(
111    std::string::FromUtf16Error,
112    exceptions::PyUnicodeDecodeError
113);
114impl_to_pyerr!(
115    std::char::DecodeUtf16Error,
116    exceptions::PyUnicodeDecodeError
117);
118impl_to_pyerr!(std::net::AddrParseError, exceptions::PyValueError);
119
120#[cfg(test)]
121mod tests {
122    use crate::{PyErr, Python};
123    use std::io;
124
125    #[test]
126    fn io_errors() {
127        use crate::types::any::PyAnyMethods;
128
129        let check_err = |kind, expected_ty| {
130            Python::with_gil(|py| {
131                let rust_err = io::Error::new(kind, "some error msg");
132
133                let py_err: PyErr = rust_err.into();
134                let py_err_msg = format!("{}: some error msg", expected_ty);
135                assert_eq!(py_err.to_string(), py_err_msg);
136                let py_error_clone = py_err.clone_ref(py);
137
138                let rust_err_from_py_err: io::Error = py_err.into();
139                assert_eq!(rust_err_from_py_err.to_string(), py_err_msg);
140                assert_eq!(rust_err_from_py_err.kind(), kind);
141
142                let py_err_recovered_from_rust_err: PyErr = rust_err_from_py_err.into();
143                assert!(py_err_recovered_from_rust_err
144                    .value_bound(py)
145                    .is(py_error_clone.value_bound(py))); // It should be the same exception
146            })
147        };
148
149        check_err(io::ErrorKind::BrokenPipe, "BrokenPipeError");
150        check_err(io::ErrorKind::ConnectionRefused, "ConnectionRefusedError");
151        check_err(io::ErrorKind::ConnectionAborted, "ConnectionAbortedError");
152        check_err(io::ErrorKind::ConnectionReset, "ConnectionResetError");
153        check_err(io::ErrorKind::Interrupted, "InterruptedError");
154        check_err(io::ErrorKind::NotFound, "FileNotFoundError");
155        check_err(io::ErrorKind::PermissionDenied, "PermissionError");
156        check_err(io::ErrorKind::AlreadyExists, "FileExistsError");
157        check_err(io::ErrorKind::WouldBlock, "BlockingIOError");
158        check_err(io::ErrorKind::TimedOut, "TimeoutError");
159    }
160}