diff --git a/lib/compat.py b/lib/compat.py index 6515af1a0f00b62d062a17fc73533285e6134046..5f1409e34f10ad9d2a46ff29a20ef56f91e2bb51 100644 --- a/lib/compat.py +++ b/lib/compat.py @@ -146,6 +146,26 @@ def TryToRoman(val, convert=True): else: return val + +def UniqueFrozenset(seq): + """Makes C{frozenset} from sequence after checking for duplicate elements. + + @raise ValueError: When there are duplicate elements + + """ + if isinstance(seq, (list, tuple)): + items = seq + else: + items = list(seq) + + result = frozenset(items) + + if len(items) != len(result): + raise ValueError("Duplicate values found") + + return result + + #: returns the first element of a list-like value fst = operator.itemgetter(0) diff --git a/test/ganeti.compat_unittest.py b/test/ganeti.compat_unittest.py index 54a1c626f93ca9139b9d368a1028cf4fd7426af1..8ed54fe31f6ae775262b69069ae1f5e55a624362 100755 --- a/test/ganeti.compat_unittest.py +++ b/test/ganeti.compat_unittest.py @@ -99,5 +99,24 @@ class TestTryToRoman(testutils.GanetiTestCase): self.assertEquals(compat.TryToRoman("19", convert=False), "19") +class TestUniqueFrozenset(unittest.TestCase): + def testDuplicates(self): + for values in [["", ""], ["Hello", "World", "Hello"]]: + self.assertRaises(ValueError, compat.UniqueFrozenset, values) + + def testEmpty(self): + self.assertEqual(compat.UniqueFrozenset([]), frozenset([])) + + def testUnique(self): + self.assertEqual(compat.UniqueFrozenset([1, 2, 3]), frozenset([1, 2, 3])) + + def testGenerator(self): + seq = ("Foo%s" % i for i in range(10)) + self.assertTrue(callable(seq.next)) + self.assertFalse(isinstance(seq, (list, tuple))) + self.assertEqual(compat.UniqueFrozenset(seq), + frozenset(["Foo%s" % i for i in range(10)])) + + if __name__ == "__main__": testutils.GanetiTestProgram()