pyo3/err/
impls.rs

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