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