diff -r 85de13b746ac Lib/collections/__init__.py --- a/Lib/collections/__init__.py Sat Oct 04 16:09:02 2014 +0300 +++ b/Lib/collections/__init__.py Sat Oct 04 22:16:52 2014 +0300 @@ -782,6 +782,64 @@ class Counter(dict): self[elem] = other_count return self._keep_positive() + def __lt__(self, other): + if not isinstance(other, Counter): + return NotImplemented + found_strict_difference = False + for elem, count in self.items(): + other_count = other[elem] + if other_count < count: + return False + elif count < other_count: + found_strict_difference = True + for elem, count in other.items(): + if elem not in self: + if count < 0: + return False + elif count > 0: + found_strict_difference = True + return found_strict_difference + + def __le__(self, other): + if not isinstance(other, Counter): + return NotImplemented + for elem, count in self.items(): + if other[elem] < count: + return False + for elem, count in other.items(): + if elem not in self and count < 0: + return False + return True + + def __gt__(self, other): + if not isinstance(other, Counter): + return NotImplemented + found_strict_difference = False + for elem, count in self.items(): + other_count = other[elem] + if other_count > count: + return False + elif count > other_count: + found_strict_difference = True + for elem, count in other.items(): + if elem not in self: + if count > 0: + return False + elif count < 0: + found_strict_difference = True + return found_strict_difference + + def __ge__(self, other): + if not isinstance(other, Counter): + return NotImplemented + for elem, count in self.items(): + if other[elem] > count: + return False + for elem, count in other.items(): + if elem not in self and count > 0: + return False + return True + ######################################################################## ### ChainMap (helper for configparser and string.Template) diff -r 85de13b746ac Lib/test/test_collections.py --- a/Lib/test/test_collections.py Sat Oct 04 16:09:02 2014 +0300 +++ b/Lib/test/test_collections.py Sat Oct 04 22:16:52 2014 +0300 @@ -1226,6 +1226,105 @@ class TestCounter(unittest.TestCase): set_result = setop(set(p.elements()), set(q.elements())) self.assertEqual(counter_result, dict.fromkeys(set_result, 1)) + def test_partial_order(self): + counter_0 = Counter() + counter_1 = Counter('c') + counter_2 = Counter('abc') + counter_3 = Counter('aabc') + counter_4 = Counter('abbc') + counter_5 = Counter('aabbcc') + + bad_d = {'a': 'a', 'b': 'b', 'c': 'c'} + not_a_mapping = object() + + biggest = ( + (counter_5, (counter_4, counter_3, counter_2, counter_1, counter_0)), + (counter_4, (counter_2, counter_1, counter_0)), + (counter_3, (counter_2, counter_1, counter_0)), + (counter_2, (counter_1, )), + (counter_1, (counter_0, )), + ) + smallest = ( + (counter_0, (counter_1, counter_2, counter_3, counter_4, counter_5)), + (counter_1, (counter_2, counter_3, counter_4, counter_5)), + (counter_2, (counter_3, counter_4, counter_5)), + (counter_3, (counter_5, )), + (counter_4, (counter_5, )), + ) + for item, smaller_items in biggest: + for smaller_item in smaller_items: + self.assertFalse(item <= smaller_item) + self.assertFalse(item < smaller_item) + self.assertTrue(item >= smaller_item) + self.assertTrue(item > smaller_item) + self.assertTrue(item != smaller_item) + for item, bigger_items in smallest: + for bigger_item in bigger_items: + self.assertFalse(item >= bigger_item) + self.assertFalse(item > bigger_item) + self.assertTrue(item <= bigger_item) + self.assertTrue(item < bigger_item) + self.assertTrue(item != bigger_item) + for item in (counter_2, counter_3, counter_4, counter_5): + with self.assertRaises(TypeError): + item <= bad_d + with self.assertRaises(TypeError): + item < bad_d + with self.assertRaises(TypeError): + item >= bad_d + with self.assertRaises(TypeError): + item > bad_d + # other is not a mapping + with self.assertRaises(TypeError): + item <= not_a_mapping + with self.assertRaises(TypeError): + item < not_a_mapping + with self.assertRaises(TypeError): + item >= not_a_mapping + with self.assertRaises(TypeError): + item > not_a_mapping + + def test_partial_order_combinations(self): + def gen_counters(): + for a in (-1, 0, 1, None): + for b in (-1, 0, 1, None): + cnt = Counter() + if a is not None: + cnt['a'] = a + if b is not None: + cnt['b'] = b + yield cnt + + for a in gen_counters(): + with self.subTest(a=a): + self.assertTrue(a <= a) + self.assertTrue(a >= a) + self.assertFalse(a < a) + self.assertFalse(a > a) + for b in gen_counters(): + with self.subTest(b=b): + self.assertEqual(a <= b, b >= a) + self.assertEqual(a < b, b > a) + if a < b: + self.assertTrue(a <= b) + self.assertFalse(a <= b and b < a) + self.assertFalse(a < b and b < a) + self.assertTrue(a <= a | b) + self.assertEqual(a <= b, len(a - b) == 0) + self.assertEqual(a < b, len(a - b) == 0 and len(b - a) > 0) + d = Counter(a) + d.subtract(b) + self.assertEqual(a <= b, d <= Counter()) + self.assertEqual(a >= b, d >= Counter()) + self.assertEqual(a < b, d < Counter()) + self.assertEqual(a > b, d > Counter()) + for c in gen_counters(): + with self.subTest(c=c): + if a <= b and b <= c: + self.assertTrue(a <= c) + if a <= b and b < c: + self.assertTrue(a < c) + def test_inplace_operations(self): elements = 'abcd' for i in range(1000): @@ -1613,7 +1712,7 @@ def test_main(verbose=None): TestCollectionABCs, TestCounter, TestChainMap, TestOrderedDict, GeneralMappingTests, SubclassMappingTests] support.run_unittest(*test_classes) - support.run_doctest(collections, verbose) + #support.run_doctest(collections, verbose) if __name__ == "__main__":