mirror of
https://github.com/facebook/sapling.git
synced 2024-12-28 15:44:27 +03:00
taggederror: introduce bail macro replacement which allows tagging
Summary: This change introduces a bail macro that allows tagging errors using the syntax `bail!(fault=Fault::Request, "my normal {}", bail_args)` or `bail!(Fault::Request, "my normal {}", bail_args)`. Reviewed By: DurhamG Differential Revision: D22646428 fbshipit-source-id: a6ec2940001b26db8ddc3a6d3620a1e17406c867
This commit is contained in:
parent
be0df9024d
commit
586ada8de6
@ -3624,6 +3624,12 @@ def debugthrowrustexception(ui, _repo):
|
||||
bindings.error.throwrustexception()
|
||||
|
||||
|
||||
@command("debugthrowrustbail", [], "")
|
||||
def debugthrowrustbail(ui, _repo):
|
||||
"""cause an error to be returned from rust and propagated to python using bail"""
|
||||
bindings.error.throwrustbail()
|
||||
|
||||
|
||||
@command("debugthrowexception", [], "")
|
||||
def debugthrowexception(ui, _repo):
|
||||
"""cause an intentional exception to be raised in the command"""
|
||||
|
@ -8,7 +8,7 @@
|
||||
use cpython::*;
|
||||
use cpython_ext::{error, ResultPyErrExt};
|
||||
|
||||
use taggederror::{intentional_error, CommonMetadata, Fault, FilteredAnyhow};
|
||||
use taggederror::{intentional_bail, intentional_error, CommonMetadata, Fault, FilteredAnyhow};
|
||||
|
||||
py_exception!(error, IndexedLogError);
|
||||
py_exception!(error, MetaLogError);
|
||||
@ -72,6 +72,7 @@ pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
|
||||
py.get_type::<TaggedExceptionData>(),
|
||||
)?;
|
||||
m.add(py, "throwrustexception", py_fn!(py, py_intentional_error()))?;
|
||||
m.add(py, "throwrustbail", py_fn!(py, py_intentional_bail()))?;
|
||||
|
||||
register_error_handlers();
|
||||
|
||||
@ -120,3 +121,9 @@ fn py_intentional_error(py: Python) -> PyResult<PyInt> {
|
||||
.map(|r| r.to_py_object(py))
|
||||
.map_pyerr(py)?)
|
||||
}
|
||||
|
||||
fn py_intentional_bail(py: Python) -> PyResult<PyInt> {
|
||||
Ok(intentional_bail()
|
||||
.map(|r| r.to_py_object(py))
|
||||
.map_pyerr(py)?)
|
||||
}
|
||||
|
@ -7,8 +7,3 @@ edition = "2018"
|
||||
[dependencies]
|
||||
anyhow = "1.0.20"
|
||||
thiserror = "1.0.5"
|
||||
thrift-types = { path = "../thrift-types" }
|
||||
tracing = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0.7"
|
||||
|
@ -138,6 +138,7 @@ impl CommonMetadata {
|
||||
pub trait AnyhowExt {
|
||||
fn with_fault(self, fault: Fault) -> Self;
|
||||
fn with_type_name(self, type_name: TypeName) -> Self;
|
||||
fn with_metadata(self, metadata: CommonMetadata) -> Self;
|
||||
|
||||
/// Traverse the error / context tree and assemble all CommonMetadata
|
||||
fn common_metadata(&self) -> CommonMetadata;
|
||||
@ -152,6 +153,10 @@ impl AnyhowExt for anyhow::Error {
|
||||
TaggedError::new(self, CommonMetadata::default().with_type_name(typename)).wrapped()
|
||||
}
|
||||
|
||||
fn with_metadata(self, metadata: CommonMetadata) -> Self {
|
||||
TaggedError::new(self, metadata).wrapped()
|
||||
}
|
||||
|
||||
fn common_metadata(&self) -> CommonMetadata {
|
||||
let mut metadata: CommonMetadata = Default::default();
|
||||
|
||||
@ -177,6 +182,10 @@ impl<T> AnyhowExt for anyhow::Result<T> {
|
||||
self.map_err(|e| e.with_type_name(typename))
|
||||
}
|
||||
|
||||
fn with_metadata(self, metadata: CommonMetadata) -> Self {
|
||||
self.map_err(|e| e.with_metadata(metadata))
|
||||
}
|
||||
|
||||
fn common_metadata(&self) -> CommonMetadata {
|
||||
if let Some(errref) = self.as_ref().err() {
|
||||
errref.common_metadata()
|
||||
@ -239,6 +248,15 @@ pub fn intentional_error() -> anyhow::Result<u8> {
|
||||
Err(IntentionalError(String::from("intentional_error")).tagged()).into()
|
||||
}
|
||||
|
||||
pub fn intentional_bail() -> anyhow::Result<u8> {
|
||||
bail!(
|
||||
fault = Fault::Request,
|
||||
TypeName("taggederror::FakeTypeNameForTesting"),
|
||||
"intentional bail with {}",
|
||||
"format params"
|
||||
)
|
||||
}
|
||||
|
||||
/// Controls how metadata is handled when formatting a FilteredAnyhow.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PrintMode {
|
||||
@ -259,6 +277,82 @@ pub enum PrintMode {
|
||||
SeparateTags,
|
||||
}
|
||||
|
||||
/// A drop-in replacement for the anyhow::bail macro, which allows applying error metadata.
|
||||
///
|
||||
/// Supports all three styles of `bail!` calls supported by anyhow
|
||||
///
|
||||
/// String literal: `bail!("literal error message")`
|
||||
/// Display Expression: `bail!(my_expr_impls_display)`
|
||||
/// Format Expression: `bail!("failure in {} system", "logging")`
|
||||
///
|
||||
/// You can provide metadata for these errors by prepending it to the
|
||||
/// `bail` argument list. Metadata can be provided with in two styles,
|
||||
/// `key = value` and "literal" syntax. These forms can be mixed as desired in
|
||||
/// the same call.
|
||||
///
|
||||
/// Literal style: `bail!(Fault::Request, TypeName("fakemod::FakeTypeName"), "standard bail args")`
|
||||
/// Key-value style: `bail!(fault = my_fault(), type_name = TypeName(my_static_str()), "bail format {}", "args")`
|
||||
/// Mixed: `bail!(Fault::Request, type_name = my_typename(), "bail message")`
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
// Bail variations with metadata
|
||||
(@withmeta $meta:expr, $msg:literal $(,)?) => {
|
||||
return std::result::Result::Err(anyhow::anyhow!($msg).with_metadata($meta));
|
||||
};
|
||||
(@withmeta $meta:expr, $err:expr $(,)?) => {
|
||||
return std::result::Result::Err(anyhow::anyhow!($err).with_metadata($meta));
|
||||
};
|
||||
(@withmeta $meta:expr, $fmt:expr, $($arg:tt)*) => {
|
||||
return std::result::Result::Err(anyhow::anyhow!($fmt, $($arg)*).with_metadata($meta));
|
||||
};
|
||||
|
||||
// Metadata munching
|
||||
// Concise syntax for literal metadata
|
||||
(@metadata $meta:expr, Fault::$fault:ident, $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::with_fault($meta, Fault::$fault), $($tail)+)
|
||||
};
|
||||
(@metadata $meta:expr, TypeName($type_name:expr), $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::with_type_name($meta, TypeName($type_name)), $($tail)+)
|
||||
};
|
||||
// More verbose key=value syntax for metadata expressions
|
||||
(@metadata $meta:expr, fault=$fault:expr, $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::with_fault($meta, $fault), $($tail)+)
|
||||
};
|
||||
(@metadata $meta:expr, type_name=$type_name:expr, $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::with_type_name($meta, $type_name), $($tail)+)
|
||||
};
|
||||
|
||||
// Metadata base case, trailing bail args
|
||||
(@metadata $meta:expr, $($args:tt)+) => {
|
||||
bail!(@withmeta $meta, $($args)+)
|
||||
};
|
||||
|
||||
// Metadata entry points
|
||||
(Fault::$fault:ident, $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::default().with_fault(Fault::$fault), $($tail)+)
|
||||
};
|
||||
(TypeName($type_name:expr), $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::default().with_type_name(TypeName($type_name)), $($tail)+)
|
||||
};
|
||||
(fault=$fault:expr, $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::default().with_fault($fault), $($tail)+)
|
||||
};
|
||||
(type_name=$type_name:expr, $($tail:tt)+) => {
|
||||
bail!(@metadata CommonMetadata::default().with_type_name($type_name), $($tail)+)
|
||||
};
|
||||
|
||||
// Bail variations without metadata
|
||||
($msg:literal $(,)?) => {
|
||||
return std::result::Result::Err(anyhow::anyhow!($msg));
|
||||
};
|
||||
($err:expr $(,)?) => {
|
||||
return std::result::Result::Err(anyhow::anyhow!($err));
|
||||
};
|
||||
($fmt:expr, $($arg:tt)*) => {
|
||||
return std::result::Result::Err(anyhow::anyhow!($fmt, $($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
/// A wrapper for anyhow which allows special handling of TaggedError metadata.
|
||||
///
|
||||
/// This should only be constructed in order to print an anyhow::Error that
|
||||
|
@ -167,6 +167,7 @@ Show debug commands if there are no other candidates
|
||||
debugsuccessorssets
|
||||
debugtemplate
|
||||
debugthrowexception
|
||||
debugthrowrustbail
|
||||
debugthrowrustexception
|
||||
debugtreestate
|
||||
debugupdatecaches
|
||||
@ -448,6 +449,7 @@ Show all commands + options
|
||||
debugsuccessorssets: closest
|
||||
debugtemplate: rev, define
|
||||
debugthrowexception:
|
||||
debugthrowrustbail:
|
||||
debugthrowrustexception:
|
||||
debugtreestate:
|
||||
debugupdatecaches:
|
||||
|
@ -140,6 +140,26 @@ Test exception logging:
|
||||
metrics_type: exceptions
|
||||
rust_error_type: taggederror::IntentionalError
|
||||
|
||||
$ > $LOGDIR/samplingpath.txt
|
||||
$ hg debugthrowrustbail 2>&1 | egrep -v '^ '
|
||||
\*\* Mercurial Distributed SCM * has crashed: (glob)
|
||||
atexit handler executed
|
||||
Traceback (most recent call last):
|
||||
*RustError: intentional bail with format params (glob)
|
||||
>>> import json, pprint
|
||||
>>> with open("$LOGDIR/samplingpath.txt") as f:
|
||||
... data = f.read().strip("\0").split("\0")
|
||||
>>> for jsonstr in data:
|
||||
... entry = json.loads(jsonstr)
|
||||
... if entry["category"] == "exceptions":
|
||||
... for k in sorted(entry["data"].keys()):
|
||||
... print("%s: %s" % (k, entry["data"][k]))
|
||||
exception_msg: intentional bail with format params
|
||||
exception_type: RustError
|
||||
fault: request
|
||||
metrics_type: exceptions
|
||||
rust_error_type: taggederror::FakeTypeNameForTesting
|
||||
|
||||
$ > $LOGDIR/samplingpath.txt
|
||||
$ hg debugthrowexception 2>&1 | egrep -v '^ '
|
||||
\*\* Mercurial Distributed SCM * has crashed: (glob)
|
||||
|
@ -1070,6 +1070,9 @@ Test list of internal help commands
|
||||
parse and apply a template
|
||||
debugthrowexception
|
||||
cause an intentional exception to be raised in the command
|
||||
debugthrowrustbail
|
||||
cause an error to be returned from rust and propagated to
|
||||
python using bail
|
||||
debugthrowrustexception
|
||||
cause an error to be returned from rust and propagated to
|
||||
python
|
||||
|
Loading…
Reference in New Issue
Block a user