aws_smithy_http_server_python/util/
error.rs1use std::fmt;
9
10use pyo3::{PyErr, Python};
11
12pub struct RichPyErr(PyErr);
14
15impl fmt::Debug for RichPyErr {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
17 Python::with_gil(|py| {
18 let mut debug_struct = f.debug_struct("RichPyErr");
19 debug_struct
20 .field("type", self.0.get_type(py))
21 .field("value", self.0.value(py));
22
23 if let Some(traceback) = self.0.traceback(py) {
24 if let Ok(traceback) = traceback.format() {
25 debug_struct.field("traceback", &traceback);
26 }
27 }
28
29 if let Some(cause) = self.0.cause(py) {
30 debug_struct.field("cause", &rich_py_err(cause));
31 }
32
33 debug_struct.finish()
34 })
35 }
36}
37
38pub fn rich_py_err(err: PyErr) -> RichPyErr {
40 RichPyErr(err)
41}
42
43#[cfg(test)]
44mod tests {
45 use pyo3::prelude::*;
46
47 use super::*;
48
49 #[test]
50 fn rich_python_errors() -> PyResult<()> {
51 pyo3::prepare_freethreaded_python();
52
53 let py_err = Python::with_gil(|py| {
54 py.run(
55 r#"
56def foo():
57 base_err = ValueError("base error")
58 raise ValueError("some python error") from base_err
59
60def bar():
61 foo()
62
63def baz():
64 bar()
65
66baz()
67"#,
68 None,
69 None,
70 )
71 .unwrap_err()
72 });
73
74 let debug_output = format!("{:?}", rich_py_err(py_err));
75
76 assert!(debug_output.contains("some python error"));
78
79 assert!(debug_output.contains("foo"));
81 assert!(debug_output.contains("bar"));
82 assert!(debug_output.contains("baz"));
83
84 assert!(debug_output.contains("base error"));
86
87 Ok(())
88 }
89}