mirror of
https://github.com/facebook/sapling.git
synced 2024-12-24 05:21:49 +03:00
taggederror: Introduce taggederror-util for more ergonomic error tagging for eden error types.
Summary: Introduce taggederror-util, which provides a new trait `AnyhowEdenExt`, which provides a method `eden_metadata` for anyhow errors and results. This method works much like `AnyhowExt::common_metadata`, but additionally supports extracting default error metadata from known `Tagged` types which are listed explicitly in the method implementation. Extend `FilteredAnyhow` to support a configuration "metadata function", which allows swapping out `eden_metadata` for the standard `common_metadata`. Modify Rust dispatch and Python bindings to use `AnyhowEdenExt` for metadata extraction and printing. Modify `intentional_error` to rely on `AnyhowEdenExt` for tagging (removes `.tagged` call, no tags will be visible if `AnyhowEdenExt` is not used). Reviewed By: DurhamG Differential Revision: D22927203 fbshipit-source-id: 04b36fdfaa24af591118acb9e418d1ed7ae33f91
This commit is contained in:
parent
6eab61a790
commit
b9f3c9c692
@ -12,4 +12,5 @@ metalog = { path = "../../../../lib/metalog" }
|
||||
revisionstore = { path = "../../../../lib/revisionstore" }
|
||||
revlogindex = { path = "../../../../lib/revlogindex" }
|
||||
taggederror = { path = "../../../../lib/taggederror" }
|
||||
taggederror-util = { path = "../../../../lib/taggederror-util" }
|
||||
treestate = { path = "../../../../lib/treestate" }
|
||||
|
@ -9,6 +9,7 @@ use cpython::*;
|
||||
use cpython_ext::{error, ResultPyErrExt};
|
||||
|
||||
use taggederror::{intentional_bail, intentional_error, CommonMetadata, Fault, FilteredAnyhow};
|
||||
use taggederror_util::AnyhowEdenExt;
|
||||
|
||||
py_exception!(error, IndexedLogError);
|
||||
py_exception!(error, MetaLogError);
|
||||
@ -131,9 +132,16 @@ fn register_error_handlers() {
|
||||
}
|
||||
|
||||
fn fallback_error_handler(py: Python, e: &error::Error, m: CommonMetadata) -> Option<PyErr> {
|
||||
TaggedExceptionData::create_instance(py, m, format!("{:?}", FilteredAnyhow::new(e)))
|
||||
.map(|data| PyErr::new::<RustError, _>(py, data))
|
||||
.ok()
|
||||
TaggedExceptionData::create_instance(
|
||||
py,
|
||||
m,
|
||||
format!(
|
||||
"{:?}",
|
||||
FilteredAnyhow::new(e).with_metadata_func(|e| e.eden_metadata())
|
||||
),
|
||||
)
|
||||
.map(|data| PyErr::new::<RustError, _>(py, data))
|
||||
.ok()
|
||||
}
|
||||
|
||||
error::register("010-specific", specific_error_handler);
|
||||
@ -141,7 +149,7 @@ fn register_error_handlers() {
|
||||
}
|
||||
|
||||
fn py_intentional_error(py: Python) -> PyResult<PyInt> {
|
||||
Ok(intentional_error()
|
||||
Ok(intentional_error(false)
|
||||
.map(|r| r.to_py_object(py))
|
||||
.map_pyerr(py)?)
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ indexedlog = { path = "../indexedlog" }
|
||||
pipe = "0.2"
|
||||
streampager = "0.8"
|
||||
taggederror = { path = "../taggederror" }
|
||||
taggederror-util = { path = "../taggederror-util" }
|
||||
thiserror = "1.0.5"
|
||||
thrift-types = { path = "../thrift-types" }
|
||||
tracing = "0.1"
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
use std::borrow::Cow;
|
||||
use taggederror::FilteredAnyhow;
|
||||
use taggederror_util::AnyhowEdenExt;
|
||||
use thiserror::Error;
|
||||
use thrift_types::edenfs as eden;
|
||||
|
||||
@ -73,10 +74,15 @@ pub fn print_error(err: &anyhow::Error, io: &mut crate::io::IO, args: &[String])
|
||||
if args.iter().any(|v| v == "--traceback") {
|
||||
let _ = io.write_err(format!(
|
||||
"abort: {:?}\n",
|
||||
FilteredAnyhow::new(err).separate_tags()
|
||||
FilteredAnyhow::new(err)
|
||||
.with_metadata_func(|e| e.eden_metadata())
|
||||
.separate_tags()
|
||||
));
|
||||
} else {
|
||||
let _ = io.write_err(format!("abort: {}\n", FilteredAnyhow::new(err)));
|
||||
let _ = io.write_err(format!(
|
||||
"abort: {}\n",
|
||||
FilteredAnyhow::new(err).with_metadata_func(|e| e.eden_metadata())
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,6 @@ python27-sys = { version = "0.5", optional = true }
|
||||
python3-sys = { version = "0.5", optional = true }
|
||||
serde = "1"
|
||||
taggederror = { path = "../taggederror" }
|
||||
taggederror-util = { path = "../taggederror-util" }
|
||||
thiserror = "1.0.5"
|
||||
types = { path = "../types" }
|
||||
|
@ -14,7 +14,8 @@ pub use anyhow::{Error, Result};
|
||||
use cpython::{exc, FromPyObject, ObjectProtocol, PyClone, PyList, PyModule, PyResult, Python};
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use taggederror::{AnyhowExt, CommonMetadata, TaggedError};
|
||||
use taggederror::{CommonMetadata, TaggedError};
|
||||
use taggederror_util::AnyhowEdenExt;
|
||||
|
||||
/// Extends the `Result` type to allow conversion to `PyResult` from a native
|
||||
/// Rust result.
|
||||
@ -96,7 +97,7 @@ impl<T, E: Into<Error>> ResultPyErrExt<T> for Result<T, E> {
|
||||
self.map_err(|e| {
|
||||
let e: anyhow::Error = e.into();
|
||||
let mut e = &e;
|
||||
let metadata = e.common_metadata();
|
||||
let metadata = e.eden_metadata();
|
||||
loop {
|
||||
if let Some(e) = e.downcast_ref::<PyErr>() {
|
||||
return e.inner.clone_ref(py);
|
||||
|
@ -347,5 +347,5 @@ pub fn debugdynamicconfig(opts: DebugDynamicConfigOpts, _io: &mut IO, repo: Repo
|
||||
|
||||
pub fn debugcauserusterror(_opts: NoOpts, _io: &mut IO, _repo: Repo) -> Result<u8> {
|
||||
// Add additional metadata via AnyhowExt trait to an anyhow::Error or anyhow::Result
|
||||
Ok(intentional_error().with_fault(Fault::Request)?)
|
||||
Ok(intentional_error(false).with_fault(Fault::Request)?)
|
||||
}
|
||||
|
9
eden/scm/lib/taggederror-util/Cargo.toml
Normal file
9
eden/scm/lib/taggederror-util/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "taggederror-util"
|
||||
version = "0.0.1"
|
||||
authors = ["Facebook Source Control Team <sourcecontrol-dev@fb.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.20"
|
||||
taggederror = { path = "../taggederror" }
|
47
eden/scm/lib/taggederror-util/src/lib.rs
Normal file
47
eden/scm/lib/taggederror-util/src/lib.rs
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This software may be used and distributed according to the terms of the
|
||||
* GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
use taggederror::{CommonMetadata, IntentionalError, Tagged, TaggedError};
|
||||
|
||||
pub trait AnyhowEdenExt {
|
||||
/// Like AnyhowExt::common_metadata, except provides default metadata for known-Tagged Eden types.
|
||||
fn eden_metadata(&self) -> CommonMetadata;
|
||||
}
|
||||
|
||||
impl AnyhowEdenExt for anyhow::Error {
|
||||
fn eden_metadata(&self) -> CommonMetadata {
|
||||
let mut metadata: CommonMetadata = Default::default();
|
||||
|
||||
for cause in self.chain() {
|
||||
// Explicit metadata in error chain, created with AnyhowExt or .tagged()
|
||||
if let Some(e) = cause.downcast_ref::<TaggedError>() {
|
||||
metadata.merge(&e.metadata);
|
||||
}
|
||||
|
||||
// Implicit metadata, types known to implement Tagged
|
||||
// Add your type here to avoid having to type .tagged() to wrap it
|
||||
if let Some(e) = cause.downcast_ref::<IntentionalError>() {
|
||||
metadata.merge(&e.metadata());
|
||||
}
|
||||
|
||||
if metadata.complete() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
metadata
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AnyhowEdenExt for anyhow::Result<T> {
|
||||
fn eden_metadata(&self) -> CommonMetadata {
|
||||
if let Some(errref) = self.as_ref().err() {
|
||||
errref.eden_metadata()
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -234,7 +234,7 @@ pub trait Tagged: Error + Send + Sync + Sized + 'static {
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("intentional error for debugging with message '{0}'")]
|
||||
struct IntentionalError(String);
|
||||
pub struct IntentionalError(String);
|
||||
|
||||
impl Tagged for IntentionalError {
|
||||
fn metadata(&self) -> CommonMetadata {
|
||||
@ -243,9 +243,14 @@ impl Tagged for IntentionalError {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intentional_error() -> anyhow::Result<u8> {
|
||||
// .tagged() method on taggederror::Tagged trait attaches metadata and wraps in anyhow::Error
|
||||
Err(IntentionalError(String::from("intentional_error")).tagged()).into()
|
||||
pub fn intentional_error(tagged: bool) -> anyhow::Result<u8> {
|
||||
if tagged {
|
||||
// Metadata explicitly attached with .tagged()
|
||||
return Err(IntentionalError(String::from("intentional_error")).tagged());
|
||||
} else {
|
||||
// Metadata is automatically associated by taggederror_util::AnyhowEdenExt
|
||||
bail!(IntentionalError(String::from("intentional_error")))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intentional_bail() -> anyhow::Result<u8> {
|
||||
@ -359,8 +364,9 @@ macro_rules! bail {
|
||||
/// might contain metadata, and is not meant to be wrapped in anyhow itself,
|
||||
/// or otherwise passed around as an error wrapper type.
|
||||
pub struct FilteredAnyhow<'a> {
|
||||
mode: PrintMode,
|
||||
pub err: &'a anyhow::Error,
|
||||
mode: PrintMode,
|
||||
metadata_func: fn(&'a anyhow::Error) -> CommonMetadata,
|
||||
}
|
||||
|
||||
impl<'a> FilteredAnyhow<'a> {
|
||||
@ -368,11 +374,18 @@ impl<'a> FilteredAnyhow<'a> {
|
||||
FilteredAnyhow {
|
||||
err,
|
||||
mode: PrintMode::NoTags,
|
||||
metadata_func: |e| e.common_metadata(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_mode(err: &'a anyhow::Error, mode: PrintMode) -> Self {
|
||||
FilteredAnyhow { mode, err }
|
||||
pub fn with_metadata_func(mut self, func: fn(&'a anyhow::Error) -> CommonMetadata) -> Self {
|
||||
self.metadata_func = func;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_mode(mut self, mode: PrintMode) -> Self {
|
||||
self.mode = mode;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn no_tags(mut self) -> Self {
|
||||
@ -414,7 +427,7 @@ impl<'a> Display for FilteredAnyhow<'a> {
|
||||
|
||||
if self.mode == PrintMode::SeparateTags {
|
||||
write!(f, "\n\nerror tags: ")?;
|
||||
write!(f, "{}", self.err.common_metadata())?;
|
||||
write!(f, "{}", (self.metadata_func)(self.err))?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -459,7 +472,7 @@ impl<'a> Debug for FilteredAnyhow<'a> {
|
||||
|
||||
if self.mode == PrintMode::SeparateTags {
|
||||
write!(f, "\n\nerror tags: ")?;
|
||||
write!(f, "{}", self.err.common_metadata())?;
|
||||
write!(f, "{}", (self.metadata_func)(self.err))?;
|
||||
}
|
||||
|
||||
// No backtrace for now
|
||||
|
Loading…
Reference in New Issue
Block a user