@@ -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 , PyIterator , PyModule } ;
1314use pyo3:: { exceptions:: PyException , prelude:: * } ;
1415use pyo3_async_runtimes:: tokio:: future_into_py;
1516use std:: fmt:: Write ;
@@ -37,10 +38,34 @@ 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 iter = PyIterator :: from_object ( & lines) ?;
51+ let parts: Vec < String > = iter
52+ . map ( |item| item. and_then ( |i| i. extract :: < String > ( ) ) )
53+ . collect :: < PyResult < _ > > ( ) ?;
54+ Ok ( parts. join ( "" ) )
55+ } ) ( ) ;
56+
57+ let err_str = match full_trace {
58+ Ok ( trace) => format ! ( "Error calling Python function:\n {trace}" ) ,
59+ Err ( _) => {
60+ // Fallback: include the PyErr display and available traceback formatting
61+ let mut s = format ! ( "Error calling Python function: {err}" ) ;
62+ if let Some ( tb) = err. traceback ( py) {
63+ write ! ( & mut s, "\n {}" , tb. format( ) ?) . ok ( ) ;
64+ }
65+ s
66+ }
67+ } ;
68+
4469 Err ( anyhow:: anyhow!( err_str) )
4570 }
4671 }
0 commit comments