diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 2a96ef4dd71..5c9059614de 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1797,7 +1797,6 @@ def test_getset_descriptor(self): 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 diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 1db10274e89..f76a2ad62ed 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -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>, + } - #[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, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + 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 = zelf.downcast_ref::().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 { + 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)] diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index ae728aaba67..857ecb33c72 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -894,21 +894,15 @@ impl VirtualMachine { } pub fn new_stop_iteration(&self, value: Option) -> 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(