pyo3 icon indicating copy to clipboard operation
pyo3 copied to clipboard

Better handling of `anyhow::Error` and `eyre::Report`

Open BlueGlassBlock opened this issue 2 years ago • 9 comments

Both anyhow::Error and eyre::Report are for dynamic error handling, I think it's feasible to make some improvements:

  • Unwrap inner error, if it's containing a PyErr (However extra information should be combined to?)
  • Extract the error chain into an exception chain (since both libraries have Error::chain/Report::chain)

I'm willing to contribute :)

BlueGlassBlock avatar Mar 02 '23 06:03 BlueGlassBlock

I think it would be best to propose concrete separate PR for these changes to discuss them based on a concrete implementation instead of in the abstract.

adamreichold avatar Mar 02 '23 06:03 adamreichold

As far as I understand anyhow already prints its error in a similar manner to how nested exceptions are printed. What difference would this make and what things would it let you do that you cannot do right now?

mejrs avatar Mar 02 '23 09:03 mejrs

As far as I understand anyhow already prints its error in a similar manner to how nested exceptions are printed. What difference would this make and what things would it let you do that you cannot do right now?

When using anyhow::Result as function return type, RuntimeError is always raised when an error occurred. I have some Python callbacks to call within the Rust function, whose exceptions will also be converted to RuntimeError (PyErr -> anyhow::Error -> PyErr)

BlueGlassBlock avatar Mar 02 '23 09:03 BlueGlassBlock

I'm not entirely sure what you want - do you want to be able to return a anyhow::Error to python, have it converted to a RuntimeError and then be able to get the original error back? If so that'll be hard because right now we just convert it to a string and create a RuntimeError with that.

mejrs avatar Mar 02 '23 10:03 mejrs

I'm not entirely sure what you want - do you want to be able to return a anyhow::Error to python, have it converted to a RuntimeError and then be able to get the original error back? If so that'll be hard because right now we just convert it to a string and create a RuntimeError with that.

I prefer letting the original error bubble up, other errors get converted to RuntimeError. (Error::downcast(PyErr))

BlueGlassBlock avatar Mar 02 '23 10:03 BlueGlassBlock

I don't understand you at all. Can you maybe give an example?

mejrs avatar Mar 02 '23 13:03 mejrs

If you have an instance of anyhow::Error that was created from a PyErr via impl From<E> for anyhow::Error where E: std::error::Error, then instead of our current approach

impl From<anyhow::Error> for PyErr {
    fn from(err: anyhow::Error) -> Self {
        PyRuntimeError::new_err(format!("{:?}", err))
    }
}

do something like

impl From<anyhow::Error> for PyErr {
    fn from(err: anyhow::Error) -> Self {
        if let Ok(err) = err.downcast::<Self>() {
            return err;
        }

        PyRuntimeError::new_err(format!("{:?}", err))
    }
}

adamreichold avatar Mar 02 '23 14:03 adamreichold

impl From<anyhow::Error> for PyErr {
    fn from(error: anyhow::Error) -> Self {
        match error.downcast::<Self>() {
            Ok(py_err) => py_err,
            Err(error) => PyRuntimeError::new_err(format!("{:?}", error))
        }
    }
}

impl From<eyre::Report> for PyErr {
    fn from(error: Report) -> Self {
        match error.downcast::<Self>() {
            Ok(py_err) => py_err,
            Err(report) => PyRuntimeError::new_err(format!("{:?}", report))
        }
    }
}

This one should do the work.

However I think it's too naive:

  • anyhow::Error & eyre::Report both support "error chain", which should be presented in the result exception.
  • anyhow::Error has an additional context, which I'm thinking about how to present that. (BaseException.__notes__ seems relevant, however it's added in Python 3.11.)

BlueGlassBlock avatar Mar 03 '23 14:03 BlueGlassBlock

However I think it's too naive:

Probably, but getting those into the main branch would be an improvement already. Why not open a PR?

adamreichold avatar Mar 03 '23 15:03 adamreichold