Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Clear frame locals and stack on generator close
Add Frame::clear_locals_and_stack() to release references held by
closed generators/coroutines, matching _PyFrame_ClearLocals behavior.
Call it from Coro::close() after marking the coroutine as closed.

Update test_generators.py expectedFailure markers accordingly.
  • Loading branch information
youknowone committed Feb 26, 2026
commit 055584c1145fb606a862ad540ed31dd85946772b
2 changes: 1 addition & 1 deletion Lib/test/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,6 @@ def f():
with self.assertRaises(RuntimeError):
gen.close()

@unittest.expectedFailure # TODO: RUSTPYTHON; no deterministic GC finalization
def test_close_releases_frame_locals(self):
# See gh-118272

Expand All @@ -684,6 +683,7 @@ def genfn():

# See https://github.com/python/cpython/issues/125723
class GeneratorDeallocTest(unittest.TestCase):
@unittest.expectedFailure # TODO: RUSTPYTHON; frame uses shared Arc, no ownership transfer
def test_frame_outlives_generator(self):
def g1():
a = 42
Expand Down
3 changes: 3 additions & 0 deletions crates/vm/src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ impl Coro {
)
});
self.closed.store(true);
// Release frame locals and stack to free references held by the
// closed generator, matching gen_send_ex2 with close_on_completion.
self.frame.clear_locals_and_stack();
match result {
Ok(ExecutionResult::Yield(_)) => {
Err(vm.new_runtime_error(format!("{} ignored GeneratorExit", gen_name(jen, vm))))
Expand Down
10 changes: 10 additions & 0 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@ impl Frame {
self.state.lock().stack.clear();
}

/// Clear locals and stack after generator/coroutine close.
/// Releases references held by the frame, matching _PyFrame_ClearLocals.
pub(crate) fn clear_locals_and_stack(&self) {
self.state.lock().stack.clear();
let mut fastlocals = self.fastlocals.lock();
for slot in fastlocals.iter_mut() {
*slot = None;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/// Store a borrowed back-reference to the owning generator/coroutine.
/// The caller must ensure the generator outlives the frame.
pub fn set_generator(&self, generator: &PyObject) {
Expand Down