diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index d88f2c598..a0b50df8f 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -657,7 +657,6 @@ class TestSet(TestJointOps, unittest.TestCase): self.assertRaises(KeyError, myset.remove, set(range(1))) self.assertRaises(KeyError, myset.remove, set(range(3))) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_unhashable_element(self): myset = {'a'} elem = [1, 2, 3] diff --git a/crates/vm/src/builtins/set.rs b/crates/vm/src/builtins/set.rs index 44dc9a221..a8f3e83c8 100644 --- a/crates/vm/src/builtins/set.rs +++ b/crates/vm/src/builtins/set.rs @@ -223,7 +223,9 @@ impl PySetInner { } fn contains(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { - self.retry_op_with_frozenset(needle, vm, |needle, vm| self.content.contains(vm, needle)) + let result = self + .retry_op_with_frozenset(needle, vm, |needle, vm| self.content.contains(vm, needle)); + Self::wrap_unhashable_error(result, needle, vm) } fn compare(&self, other: &Self, op: PyComparisonOp, vm: &VirtualMachine) -> PyResult { @@ -327,15 +329,20 @@ impl PySetInner { } fn add(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.content.insert(vm, &*item, ()) + let result = self.content.insert(vm, &*item, ()); + Self::wrap_unhashable_error(result, &item, vm) } fn remove(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.retry_op_with_frozenset(&item, vm, |item, vm| self.content.delete(vm, item)) + let result = + self.retry_op_with_frozenset(&item, vm, |item, vm| self.content.delete(vm, item)); + Self::wrap_unhashable_error(result, &item, vm) } fn discard(&self, item: &PyObject, vm: &VirtualMachine) -> PyResult { - self.retry_op_with_frozenset(item, vm, |item, vm| self.content.delete_if_exists(vm, item)) + let result = self + .retry_op_with_frozenset(item, vm, |item, vm| self.content.delete_if_exists(vm, item)); + Self::wrap_unhashable_error(result, item, vm) } fn clear(&self) { @@ -488,6 +495,25 @@ impl PySetInner { }) }) } + + fn wrap_unhashable_error( + result: PyResult, + item: &PyObject, + vm: &VirtualMachine, + ) -> PyResult { + match result { + Err(cause) if cause.fast_isinstance(vm.ctx.exceptions.type_error) => { + let message = cause.as_object().str(vm)?; + let err = vm.new_type_error(format!( + "cannot use '{}' as a set element ({message})", + item.class().name() + )); + err.set___cause__(Some(cause)); + Err(err) + } + result => result, + } + } } fn extract_set(obj: &PyObject) -> Option<&PySetInner> {