@@ -20,6 +20,8 @@ use crate::{BoundObject, Py, PyAny, Python};
2020use err_state:: { PyErrState , PyErrStateLazyFnOutput , PyErrStateNormalized } ;
2121use std:: convert:: Infallible ;
2222use std:: ffi:: CStr ;
23+ #[ cfg( all( debug_assertions, not( Py_LIMITED_API ) ) ) ]
24+ use { crate :: types:: PyFrame , std:: ffi:: CString } ;
2325
2426mod cast_error;
2527mod downcast_error;
@@ -127,10 +129,14 @@ impl PyErr {
127129 T : PyTypeInfo ,
128130 A : PyErrArguments + Send + Sync + ' static ,
129131 {
132+ #[ cfg( all( debug_assertions, not( Py_LIMITED_API ) ) ) ]
133+ let backtrace = backtrace:: Backtrace :: new_unresolved ( ) ;
130134 PyErr :: from_state ( PyErrState :: lazy ( Box :: new ( move |py| {
131135 PyErrStateLazyFnOutput {
132136 ptype : T :: type_object ( py) . into ( ) ,
133137 pvalue : args. arguments ( py) ,
138+ #[ cfg( all( debug_assertions, not( Py_LIMITED_API ) ) ) ]
139+ backtrace,
134140 }
135141 } ) ) )
136142 }
@@ -289,7 +295,24 @@ impl PyErr {
289295 Self :: print_panic_and_unwind ( py, state)
290296 }
291297
292- Some ( PyErr :: from_state ( PyErrState :: normalized ( state) ) )
298+ let err = PyErr :: from_state ( PyErrState :: normalized ( state) ) ;
299+
300+ #[ cfg( all( debug_assertions, not( Py_LIMITED_API ) ) ) ]
301+ {
302+ let mut backtrace = backtrace:: Backtrace :: new ( ) ;
303+ if let Some ( traceback) = PyTraceback :: from_frames (
304+ py,
305+ err. traceback ( py) ,
306+ backtrace_to_frames ( py, & mut backtrace) ,
307+ )
308+ . ok ( )
309+ . flatten ( )
310+ {
311+ err. set_traceback ( py, Some ( traceback) ) ;
312+ }
313+ }
314+
315+ Some ( err)
293316 }
294317
295318 #[ cold]
@@ -696,6 +719,49 @@ impl<'py> IntoPyObject<'py> for PyErr {
696719 }
697720}
698721
722+ #[ cfg( all( debug_assertions, not( Py_LIMITED_API ) ) ) ]
723+ fn backtrace_to_frames < ' py , ' a > (
724+ py : Python < ' py > ,
725+ backtrace : & ' a mut backtrace:: Backtrace ,
726+ ) -> impl Iterator < Item = Bound < ' py , PyFrame > > + use < ' py , ' a > {
727+ backtrace. resolve ( ) ;
728+ backtrace
729+ . frames ( )
730+ . iter ( )
731+ . flat_map ( |frame| frame. symbols ( ) )
732+ . map ( |symbol| ( symbol. name ( ) . map ( |name| format ! ( "{name:#}" ) ) , symbol) )
733+ . skip_while ( |( name, _) | {
734+ if cfg ! ( any( target_vendor = "apple" , windows) ) {
735+ // On Apple & Windows platforms, backtrace is not able to remove internal frames
736+ // from the backtrace, so we need to skip them manually here.
737+ name. as_ref ( )
738+ . map ( |name| name. starts_with ( "backtrace::" ) )
739+ . unwrap_or ( true )
740+ } else {
741+ false
742+ }
743+ } )
744+ // The first frame is always the capture function, so skip it.
745+ . skip ( 1 )
746+ . take_while ( |( name, _) | {
747+ name. as_ref ( )
748+ . map ( |name| {
749+ !( name. starts_with ( "pyo3::impl_::trampoline::" )
750+ || name. contains ( "__rust_begin_short_backtrace" ) )
751+ } )
752+ . unwrap_or ( true )
753+ } )
754+ . filter_map ( move |( name, symbol) | {
755+ let file =
756+ CString :: new ( symbol. filename ( ) ?. as_os_str ( ) . to_string_lossy ( ) . as_ref ( ) ) . ok ( ) ?;
757+
758+ let function = CString :: new ( name. as_deref ( ) . unwrap_or ( "<unknown>" ) ) . ok ( ) ?;
759+ let line = symbol. lineno ( ) ?;
760+
761+ PyFrame :: new ( py, & file, & function, line as _ ) . ok ( )
762+ } )
763+ }
764+
699765impl < ' py > IntoPyObject < ' py > for & PyErr {
700766 type Target = PyBaseException ;
701767 type Output = Bound < ' py , Self :: Target > ;
@@ -844,6 +910,7 @@ mod tests {
844910 }
845911
846912 #[ test]
913+ #[ cfg( false ) ]
847914 fn err_debug ( ) {
848915 // Debug representation should be like the following (without the newlines):
849916 // PyErr {
0 commit comments