@@ -10,6 +10,7 @@ use crate::server::{self, ServerSettings};
1010use crate :: settings:: Settings ;
1111use crate :: setup:: { self } ;
1212use pyo3:: IntoPyObjectExt ;
13+ use pyo3:: types:: { PyDict , PyModule , PyString } ;
1314use pyo3:: { exceptions:: PyException , prelude:: * } ;
1415use pyo3_async_runtimes:: tokio:: future_into_py;
1516use std:: fmt:: Write ;
@@ -37,10 +38,31 @@ impl<T> ToResultWithPyTrace<T> for Result<T, PyErr> {
3738 match self {
3839 Ok ( value) => Ok ( value) ,
3940 Err ( err) => {
40- let mut err_str = format ! ( "Error calling Python function: {err}" ) ;
41- if let Some ( tb) = err. traceback ( py) {
42- write ! ( & mut err_str, "\n {}" , tb. format( ) ?) ?;
43- }
41+ // Attempt to render a full Python-style traceback including cause/context chain
42+ let full_trace: PyResult < String > = ( || {
43+ let exc = err. value ( py) ;
44+ let traceback = PyModule :: import ( py, "traceback" ) ?;
45+ let tbe_class = traceback. getattr ( "TracebackException" ) ?;
46+ let tbe = tbe_class. call_method1 ( "from_exception" , ( exc, ) ) ?;
47+ let kwargs = PyDict :: new ( py) ;
48+ kwargs. set_item ( "chain" , true ) ?;
49+ let lines = tbe. call_method ( "format" , ( ) , Some ( & kwargs) ) ?;
50+ let joined = PyString :: new ( py, "" ) . call_method1 ( "join" , ( lines, ) ) ?;
51+ joined. extract :: < String > ( )
52+ } ) ( ) ;
53+
54+ let err_str = match full_trace {
55+ Ok ( trace) => format ! ( "Error calling Python function:\n {trace}" ) ,
56+ Err ( _) => {
57+ // Fallback: include the PyErr display and available traceback formatting
58+ let mut s = format ! ( "Error calling Python function: {err}" ) ;
59+ if let Some ( tb) = err. traceback ( py) {
60+ write ! ( & mut s, "\n {}" , tb. format( ) ?) . ok ( ) ;
61+ }
62+ s
63+ }
64+ } ;
65+
4466 Err ( anyhow:: anyhow!( err_str) )
4567 }
4668 }
0 commit comments