1use crate::IntoPyObject;
2use crate::{err::PyErrArguments, exceptions, PyErr, PyObject, Python};
3use std::io;
4
5impl 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
46impl From<io::Error> for PyErr {
50 fn from(err: io::Error) -> PyErr {
51 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 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 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))); })
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}