pyo3

Module anyhow

Source
Expand description

A conversion from anyhow’s Error type to PyErr.

Use of an error handling library like anyhow is common in application code and when you just want error handling to be easy. If you are writing a library or you need more control over your errors you might want to design your own error type instead.

When the inner error is a PyErr without source, it will be extracted out. Otherwise a Python RuntimeError will be created. You might find that you need to map the error from your Rust code into another Python exception. See PyErr::new for more information about that.

For information about error handling in general, see the Error handling chapter of the Rust book.

§Setup

To use this feature, add this to your Cargo.toml:

[dependencies]
## change * to the version you want to use, ideally the latest.
anyhow = "*"
pyo3 = { version = "0.23.2", features = ["anyhow"] }

Note that you must use compatible versions of anyhow and PyO3. The required anyhow version may vary based on the version of PyO3.

§Example: Propagating a PyErr into anyhow::Error

use pyo3::prelude::*;
use std::path::PathBuf;

// A wrapper around a Rust function.
// The pyfunction macro performs the conversion to a PyErr
#[pyfunction]
fn py_open(filename: PathBuf) -> anyhow::Result<Vec<u8>> {
    let data = std::fs::read(filename)?;
    Ok(data)
}

fn main() {
    let error = Python::with_gil(|py| -> PyResult<Vec<u8>> {
        let fun = wrap_pyfunction!(py_open, py)?;
        let text = fun.call1(("foo.txt",))?.extract::<Vec<u8>>()?;
        Ok(text)
    }).unwrap_err();

    println!("{}", error);
}

§Example: Using anyhow in general

Note that you don’t need this feature to convert a PyErr into an anyhow::Error, because it can already convert anything that implements Error:

use pyo3::prelude::*;
use pyo3::types::PyBytes;

// An example function that must handle multiple error types.
//
// To do this you usually need to design your own error type or use
// `Box<dyn Error>`. `anyhow` is a convenient alternative for this.
pub fn decompress(bytes: &[u8]) -> anyhow::Result<String> {
    // An arbitrary example of a Python api you
    // could call inside an application...
    // This might return a `PyErr`.
    let res = Python::with_gil(|py| {
        let zlib = PyModule::import(py, "zlib")?;
        let decompress = zlib.getattr("decompress")?;
        let bytes = PyBytes::new(py, bytes);
        let value = decompress.call1((bytes,))?;
        value.extract::<Vec<u8>>()
    })?;

    // This might be a `FromUtf8Error`.
    let text = String::from_utf8(res)?;

    Ok(text)
}

fn main() -> anyhow::Result<()> {
    let bytes: &[u8] = b"x\x9c\x8b\xcc/U(\xce\xc8/\xcdIQ((\xcaOJL\xca\xa9T\
                        (-NU(\xc9HU\xc8\xc9LJ\xcbI,IUH.\x02\x91\x99y\xc5%\
                        \xa9\x89)z\x00\xf2\x15\x12\xfe";
    let text = decompress(bytes)?;

    println!("The text is \"{}\"", text);
    Ok(())
}