Store StopIteration.value in a struct field instead of the instance dict (#8301)

* Store StopIteration.value in a struct field like SystemExit

* Unmark test_pydoc's test_member_descriptor

* Use expect instead of unwrap in new_stop_iteration
This commit is contained in:
강동안 authored and GitHub committed 2026-07-19 13:50:13 +09:00
1 parent e68d9c266f
commit 39f1630bf6
3 files changed
+67 -20

No files matched your search

-1
View File
@@ -1797,7 +1797,6 @@ cm(x) class method of test.test_pydoc.test_pydoc.X
self.assertEqual(self._get_summary_line(Exception.args), "args")
self.assertEqual(self._get_summary_line(memoryview.obj), "obj")
@unittest.expectedFailure # TODO: RUSTPYTHON
@requires_docstrings
def test_member_descriptor(self):
# Currently these attributes are implemented as member descriptors
+64 -10
View File
@@ -965,9 +965,7 @@ impl ExceptionZoo {
extend_exception!(PyException, ctx, excs.exception_type);
extend_exception!(PyStopIteration, ctx, excs.stop_iteration, {
"value" => ctx.none(),
});
extend_exception!(PyStopIteration, ctx, excs.stop_iteration);
extend_exception!(PyStopAsyncIteration, ctx, excs.stop_async_iteration);
extend_exception!(PyArithmeticError, ctx, excs.arithmetic_error);
@@ -1702,18 +1700,57 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PyException(PyBaseException);
#[pyexception(name, base = PyException, ctx = "stop_iteration")]
#[derive(Debug)]
#[repr(transparent)]
pub struct PyStopIteration(PyException);
#[pyexception(name, base = PyException, ctx = "stop_iteration", traverse = "manual")]
#[repr(C)]
pub struct PyStopIteration {
base: PyException,
value: PyAtomicRef<Option<PyObject>>,
}
#[pyexception(with(Initializer))]
impl PyStopIteration {}
impl crate::class::PySubclass for PyStopIteration {
type Base = PyException;
fn as_base(&self) -> &Self::Base {
&self.base
}
}
impl core::fmt::Debug for PyStopIteration {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PyStopIteration").finish_non_exhaustive()
}
}
unsafe impl Traverse for PyStopIteration {
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
self.base.0.traverse(tracer_fn);
if let Some(obj) = self.value.deref() {
tracer_fn(obj);
}
}
}
impl Constructor for PyStopIteration {
type Args = FuncArgs;
fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
let base_exception = PyBaseException::new(args.args, vm);
Ok(Self {
base: PyException(base_exception),
value: None.into(),
})
}
}
impl Initializer for PyStopIteration {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
zelf.set_attr("value", vm.unwrap_or_none(args.args.first().cloned()), vm)?;
let value = match args.args.len() {
0 => vm.ctx.none(),
_ => args.args[0].clone(),
};
PyBaseException::slot_init(zelf.clone(), args, vm)?;
let exc: &Py<Self> = zelf.downcast_ref::<Self>().unwrap();
exc.value.swap_to_temporary_refs(Some(value), vm);
Ok(())
}
@@ -1722,6 +1759,23 @@ pub(super) mod types {
}
}
#[pyexception(with(Constructor, Initializer))]
impl PyStopIteration {
#[pygetset]
fn value(&self) -> Option<PyObjectRef> {
self.value.to_owned()
}
#[pygetset(setter)]
fn set_value(&self, setter_value: PySetterValue, vm: &VirtualMachine) {
let value = match setter_value {
PySetterValue::Assign(v) => Some(v),
PySetterValue::Delete => None,
};
self.value.swap_to_temporary_refs(value, vm);
}
}
#[pyexception(name, base = PyException, ctx = "stop_async_iteration", impl)]
#[derive(Debug)]
#[repr(transparent)]
+3 -9
View File
@@ -894,21 +894,15 @@ impl VirtualMachine {
}
pub fn new_stop_iteration(&self, value: Option<PyObjectRef>) -> PyBaseExceptionRef {
let dict = self.ctx.new_dict();
let stop_iteration_error = self.ctx.exceptions.stop_iteration.to_owned();
let args = if let Some(value) = value {
// manually set `value` attribute like StopIteration.__init__
dict.set_item("value", value.clone(), self)
.expect("dict.__setitem__ never fails");
vec![value]
} else {
Vec::new()
};
let exc = self.invoke_exception(stop_iteration_error, args);
PyRef::new_ref(
PyBaseException::new(args, self),
self.ctx.exceptions.stop_iteration.to_owned(),
Some(dict),
)
exc.expect("StopIteration is a BaseException Subclass.")
}
fn new_downcast_error(