diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 6148bd3a6..61f4156dc 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -601,7 +601,6 @@ class ExceptionTests(unittest.TestCase): with self.assertRaisesRegex(TypeError, "state is not a dictionary"): e.__setstate__(42) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_notes(self): for e in [BaseException(1), Exception(2), ValueError(3)]: with self.subTest(e=e): diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 1a9c95a93..3cf26c1d5 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -4,7 +4,7 @@ use crate::object::{Traverse, TraverseFn}; use crate::{ AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, builtins::{ - PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, + PyList, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, traceback::{PyTraceback, PyTracebackRef}, }, class::{PyClassImpl, StaticType}, @@ -652,6 +652,29 @@ impl PyRef { Ok(self) } + #[pymethod] + fn add_note(self, note: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { + let dict = self + .as_object() + .dict() + .ok_or_else(|| vm.new_attribute_error("Exception object has no __dict__"))?; + + let notes = if let Ok(notes) = dict.get_item("__notes__", vm) { + notes + } else { + let new_notes = vm.ctx.new_list(vec![]); + dict.set_item("__notes__", new_notes.clone().into(), vm)?; + new_notes.into() + }; + + let notes = notes + .downcast::() + .map_err(|_| vm.new_type_error("__notes__ must be a list"))?; + + notes.borrow_vec_mut().push(note.into()); + Ok(()) + } + #[pymethod] fn __reduce__(self, vm: &VirtualMachine) -> PyTupleRef { if let Some(dict) = self.as_object().dict().filter(|x| !x.is_empty()) {