@@ -1739,7 +1739,42 @@ impl ExecutingFrame<'_> {
17391739 // We do not have any more blocks to unwind. Inspect the reason we are here:
17401740 match reason {
17411741 UnwindReason :: Raising { exception } => Err ( exception) ,
1742- UnwindReason :: Returning { value } => Ok ( Some ( ExecutionResult :: Return ( value) ) ) ,
1742+ UnwindReason :: Returning { value } => {
1743+ // Clear tracebacks of exceptions in fastlocals to break reference cycles.
1744+ // This is needed because when returning from inside an except block,
1745+ // the exception cleanup code (e = None; del e) is skipped, leaving the
1746+ // exception with a traceback that references this frame, which references
1747+ // the exception in fastlocals, creating a cycle that can't be collected
1748+ // since RustPython doesn't have a tracing GC.
1749+ //
1750+ // We only clear tracebacks of exceptions that:
1751+ // 1. Are not the return value itself (will be needed by caller)
1752+ // 2. Are not the current active exception (still being handled)
1753+ // 3. Have a traceback whose top frame is THIS frame (we created it)
1754+ let current_exc = vm. current_exception ( ) ;
1755+ let fastlocals = self . fastlocals . lock ( ) ;
1756+ for obj in fastlocals. iter ( ) . flatten ( ) {
1757+ // Skip if this object is the return value
1758+ if obj. is ( & value) {
1759+ continue ;
1760+ }
1761+ if let Ok ( exc) = obj. clone ( ) . downcast :: < PyBaseException > ( ) {
1762+ // Skip if this is the current active exception
1763+ if current_exc. as_ref ( ) . is_some_and ( |cur| exc. is ( cur) ) {
1764+ continue ;
1765+ }
1766+ // Only clear if traceback's top frame is this frame
1767+ if exc
1768+ . __traceback__ ( )
1769+ . is_some_and ( |tb| core:: ptr:: eq :: < Py < Frame > > ( & * tb. frame , self . object ) )
1770+ {
1771+ exc. set_traceback_typed ( None ) ;
1772+ }
1773+ }
1774+ }
1775+ drop ( fastlocals) ;
1776+ Ok ( Some ( ExecutionResult :: Return ( value) ) )
1777+ }
17431778 UnwindReason :: Break { .. } | UnwindReason :: Continue { .. } => {
17441779 self . fatal ( "break or continue must occur within a loop block." )
17451780 } // UnwindReason::NoWorries => Ok(None),
0 commit comments