Skip to content

Commit

Permalink
Unwrap the underlying PyErr when converting an io::Error back to a PyErr
Browse files Browse the repository at this point in the history
Exposes it directly instead of loosing all information outside the message
  • Loading branch information
Tpt committed Aug 18, 2023
1 parent 7f32ed9 commit 9f02910
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 13 deletions.
1 change: 1 addition & 0 deletions newsfragments/3401.changed.md
@@ -0,0 +1 @@
`std::io::Error` cast: Reuses the underlying `PyErr` Python error instead of wrapping it again if the `io::Error` has been built using a Python exception
37 changes: 24 additions & 13 deletions src/err/impls.rs
Expand Up @@ -29,13 +29,19 @@ impl From<PyErr> for io::Error {
io::ErrorKind::Other
}
});
io::Error::new(kind, format!("Python exception: {}", err))
io::Error::new(kind, err)
}
}

/// Create `OSError` from `io::Error`
/// Create `PyErr` from `io::Error`
/// (`OSError` except if the `io::Error` is wrapping a Python exception,
/// in this case the exception is returned)
impl From<io::Error> for PyErr {
fn from(err: io::Error) -> PyErr {
// If the error wraps a Python error we return it
if err.get_ref().map_or(false, |e| e.is::<PyErr>()) {
return *err.into_inner().unwrap().downcast().unwrap();
}
match err.kind() {
io::ErrorKind::BrokenPipe => exceptions::PyBrokenPipeError::new_err(err),
io::ErrorKind::ConnectionRefused => exceptions::PyConnectionRefusedError::new_err(err),
Expand Down Expand Up @@ -113,24 +119,29 @@ impl_to_pyerr!(std::net::AddrParseError, exceptions::PyValueError);

#[cfg(test)]
mod tests {
use crate::PyErr;
use crate::{PyErr, Python};
use std::io;

#[test]
fn io_errors() {
let check_err = |kind, expected_ty| {
let rust_err = io::Error::new(kind, "some error msg");
Python::with_gil(|py| {
let rust_err = io::Error::new(kind, "some error msg");

let py_err: PyErr = rust_err.into();
let py_err_msg = format!("{}: some error msg", expected_ty);
assert_eq!(py_err.to_string(), py_err_msg);
let py_error_clone = py_err.clone_ref(py);

let py_err: PyErr = rust_err.into();
let py_err_msg = format!("{}: some error msg", expected_ty);
assert_eq!(py_err.to_string(), py_err_msg);
let rust_err_from_py_err: io::Error = py_err.into();
assert_eq!(rust_err_from_py_err.to_string(), py_err_msg);
assert_eq!(rust_err_from_py_err.kind(), kind);

let rust_err_from_py_err: io::Error = py_err.into();
assert_eq!(
rust_err_from_py_err.to_string(),
format!("Python exception: {}", py_err_msg)
);
assert_eq!(rust_err_from_py_err.kind(), kind);
let py_err_recovered_from_rust_err: PyErr = rust_err_from_py_err.into();
assert!(py_err_recovered_from_rust_err
.value(py)
.is(py_error_clone.value(py))); // It should be the same exception
})
};

check_err(io::ErrorKind::BrokenPipe, "BrokenPipeError");
Expand Down

0 comments on commit 9f02910

Please sign in to comment.