1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::{err::PyErrArguments, exceptions, IntoPy, PyErr, PyObject, Python};
use std::io;

/// Convert `PyErr` to `io::Error`
impl std::convert::From<PyErr> for io::Error {
    fn from(err: PyErr) -> Self {
        io::Error::new(io::ErrorKind::Other, format!("Python exception: {}", err))
    }
}

/// Create `OSError` from `io::Error`
impl std::convert::From<io::Error> for PyErr {
    fn from(err: io::Error) -> PyErr {
        match err.kind() {
            io::ErrorKind::BrokenPipe => exceptions::PyBrokenPipeError::new_err(err),
            io::ErrorKind::ConnectionRefused => exceptions::PyConnectionRefusedError::new_err(err),
            io::ErrorKind::ConnectionAborted => exceptions::PyConnectionAbortedError::new_err(err),
            io::ErrorKind::ConnectionReset => exceptions::PyConnectionResetError::new_err(err),
            io::ErrorKind::Interrupted => exceptions::PyInterruptedError::new_err(err),
            io::ErrorKind::NotFound => exceptions::PyFileNotFoundError::new_err(err),
            io::ErrorKind::WouldBlock => exceptions::PyBlockingIOError::new_err(err),
            io::ErrorKind::TimedOut => exceptions::PyTimeoutError::new_err(err),
            _ => exceptions::PyOSError::new_err(err),
        }
    }
}

impl PyErrArguments for io::Error {
    fn arguments(self, py: Python) -> PyObject {
        self.to_string().into_py(py)
    }
}

impl<W: 'static + Send + Sync + std::fmt::Debug> std::convert::From<std::io::IntoInnerError<W>>
    for PyErr
{
    fn from(err: std::io::IntoInnerError<W>) -> PyErr {
        exceptions::PyOSError::new_err(err)
    }
}

impl<W: Send + Sync + std::fmt::Debug> PyErrArguments for std::io::IntoInnerError<W> {
    fn arguments(self, py: Python) -> PyObject {
        self.to_string().into_py(py)
    }
}

impl std::convert::From<std::convert::Infallible> for PyErr {
    fn from(_: std::convert::Infallible) -> PyErr {
        unreachable!()
    }
}

macro_rules! impl_to_pyerr {
    ($err: ty, $pyexc: ty) => {
        impl PyErrArguments for $err {
            fn arguments(self, py: Python) -> PyObject {
                self.to_string().into_py(py)
            }
        }

        impl std::convert::From<$err> for PyErr {
            fn from(err: $err) -> PyErr {
                <$pyexc>::new_err(err)
            }
        }
    };
}

impl_to_pyerr!(std::array::TryFromSliceError, exceptions::PyValueError);
impl_to_pyerr!(std::num::ParseIntError, exceptions::PyValueError);
impl_to_pyerr!(std::num::ParseFloatError, exceptions::PyValueError);
impl_to_pyerr!(std::num::TryFromIntError, exceptions::PyValueError);
impl_to_pyerr!(std::str::ParseBoolError, exceptions::PyValueError);
impl_to_pyerr!(std::ffi::IntoStringError, exceptions::PyUnicodeDecodeError);
impl_to_pyerr!(std::ffi::NulError, exceptions::PyValueError);
impl_to_pyerr!(std::str::Utf8Error, exceptions::PyUnicodeDecodeError);
impl_to_pyerr!(std::string::FromUtf8Error, exceptions::PyUnicodeDecodeError);
impl_to_pyerr!(
    std::string::FromUtf16Error,
    exceptions::PyUnicodeDecodeError
);
impl_to_pyerr!(
    std::char::DecodeUtf16Error,
    exceptions::PyUnicodeDecodeError
);
impl_to_pyerr!(std::net::AddrParseError, exceptions::PyValueError);

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

    #[test]
    fn io_errors() {
        let check_err = |kind, expected_ty| {
            let py_err: PyErr = io::Error::new(kind, "some error msg").into();
            let err_msg = format!("{}: some error msg", expected_ty);
            assert_eq!(py_err.to_string(), err_msg);

            let os_err: io::Error = py_err.into();
            assert_eq!(os_err.to_string(), format!("Python exception: {}", err_msg));
        };

        check_err(io::ErrorKind::BrokenPipe, "BrokenPipeError");
        check_err(io::ErrorKind::ConnectionRefused, "ConnectionRefusedError");
        check_err(io::ErrorKind::ConnectionAborted, "ConnectionAbortedError");
        check_err(io::ErrorKind::ConnectionReset, "ConnectionResetError");
        check_err(io::ErrorKind::Interrupted, "InterruptedError");
        check_err(io::ErrorKind::NotFound, "FileNotFoundError");
        check_err(io::ErrorKind::WouldBlock, "BlockingIOError");
        check_err(io::ErrorKind::TimedOut, "TimeoutError");
    }
}