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
Prev Previous commit
Next Next commit
address review: aclose, unraisable, frame publish
aclose ignore-yield does not close the generator. Format
unraisable object is None. close() leaves traceback to the
caller; finalize attaches the generator frame. Publish the
iframe before the lock-free slot.

Assisted-by: Grok:grok-4.6
  • Loading branch information
youknowone committed Sep 13, 2026
commit c2b10fc42e7a0c5dfc1097a5684351c44c58e3e4
20 changes: 9 additions & 11 deletions crates/vm/src/builtins/asyncgenerator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,11 @@ impl PyAsyncGenAThrow {

let (ty, val, tb) = self.value.clone();
let ret = self.ag.inner.throw(self.ag.as_object(), ty, val, tb, vm);
if self.aclose && self.ignored_close(&ret) {
return Err(self.yield_close(vm));
}
let ret = if self.aclose {
if self.ignored_close(&ret) {
Err(self.yield_close(vm))
} else {
ret.and_then(|o| o.into_async_pyresult(vm))
}
ret.and_then(|o| o.into_async_pyresult(vm))
} else {
PyAsyncGenWrappedValue::unbox(&self.ag, ret, vm)
};
Expand Down Expand Up @@ -572,12 +571,11 @@ impl PyAsyncGenAThrow {
exc_tb.unwrap_or_none(vm),
vm,
);
if self.aclose && self.ignored_close(&ret) {
return Err(self.yield_close(vm));
}
let res = if self.aclose {
if self.ignored_close(&ret) {
Err(self.yield_close(vm))
} else {
ret.and_then(|o| o.into_async_pyresult(vm))
}
ret.and_then(|o| o.into_async_pyresult(vm))
} else {
PyAsyncGenWrappedValue::unbox(&self.ag, ret, vm)
};
Expand Down Expand Up @@ -836,7 +834,7 @@ impl Destructor for PyAsyncGen {
return Ok(());
}
if let Err(e) = zelf.inner.close(zelf.as_object(), vm) {
crate::coroutine::unraisable_while_closing(zelf.as_object(), e, vm);
crate::coroutine::unraisable_while_closing(zelf.as_object(), &zelf.inner, e, vm);
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl Destructor for PyCoroutine {
return Ok(());
}
if let Err(e) = zelf.inner.close(zelf.as_object(), vm) {
crate::coroutine::unraisable_while_closing(zelf.as_object(), e, vm);
crate::coroutine::unraisable_while_closing(zelf.as_object(), &zelf.inner, e, vm);
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl Destructor for PyGenerator {
return Ok(());
}
if let Err(e) = zelf.inner.close(zelf.as_object(), vm) {
crate::coroutine::unraisable_while_closing(zelf.as_object(), e, vm);
crate::coroutine::unraisable_while_closing(zelf.as_object(), &zelf.inner, e, vm);
}
Ok(())
}
Expand Down
26 changes: 15 additions & 11 deletions crates/vm/src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ fn gen_name(jen: &PyObject, vm: &VirtualMachine) -> &'static str {
impl Coro {
pub fn new(frame: FrameObjectRef, name: PyStrRef, qualname: PyStrRef) -> Self {
let code = frame.iframe().code().to_owned();
frame.as_object().mark_cache_published();
Self {
frame: Some(frame).into(),
code,
Expand Down Expand Up @@ -330,16 +331,7 @@ impl Coro {
drop(claim);
match result {
Ok(ExecutionResult::Yield(_)) => {
let err =
vm.new_runtime_error(format!("{} ignored GeneratorExit", gen_name(jen, vm)));
if let Some(frame) = self.frame_opt() {
let lasti = frame.lasti().saturating_mul(2);
let lineno = rustpython_compiler_core::OneIndexed::new(frame.f_lineno().max(1))
.unwrap_or(rustpython_compiler_core::OneIndexed::MIN);
let tb = PyTraceback::new(None, frame, lasti, lineno);
err.set_traceback_typed(Some(tb.into_ref(&vm.ctx)));
}
Err(err)
Err(vm.new_runtime_error(format!("{} ignored GeneratorExit", gen_name(jen, vm))))
}
Err(e) if !is_gen_exit(&e, vm) => Err(e),
Ok(ExecutionResult::Return(value)) => Ok(value),
Expand Down Expand Up @@ -495,14 +487,26 @@ pub(crate) fn get_awaitable_iter(obj: PyObjectRef, vm: &VirtualMachine) -> PyRes

pub(crate) fn unraisable_while_closing(
jen: &PyObject,
coro: &Coro,
e: crate::builtins::PyBaseExceptionRef,
vm: &VirtualMachine,
) {
// Explicit close() leaves the traceback to the caller frame.
// Finalize has no caller frame, so attach the generator site here.
if e.__traceback__().is_none()
&& let Some(frame) = coro.frame_opt()
{
let lasti = frame.lasti().saturating_mul(2);
let lineno = rustpython_compiler_core::OneIndexed::new(frame.f_lineno().max(1))
.unwrap_or(rustpython_compiler_core::OneIndexed::MIN);
let tb = PyTraceback::new(None, frame, lasti, lineno);
e.set_traceback_typed(Some(tb.into_ref(&vm.ctx)));
}
let msg = jen
.repr(vm)
.ok()
.map(|r| format!("Exception ignored while closing generator {r}"));
vm.run_unraisable(e, msg, jen.to_owned());
vm.run_unraisable(e, msg, vm.ctx.none());
}

/// Emit DeprecationWarning for the deprecated 3-argument throw() signature.
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3404,7 +3404,7 @@ impl ExecutingFrame<'_> {
.repr(vm)
.ok()
.map(|r| format!("Exception ignored while closing generator {r}"));
vm.run_unraisable(e, msg, jen.to_owned());
vm.run_unraisable(e, msg, vm.ctx.none());
Ok(())
}
}
Expand Down
Loading