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