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