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