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.0-dev", 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(())
}