diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 0d9ac1fc0..78e0283a8 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -172,7 +172,6 @@ class TestJointOps: else: self.fail("s-t did not screen-out general iterables") - @unittest.skip("TODO: RUSTPYTHON") def test_symmetric_difference(self): i = self.s.symmetric_difference(self.otherword) for c in self.letters: @@ -187,7 +186,6 @@ class TestJointOps: self.assertEqual(self.thetype('abcba').symmetric_difference(C('ccb')), set('a')) self.assertEqual(self.thetype('abcba').symmetric_difference(C('ef')), set('abcef')) - @unittest.skip("TODO: RUSTPYTHON") def test_xor(self): i = self.s.symmetric_difference(self.otherword) self.assertEqual(self.s ^ set(self.otherword), i) @@ -207,7 +205,6 @@ class TestJointOps: self.assertNotEqual(self.s, frozenset(self.otherword)) self.assertEqual(self.s != self.word, True) - @unittest.skip("TODO: RUSTPYTHON") def test_setOfFrozensets(self): t = map(frozenset, ['abcdef', 'bcd', 'bdcb', 'fed', 'fedccba']) s = self.thetype(t) @@ -515,7 +512,6 @@ class TestSet(TestJointOps, unittest.TestCase): for c in (self.word + self.otherword): self.assertIn(c, self.s) - @unittest.skip("TODO: RUSTPYTHON") def test_intersection_update(self): retval = self.s.intersection_update(self.otherword) self.assertEqual(retval, None) @@ -582,7 +578,6 @@ class TestSet(TestJointOps, unittest.TestCase): else: self.assertNotIn(c, self.s) - @unittest.skip("TODO: RUSTPYTHON") def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.otherword) self.assertEqual(retval, None) @@ -713,7 +708,6 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): t = self.thetype(s) self.assertEqual(id(s), id(t)) - @unittest.skip("TODO: RUSTPYTHON") def test_hash(self): self.assertEqual(hash(self.thetype('abcdeb')), hash(self.thetype('ebecda'))) @@ -732,7 +726,6 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): dup = self.s.copy() self.assertEqual(id(self.s), id(dup)) - @unittest.skip("TODO: RUSTPYTHON") def test_frozen_as_dictkey(self): seq = list(range(10)) + list('abcdefg') + ['apple'] key1 = self.thetype(seq) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 1483a8a3d..369af668d 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -9,6 +9,7 @@ use super::objlist::PyListIterator; use super::objtype::{self, PyClassRef}; use crate::dictdatatype; use crate::function::{Args, OptionalArg}; +use crate::pyhash; use crate::pyobject::{ PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, @@ -206,8 +207,11 @@ impl PySetInner { fn symmetric_difference(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { let mut new_inner = self.clone(); - for item in other.iter(vm)? { - new_inner.content.delete_or_insert(vm, &item?, ())? + // We want to remove duplicates in other + let other_set = Self::new(other, vm)?; + + for item in other_set.content.keys() { + new_inner.content.delete_or_insert(vm, &item, ())? } Ok(new_inner) @@ -294,7 +298,7 @@ impl PySetInner { others: Args, vm: &VirtualMachine, ) -> PyResult<()> { - let temp_inner = self.copy(); + let mut temp_inner = self.copy(); self.clear(); for iterable in others { for item in iterable.iter(vm)? { @@ -303,6 +307,7 @@ impl PySetInner { self.add(&obj, vm)?; } } + temp_inner = self.copy() } Ok(()) } @@ -322,12 +327,18 @@ impl PySetInner { vm: &VirtualMachine, ) -> PyResult<()> { for iterable in others { - for item in iterable.iter(vm)? { - self.content.delete_or_insert(vm, &item?, ())?; + // We want to remove duplicates in iterable + let iterable_set = Self::new(iterable, vm)?; + for item in iterable_set.content.keys() { + self.content.delete_or_insert(vm, &item, ())?; } } Ok(()) } + + fn hash(&self, vm: &VirtualMachine) -> PyResult { + pyhash::hash_iter_unordered(self.content.keys(), vm) + } } macro_rules! try_set_cmp { @@ -780,6 +791,11 @@ impl PyFrozenSet { }; Ok(vm.new_str(s)) } + + #[pymethod(name = "__hash__")] + fn hash(&self, vm: &VirtualMachine) -> PyResult { + self.inner.hash(vm) + } } struct SetIterable { diff --git a/vm/src/pyhash.rs b/vm/src/pyhash.rs index 83776bd58..03b912b9f 100644 --- a/vm/src/pyhash.rs +++ b/vm/src/pyhash.rs @@ -84,6 +84,19 @@ pub fn hash_iter<'a, I: std::iter::Iterator>( Ok(hasher.finish() as PyHash) } +pub fn hash_iter_unordered>( + iter: I, + vm: &VirtualMachine, +) -> PyResult { + let mut hash: PyHash = 0; + for element in iter { + let item_hash = vm._hash(&element)?; + // xor is commutative and hash should be independent of order + hash ^= item_hash; + } + Ok(hash) +} + pub fn hash_bigint(value: &BigInt) -> PyHash { match value.to_i64() { Some(i64_value) => (i64_value % MODULUS as i64),