From d6dc5f1b4de6ffa67f16b2153a6504144d2f6685 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Thu, 24 Aug 2017 11:12:34 -0400 Subject: [PATCH 01/21] bpo-12067: Add tests to test_compare --- Lib/test/test_compare.py | 667 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 641 insertions(+), 26 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 471c8dae767a5c..5a673eb913f848 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -1,27 +1,40 @@ +""" +Test equality and order comparisons. +""" + + import unittest +from fractions import Fraction +from decimal import Decimal + + +class ComparisonSimpleTest(unittest.TestCase): + """ + A testcase that verifies the behavior of equality and order comparisons for + some simple cases. + """ -class Empty: - def __repr__(self): - return '' + class Empty: + def __repr__(self): + return '' -class Cmp: - def __init__(self,arg): - self.arg = arg + class Cmp: + def __init__(self, arg): + self.arg = arg - def __repr__(self): - return '' % self.arg + def __repr__(self): + return '' % self.arg - def __eq__(self, other): - return self.arg == other + def __eq__(self, other): + return self.arg == other -class Anything: - def __eq__(self, other): - return True + class Anything: + def __eq__(self, other): + return True - def __ne__(self, other): - return False + def __ne__(self, other): + return False -class ComparisonTest(unittest.TestCase): set1 = [2, 2.0, 2, 2+0j, Cmp(2.0)] set2 = [[1], (3,), None, Empty()] candidates = set1 + set2 @@ -38,16 +51,15 @@ def test_id_comparisons(self): # Ensure default comparison compares id() of args L = [] for i in range(10): - L.insert(len(L)//2, Empty()) + L.insert(len(L)//2, self.Empty()) for a in L: for b in L: - self.assertEqual(a == b, id(a) == id(b), - 'a=%r, b=%r' % (a, b)) + self.assertEqual(a == b, a is b, 'a=%r, b=%r' % (a, b)) def test_ne_defaults_to_not_eq(self): - a = Cmp(1) - b = Cmp(1) - c = Cmp(2) + a = self.Cmp(1) + b = self.Cmp(1) + c = self.Cmp(2) self.assertIs(a == b, True) self.assertIs(a != b, False) self.assertIs(a != c, True) @@ -113,11 +125,614 @@ class C: def test_issue_1393(self): x = lambda: None - self.assertEqual(x, Anything()) - self.assertEqual(Anything(), x) + self.assertEqual(x, self.Anything()) + self.assertEqual(self.Anything(), x) y = object() - self.assertEqual(y, Anything()) - self.assertEqual(Anything(), y) + self.assertEqual(y, self.Anything()) + self.assertEqual(self.Anything(), y) + + +class ComparisonFullTest(unittest.TestCase): + """ + A testcase that verifies the behavior of equality and ordering + comparisons for built-in types and user-defined classes that implement + relevant combinations of rich comparison methods. + """ + + class CompBase: + """ Base class for classes with rich comparison methods. + + The "x" attribute should be set to an underlying value to compare. + + Derived classes have a "meth" tuple attribute listing names of + comparison methods implemented. See assert_total_order(). + """ + + # Class without any rich comparison methods. + class CompNone(CompBase): + meth = () + + # Classes with all combinations of value-based equality comparison methods. + class CompEq(CompBase): + meth = ("eq",) + def __eq__(self, other): + return self.x == other.x + + class CompNe(CompBase): + meth = ("ne",) + def __ne__(self, other): + return self.x != other.x + + class CompEqNe(CompBase): + meth = ("eq", "ne") + def __eq__(self, other): + return self.x == other.x + def __ne__(self, other): + return self.x != other.x + + # Classes with all combinations of value-based less/greater-than order + # comparison methods. + class CompLt(CompBase): + meth = ("lt",) + def __lt__(self, other): + return self.x < other.x + + class CompGt(CompBase): + meth = ("gt",) + def __gt__(self, other): + return self.x > other.x + + class CompLtGt(CompBase): + meth = ("lt", "gt") + def __lt__(self, other): + return self.x < other.x + def __gt__(self, other): + return self.x > other.x + + # Classes with all combinations of value-based less/greater-or-equal-than + # order comparison methods + class CompLe(CompBase): + meth = ("le",) + def __le__(self, other): + return self.x <= other.x + + class CompGe(CompBase): + meth = ("ge",) + def __ge__(self, other): + return self.x >= other.x + + class CompLeGe(CompBase): + meth = ("le", "ge") + def __le__(self, other): + return self.x <= other.x + def __ge__(self, other): + return self.x >= other.x + + # It should be sufficient to combine the comparison methods only within + # each group: + all_comp_classes = (CompNone, + CompEq, CompNe, CompEqNe, # equal group + CompLt, CompGt, CompLtGt, # less/greater-than group + CompLe, CompGe, CompLeGe) # less/greater-or-equal group + + def create_sorted_insts(self, class_, values): + """ + Create a number of objects of type `class_` and return them in a list. + + `values` is a list of values that determines the value of data + attribute `x` of each object. + + The main feature of this function is that the objects in the result + list are sorted by their identity, and afterwards the values of the + `values` list are assigned to these objects. Testcases can utilize this + to assign decreasing values to objects with increasing identities, + which in turn allows asserting that order comparison is performed by + value and not by identity. + """ + + # Create a list of instances with the default constructor. + insts = [class_() for __ in range(len(values))] + + # Sort the instance list by identity. + insts.sort(key=id) + + # Assign the provided values to the instances. + for i, inst in enumerate(insts): + inst.x = values[i] + + return insts + + def assert_equality_only(self, a, b, equal): + """ Assert equality result and that ordering is not implemented. + + a, b: Instances to be tested (of same or different type). + + equal: Boolean indicating the expected equality comparison result: + True means: a == b + False means: a != b + """ + self.assertEqual(a == b, equal) + self.assertEqual(b == a, equal) + self.assertEqual(a != b, not equal) + self.assertEqual(b != a, not equal) + with self.assertRaisesRegex(TypeError, "not supported"): + a < b + with self.assertRaisesRegex(TypeError, "not supported"): + a <= b + with self.assertRaisesRegex(TypeError, "not supported"): + a > b + with self.assertRaisesRegex(TypeError, "not supported"): + a >= b + with self.assertRaisesRegex(TypeError, "not supported"): + b < a + with self.assertRaisesRegex(TypeError, "not supported"): + b <= a + with self.assertRaisesRegex(TypeError, "not supported"): + b > a + with self.assertRaisesRegex(TypeError, "not supported"): + b >= a + + def assert_total_order(self, a, b, comp, a_meth=None, b_meth=None): + """ + Perform assertions on total ordering comparison of two instances. + + This function implements the knowledge about how equality and ordering + comparisons are supposed to work. + + a, b: Instances to be tested (of same or different type). + + comp: Integer indicating the expected order comparison result, + for operations that are supported by the classes: + <0 means: a < b + 0 means: a == b + >0 means: a > b + + a_meth, b_meth: Tuple of rich comparison method names (without + leading and trailing underscores) that are expected to be + available for the corresponding instance. This information + is only needed for instances of user-defined classes, + when some operations are not implemented. + Possible values for the tuple items are: + "eq", "ne", "lt", "le", "gt", "ge". + """ + + args = dict(a=a, b=b, comp=comp, a_meth=a_meth, b_meth=b_meth) + self.assert_eq_comparison_subtest(**args) + self.assert_ne_comparison_subtest(**args) + self.assert_lt_comparison_subtest(**args) + self.assert_le_comparison_subtest(**args) + self.assert_gt_comparison_subtest(**args) + self.assert_ge_comparison_subtest(**args) + + def assert_eq_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + """ Test "==" comparison. + The comparison is performed in both directions of the operands. + """ + + if a_meth is None or "eq" in a_meth or "eq" in b_meth: + # There are value-based comparison methods; + # we expect what the testcase defined. + self.assertEqual(a == b, comp == 0) + self.assertEqual(b == a, comp == 0) + else: + # There are no value-based comparison methods; + # we expect the default behavior of object. + self.assertEqual(a == b, a is b) + self.assertEqual(b == a, a is b) + + def assert_ne_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + """ Test "!=" comparison. + The comparison is performed in both directions of the operands. + """ + + if a_meth is None or not {"ne", "eq"}.isdisjoint(a_meth + b_meth): + # There are value-based comparison methods; + # we expect what the testcase defined. + self.assertEqual(a != b, comp != 0) + self.assertEqual(b != a, comp != 0) + else: + # There are no value-based comparison methods; + # we expect the default behavior of object. + self.assertEqual(a != b, a is not b) + self.assertEqual(b != a, a is not b) + + def assert_lt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + """ Test "<" comparison. + The comparison is performed in both directions of the operands. + """ + + if a_meth is None or "lt" in a_meth or "gt" in b_meth: + # There are value-based comparison methods; + # we expect what the testcase defined. + self.assertEqual(a < b, comp < 0) + self.assertEqual(b > a, comp < 0) + else: + # There are no value-based comparison methods; + # we expect the default behavior of object. + with self.assertRaisesRegex(TypeError, "not supported"): + a < b + with self.assertRaisesRegex(TypeError, "not supported"): + b > a + + def assert_le_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + """ Test "<=" comparison. + The comparison is performed in both directions of the operands. + """ + + if a_meth is None or "le" in a_meth or "ge" in b_meth: + # There are value-based comparison methods; + # we expect what the testcase defined. + self.assertEqual(a <= b, comp <= 0) + self.assertEqual(b >= a, comp <= 0) + else: + # There are no value-based comparison methods; + # we expect the default behavior of object. + with self.assertRaisesRegex(TypeError, "not supported"): + a <= b + with self.assertRaisesRegex(TypeError, "not supported"): + b >= a + + def assert_gt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + """ Test ">" comparison. + The comparison is performed in both directions of the operands. + """ + + if a_meth is None or "gt" in a_meth or "lt" in b_meth: + # There are value-based comparison methods; + # we expect what the testcase defined. + self.assertEqual(a > b, comp > 0) + self.assertEqual(b < a, comp > 0) + else: + # There are no value-based comparison methods; + # we expect the default behavior of object. + with self.assertRaisesRegex(TypeError, "not supported"): + a > b + with self.assertRaisesRegex(TypeError, "not supported"): + b < a + + def assert_ge_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + """ Test ">=" comparison. + The comparison is performed in both directions of the operands. + """ + + if a_meth is None or "ge" in a_meth or "le" in b_meth: + # There are value-based comparison methods; + # we expect what the testcase defined. + self.assertEqual(a >= b, comp >= 0) + self.assertEqual(b <= a, comp >= 0) + else: + # There are no value-based comparison methods; + # we expect the default behavior of object. + with self.assertRaisesRegex(TypeError, "not supported"): + a >= b + with self.assertRaisesRegex(TypeError, "not supported"): + b <= a + + def test_objects(self): + """ Test comparison for two instances of type 'object'. + """ + a = object() + b = object() + + self.assert_equality_only(a, a, True) + self.assert_equality_only(a, b, False) + + def test_comp_classes(self): + """ + Test comparison for instances that have different combinations of + comparison methods implemented. + + This test function tests all combinations of a set of classes, so that + instances are sometimes of different classes and sometimes of the same + class. + """ + + # Comparisons of objects of the same class. + for cls in self.all_comp_classes: + with self.subTest(cls): + insts = self.create_sorted_insts(cls, (1, 2, 1)) + + # same object + self.assert_total_order(insts[0], insts[0], 0, + cls.meth, cls.meth) + + # different objects, same value + self.assert_total_order(insts[0], insts[2], 0, + cls.meth, cls.meth) + + # different objects, value ascending for ascending identities + self.assert_total_order(insts[0], insts[1], -1, + cls.meth, cls.meth) + + # different objects, value descending for ascending identities. + # This is the interesting case to assert that order comparison + # is performed based on the value and not based on the identity. + self.assert_total_order(insts[1], insts[2], +1, + cls.meth, cls.meth) + + # Comparisons of objects of the combination of all classes. + for cls_a in self.all_comp_classes: + for cls_b in self.all_comp_classes: + with self.subTest(a=cls_a, b=cls_b): + a1 = cls_a() + a1.x = 1 + b1 = cls_b() + b1.x = 1 + b2 = cls_b() + b2.x = 2 + + # Different objects, same value. + self.assert_total_order( + a1, b1, 0, cls_a.meth, cls_b.meth) + + self.assert_total_order( + a1, b2, -1, cls_a.meth, cls_b.meth) + + def test_str_subclass(self): + """ Test comparison for type ``str`` and its subclass ``StrSubclass``. + """ + class StrSubclass(str): + pass + + c1 = StrSubclass("a") + c2 = StrSubclass("b") + c3 = StrSubclass("b") # Different instance than c2. + self.assertIsNot(c2, c3, "Testcase error: c2 is c3") + + s1 = str("a") + s2 = str("b") + s3 = str("b") # Same instance as s2. + self.assertIs(s2, s3, "Testcase error: s2 is not s3") + + self.assert_total_order(c1, c1, 0) + self.assert_total_order(c1, c2, -1) + self.assert_total_order(c2, c3, 0) + + self.assert_total_order(s1, s1, 0) + self.assert_total_order(s1, s2, -1) + self.assert_total_order(s2, s3, 0) + + self.assert_total_order(c1, s2, -1) + self.assert_total_order(c2, s3, 0) + + self.assert_total_order(s1, c2, -1) + self.assert_total_order(s2, c3, 0) + + def test_numbers(self): + """ Test comparison for number types. + """ + i1 = int(10001) + i2 = int(10002) + i3 = i1 + 1 # Same value, different instance than i2. + i4 = i2 - 1 # Same value, different instance than i1. + i5 = 42 + self.assertIsNot(i2, i3, "Testcase error: i2 is i3") + self.assertIsNot(i1, i4, "Testcase error: i1 is i4") + + f1 = 1.1 + f2 = 2.1 + f3 = f1 + 1 # Same value, different instance than f2. + f4 = f2 - 1 # Same value, different instance than f1. + f5 = 42.0 + self.assertIsNot(f2, f3, "Testcase error: f2 is f3") + self.assertIsNot(f1, f4, "Testcase error: f1 is f4") + + c1 = 1+1j + c2 = 2+2j + c3 = 2+2j # Same value, different instance than c2. + c4 = 1+1j # Same value, different instance than c1. + c5 = 42+0j + self.assertIsNot(c2, c3, "Testcase error: c2 is c3") + self.assertIsNot(c1, c4, "Testcase error: c1 is c4") + + q1 = Fraction(1, 2) + q2 = Fraction(2, 3) + q3 = Fraction(2, 3) # Same value, different instance than q2. + q4 = Fraction(1, 2) # Same value, different instance than q1. + q5 = Fraction(84, 2) + self.assertIsNot(q2, q3, "Testcase error: q2 is q3") + self.assertIsNot(q1, q4, "Testcase error: q1 is q4") + + d1 = Decimal('1.2') + d2 = Decimal('2.3') + d3 = Decimal('2.3') # Same value, different instance than d2. + d4 = Decimal('1.2') # Same value, different instance than d1. + d5 = Decimal('42.0') + self.assertIsNot(d2, d3, "Testcase error: d2 is d3") + self.assertIsNot(d1, d4, "Testcase error: d1 is d4") + + # Same types. + self.assert_total_order(i1, i1, 0) + self.assert_total_order(i1, i2, -1) + self.assert_total_order(i2, i3, 0) + self.assert_total_order(i3, i4, +1) + + self.assert_total_order(f1, f1, 0) + self.assert_total_order(f1, f2, -1) + self.assert_total_order(f2, f3, 0) + self.assert_total_order(f3, f4, +1) + + self.assert_equality_only(c1, c1, True) + self.assert_equality_only(c1, c2, False) + self.assert_equality_only(c2, c3, True) + self.assert_equality_only(c3, c4, False) + + self.assert_total_order(q1, q1, 0) + self.assert_total_order(q1, q2, -1) + self.assert_total_order(q2, q3, 0) + self.assert_total_order(q3, q4, +1) + + self.assert_total_order(d1, d1, 0) + self.assert_total_order(d1, d2, -1) + self.assert_total_order(d2, d3, 0) + self.assert_total_order(d3, d4, +1) + + # Mixing types. + self.assert_total_order(i5, f5, 0) + self.assert_equality_only(i5, c5, True) + self.assert_total_order(i5, q5, 0) + self.assert_total_order(i5, d5, 0) + + self.assert_equality_only(f5, c5, True) + self.assert_total_order(f5, q5, 0) + self.assert_total_order(f5, d5, 0) + + self.assert_equality_only(c5, q5, True) + self.assert_equality_only(c5, d5, True) + + self.assert_total_order(q5, d5, 0) + + self.assert_total_order(i1, f1, +1) + self.assert_equality_only(i1, c1, False) + self.assert_total_order(i1, q1, +1) + self.assert_total_order(i1, d1, +1) + + self.assert_equality_only(f1, c1, False) + self.assert_total_order(f1, q1, +1) + self.assert_total_order(f1, d1, -1) + + self.assert_equality_only(c1, q1, False) + self.assert_equality_only(c1, d1, False) + + self.assert_total_order(q1, d1, -1) + + def test_sequences(self): + """ Test comparison for sequences (list, tuple, range). + """ + l1 = [1, 2] + l2 = [2, 3] + l3 = [2, 3] # Same value, different instance than l2. + l4 = [1, 2] # Same value, different instance than l1. + self.assertIsNot(l2, l3, "Testcase error: l2 is l3") + self.assertIsNot(l1, l4, "Testcase error: l1 is l4") + + t1 = (1, 2) + t2 = (2, 3) + t3 = (2, 3) # Same value, different instance than t2. + t4 = (1, 2) # Same value, different instance than t1. + self.assertIsNot(t2, t3, "Testcase error: t2 is t3") + self.assertIsNot(t1, t4, "Testcase error: t1 is t4") + + r1 = range(1, 2) + r2 = range(2, 2) + r3 = range(2, 2) # Same value, different instance than r2. + r4 = range(1, 2) # Same value, different instance than r1. + self.assertIsNot(r2, r3, "Testcase error: r2 is r3") + self.assertIsNot(r1, r4, "Testcase error: r1 is r4") + + # Same types. + self.assert_total_order(t1, t1, 0) + self.assert_total_order(t1, t2, -1) + self.assert_total_order(t2, t3, 0) + self.assert_total_order(t3, t4, +1) + + self.assert_total_order(l1, l1, 0) + self.assert_total_order(l1, l2, -1) + self.assert_total_order(l2, l3, 0) + self.assert_total_order(l3, l4, +1) + + self.assert_equality_only(r1, r1, True) + self.assert_equality_only(r1, r2, False) + self.assert_equality_only(r2, r3, True) + self.assert_equality_only(r3, r4, False) + + # Mixing types. + self.assert_equality_only(t1, l1, False) + self.assert_equality_only(l1, r1, False) + self.assert_equality_only(r1, t1, False) + + def test_binary_sequences(self): + """ Test comparison for binary sequences (bytes, bytearray). + """ + bs1 = b'a1' + bs2 = b'b2' + bs3 = b'b' + b'2' # Same value, different instance than bs2. + bs4 = b'a' + b'1' # Same value, different instance than bs1. + self.assertIsNot(bs2, bs3, "Testcase error: bs2 is bs3") + self.assertIsNot(bs1, bs4, "Testcase error: bs1 is bs4") + + ba1 = bytearray(b'a1') + ba2 = bytearray(b'b2') + ba3 = bytearray(b'b2') # Same value, different instance than ba2. + ba4 = bytearray(b'a1') # Same value, different instance than ba1. + self.assertIsNot(ba2, ba3, "Testcase error: ba2 is ba3") + self.assertIsNot(ba1, ba4, "Testcase error: ba1 is ba4") + + # Same types. + self.assert_total_order(bs1, bs1, 0) + self.assert_total_order(bs1, bs2, -1) + self.assert_total_order(bs2, bs3, 0) + self.assert_total_order(bs3, bs4, +1) + + self.assert_total_order(ba1, ba1, 0) + self.assert_total_order(ba1, ba2, -1) + self.assert_total_order(ba2, ba3, 0) + self.assert_total_order(ba3, ba4, +1) + + # Mixing types. + self.assert_total_order(bs1, ba1, 0) + self.assert_total_order(bs1, ba2, -1) + self.assert_total_order(bs2, ba3, 0) + self.assert_total_order(bs3, ba4, +1) + + self.assert_total_order(ba1, bs1, 0) + self.assert_total_order(ba1, bs2, -1) + self.assert_total_order(ba2, bs3, 0) + self.assert_total_order(ba3, bs4, +1) + + def test_sets(self): + """ Test comparison for sets (set, frozenset). + """ + s1 = {1, 2} + s2 = {1, 2, 3} + s3 = {1, 2, 3} # Same value, different instance than s2. + s4 = {1, 2} # Same value, different instance than s1. + self.assertIsNot(s2, s3, "Testcase error: s2 is s3") + self.assertIsNot(s1, s4, "Testcase error: s1 is s4") + + f1 = frozenset({1, 2}) + f2 = frozenset({1, 2, 3}) + f3 = frozenset({1, 2, 3}) # Same value, different instance than f2. + f4 = frozenset({1, 2}) # Same value, different instance than f1. + self.assertIsNot(f2, f3, "Testcase error: f2 is f3") + self.assertIsNot(f1, f4, "Testcase error: f1 is f4") + + # Same types. + self.assert_total_order(s1, s1, 0) + self.assert_total_order(s1, s2, -1) + self.assert_total_order(s2, s3, 0) + self.assert_total_order(s3, s4, +1) + + self.assert_total_order(f1, f1, 0) + self.assert_total_order(f1, f2, -1) + self.assert_total_order(f2, f3, 0) + self.assert_total_order(f3, f4, +1) + + # Mixing types. + self.assert_total_order(s1, f1, 0) + self.assert_total_order(s1, f2, -1) + self.assert_total_order(s2, f3, 0) + self.assert_total_order(s3, f4, +1) + + self.assert_total_order(f1, s1, 0) + self.assert_total_order(f1, s2, -1) + self.assert_total_order(f2, s3, 0) + self.assert_total_order(f3, s4, +1) + + def test_mappings(self): + """ Test comparison for mappings (dict). + """ + d1 = {1: "a", 2: "b"} + d2 = {2: "b", 3: "c"} + d3 = {3: "c", 2: "b"} # Same value, different instance than d2. + d4 = {1: "a", 2: "b"} # Same value, different instance than d1. + self.assertIsNot(d2, d3, "Testcase error: d2 is d3") + self.assertIsNot(d1, d4, "Testcase error: d1 is d4") + + self.assert_equality_only(d1, d1, True) + self.assert_equality_only(d1, d2, False) + self.assert_equality_only(d2, d3, True) + self.assert_equality_only(d3, d4, False) if __name__ == '__main__': From 8811145e3b035204f38b7e8cf59eab329fa8b1a7 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 25 Jan 2022 19:59:14 -0500 Subject: [PATCH 02/21] From test_compare part of PR-15167. From test_compare part of PR-15167. --- Lib/test/test_compare.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 5a673eb913f848..4d9d852efa51e4 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -4,6 +4,7 @@ import unittest +from test.support import ALWAYS_EQ from fractions import Fraction from decimal import Decimal From 33e7bc80c2dd433ffa1284adaf3ef1ca0e50d448 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 25 Jan 2022 20:04:10 -0500 Subject: [PATCH 03/21] @terryjreedy From test_compare part of PR-15167. --- Lib/test/test_compare.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 4d9d852efa51e4..957a68afe73b95 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -29,9 +29,6 @@ def __repr__(self): def __eq__(self, other): return self.arg == other - class Anything: - def __eq__(self, other): - return True def __ne__(self, other): return False From 18a8e8a2d973dec3a085ef3b690c5d938a5687f7 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 25 Jan 2022 20:04:54 -0500 Subject: [PATCH 04/21] @terryjreedy From test_compare part of PR-15167. --- Lib/test/test_compare.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 957a68afe73b95..60882e8f6bc5ed 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -30,8 +30,6 @@ def __eq__(self, other): return self.arg == other - def __ne__(self, other): - return False set1 = [2, 2.0, 2, 2+0j, Cmp(2.0)] set2 = [[1], (3,), None, Empty()] From fe1ead6c06ec405c89e071cf92d9f0c19cf4760c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 25 Jan 2022 20:08:02 -0500 Subject: [PATCH 05/21] @terryjreedy From test_compare part of PR-15167. --- Lib/test/test_compare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 60882e8f6bc5ed..53be368791be5f 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -121,8 +121,8 @@ class C: def test_issue_1393(self): x = lambda: None - self.assertEqual(x, self.Anything()) - self.assertEqual(self.Anything(), x) + self.assertEqual(x, ALWAYS_EQ) + self.assertEqual(ALWAYS_EQ, x) y = object() self.assertEqual(y, self.Anything()) self.assertEqual(self.Anything(), y) From 9021b89f5fdf45a8c975911cda06b1844de75175 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 25 Jan 2022 20:08:16 -0500 Subject: [PATCH 06/21] @terryjreedy From test_compare part of PR-15167. --- Lib/test/test_compare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 53be368791be5f..dc16268c93c755 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -124,8 +124,8 @@ def test_issue_1393(self): self.assertEqual(x, ALWAYS_EQ) self.assertEqual(ALWAYS_EQ, x) y = object() - self.assertEqual(y, self.Anything()) - self.assertEqual(self.Anything(), y) + self.assertEqual(y, ALWAYS_EQ) + self.assertEqual(ALWAYS_EQ, y) class ComparisonFullTest(unittest.TestCase): From 4e82f2569db24d7ba7eb0eb4c970ca9449656964 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 01:01:59 -0400 Subject: [PATCH 07/21] Replace 'insts' with 'instances'. --- Lib/test/test_compare.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 701e0b9d0fa299..1e7b595496f96e 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -206,7 +206,7 @@ def __ge__(self, other): CompLt, CompGt, CompLtGt, # less/greater-than group CompLe, CompGe, CompLeGe) # less/greater-or-equal group - def create_sorted_insts(self, class_, values): + def create_sorted_instances(self, class_, values): """ Create a number of objects of type `class_` and return them in a list. @@ -222,16 +222,16 @@ def create_sorted_insts(self, class_, values): """ # Create a list of instances with the default constructor. - insts = [class_() for __ in range(len(values))] + instances = [class_() for __ in range(len(values))] # Sort the instance list by identity. - insts.sort(key=id) + instances.sort(key=id) # Assign the provided values to the instances. - for i, inst in enumerate(insts): + for i, inst in enumerate(instances): inst.x = values[i] - return insts + return instances def assert_equality_only(self, a, b, equal): """ Assert equality result and that ordering is not implemented. @@ -421,24 +421,24 @@ def test_comp_classes(self): # Comparisons of objects of the same class. for cls in self.all_comp_classes: with self.subTest(cls): - insts = self.create_sorted_insts(cls, (1, 2, 1)) + instances = self.create_sorted_instances(cls, (1, 2, 1)) # same object - self.assert_total_order(insts[0], insts[0], 0, + self.assert_total_order(instances[0], instances[0], 0, cls.meth, cls.meth) # different objects, same value - self.assert_total_order(insts[0], insts[2], 0, + self.assert_total_order(instances[0], instances[2], 0, cls.meth, cls.meth) # different objects, value ascending for ascending identities - self.assert_total_order(insts[0], insts[1], -1, + self.assert_total_order(instances[0], instances[1], -1, cls.meth, cls.meth) # different objects, value descending for ascending identities. # This is the interesting case to assert that order comparison # is performed based on the value and not based on the identity. - self.assert_total_order(insts[1], insts[2], +1, + self.assert_total_order(instances[1], instances[2], +1, cls.meth, cls.meth) # Comparisons of objects of the combination of all classes. From 1ffae6dfbd3e8d10f396b00f5db0a2ab97b2a608 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 01:17:00 -0400 Subject: [PATCH 08/21] Factor repeated comments out of comparison subtests. --- Lib/test/test_compare.py | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 1e7b595496f96e..927ceaa715fc78 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -295,19 +295,20 @@ def assert_total_order(self, a, b, comp, a_meth=None, b_meth=None): self.assert_gt_comparison_subtest(**args) self.assert_ge_comparison_subtest(**args) + # The body of each subtest has form + # if value-based comparison methods: + # expect what the testcase defined; + # else: no value-based comparison + # expect default behavior of object. + def assert_eq_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): """ Test "==" comparison. The comparison is performed in both directions of the operands. """ - if a_meth is None or "eq" in a_meth or "eq" in b_meth: - # There are value-based comparison methods; - # we expect what the testcase defined. self.assertEqual(a == b, comp == 0) self.assertEqual(b == a, comp == 0) else: - # There are no value-based comparison methods; - # we expect the default behavior of object. self.assertEqual(a == b, a is b) self.assertEqual(b == a, a is b) @@ -315,15 +316,10 @@ def assert_ne_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): """ Test "!=" comparison. The comparison is performed in both directions of the operands. """ - if a_meth is None or not {"ne", "eq"}.isdisjoint(a_meth + b_meth): - # There are value-based comparison methods; - # we expect what the testcase defined. self.assertEqual(a != b, comp != 0) self.assertEqual(b != a, comp != 0) else: - # There are no value-based comparison methods; - # we expect the default behavior of object. self.assertEqual(a != b, a is not b) self.assertEqual(b != a, a is not b) @@ -331,15 +327,10 @@ def assert_lt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): """ Test "<" comparison. The comparison is performed in both directions of the operands. """ - if a_meth is None or "lt" in a_meth or "gt" in b_meth: - # There are value-based comparison methods; - # we expect what the testcase defined. self.assertEqual(a < b, comp < 0) self.assertEqual(b > a, comp < 0) else: - # There are no value-based comparison methods; - # we expect the default behavior of object. with self.assertRaisesRegex(TypeError, "not supported"): a < b with self.assertRaisesRegex(TypeError, "not supported"): @@ -349,15 +340,10 @@ def assert_le_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): """ Test "<=" comparison. The comparison is performed in both directions of the operands. """ - if a_meth is None or "le" in a_meth or "ge" in b_meth: - # There are value-based comparison methods; - # we expect what the testcase defined. self.assertEqual(a <= b, comp <= 0) self.assertEqual(b >= a, comp <= 0) else: - # There are no value-based comparison methods; - # we expect the default behavior of object. with self.assertRaisesRegex(TypeError, "not supported"): a <= b with self.assertRaisesRegex(TypeError, "not supported"): @@ -367,15 +353,10 @@ def assert_gt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): """ Test ">" comparison. The comparison is performed in both directions of the operands. """ - if a_meth is None or "gt" in a_meth or "lt" in b_meth: - # There are value-based comparison methods; - # we expect what the testcase defined. self.assertEqual(a > b, comp > 0) self.assertEqual(b < a, comp > 0) else: - # There are no value-based comparison methods; - # we expect the default behavior of object. with self.assertRaisesRegex(TypeError, "not supported"): a > b with self.assertRaisesRegex(TypeError, "not supported"): @@ -385,15 +366,10 @@ def assert_ge_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): """ Test ">=" comparison. The comparison is performed in both directions of the operands. """ - if a_meth is None or "ge" in a_meth or "le" in b_meth: - # There are value-based comparison methods; - # we expect what the testcase defined. self.assertEqual(a >= b, comp >= 0) self.assertEqual(b <= a, comp >= 0) else: - # There are no value-based comparison methods; - # we expect the default behavior of object. with self.assertRaisesRegex(TypeError, "not supported"): a >= b with self.assertRaisesRegex(TypeError, "not supported"): From d763ab36b110bceacd674e36298c87f6f27c8513 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 01:36:12 -0400 Subject: [PATCH 09/21] Edit/condense docstrings/comments; split test_comp_classes. --- Lib/test/test_compare.py | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 927ceaa715fc78..58bff12b76b102 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -200,11 +200,12 @@ def __ge__(self, other): return self.x >= other.x # It should be sufficient to combine the comparison methods only within - # each group: - all_comp_classes = (CompNone, - CompEq, CompNe, CompEqNe, # equal group - CompLt, CompGt, CompLtGt, # less/greater-than group - CompLe, CompGe, CompLeGe) # less/greater-or-equal group + # each group. + all_comp_classes = ( + CompNone, + CompEq, CompNe, CompEqNe, # equal group + CompLt, CompGt, CompLtGt, # less/greater-than group + CompLe, CompGe, CompLeGe) # less/greater-or-equal group def create_sorted_instances(self, class_, values): """ @@ -376,38 +377,28 @@ def assert_ge_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): b <= a def test_objects(self): - """ Test comparison for two instances of type 'object'. - """ + """ Compare instances of type 'object'.""" a = object() b = object() - self.assert_equality_only(a, a, True) self.assert_equality_only(a, b, False) - def test_comp_classes(self): - """ - Test comparison for instances that have different combinations of - comparison methods implemented. + def test_comp_classes_same(self): + """ Compare same-class instances with comparison methods.""" - This test function tests all combinations of a set of classes, so that - instances are sometimes of different classes and sometimes of the same - class. - """ - - # Comparisons of objects of the same class. for cls in self.all_comp_classes: with self.subTest(cls): instances = self.create_sorted_instances(cls, (1, 2, 1)) - # same object + # Same object. self.assert_total_order(instances[0], instances[0], 0, cls.meth, cls.meth) - # different objects, same value + # Different objects, same value. self.assert_total_order(instances[0], instances[2], 0, cls.meth, cls.meth) - # different objects, value ascending for ascending identities + # Different objects, value ascending for ascending identities. self.assert_total_order(instances[0], instances[1], -1, cls.meth, cls.meth) @@ -417,7 +408,9 @@ def test_comp_classes(self): self.assert_total_order(instances[1], instances[2], +1, cls.meth, cls.meth) - # Comparisons of objects of the combination of all classes. + def test_comp_classes_different(self): + """ Compare different-class instances with comparison methods.""" + for cls_a in self.all_comp_classes: for cls_b in self.all_comp_classes: with self.subTest(a=cls_a, b=cls_b): @@ -428,10 +421,8 @@ def test_comp_classes(self): b2 = cls_b() b2.x = 2 - # Different objects, same value. self.assert_total_order( a1, b1, 0, cls_a.meth, cls_b.meth) - self.assert_total_order( a1, b2, -1, cls_a.meth, cls_b.meth) From 0b614eca1b27df585dbc6eb88a2cf79579d7cc76 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 01:53:33 -0400 Subject: [PATCH 10/21] Remove identity claims and tests within test_str_subclass. They passed because they remain correct for CPython and the toy subclass, but are not part of the definition of Python. --- Lib/test/test_compare.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 58bff12b76b102..458d4666ed3474 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -427,34 +427,26 @@ def test_comp_classes_different(self): a1, b2, -1, cls_a.meth, cls_b.meth) def test_str_subclass(self): - """ Test comparison for type ``str`` and its subclass ``StrSubclass``. - """ + """ Compare instances of str and a subclass.""" class StrSubclass(str): pass - c1 = StrSubclass("a") - c2 = StrSubclass("b") - c3 = StrSubclass("b") # Different instance than c2. - self.assertIsNot(c2, c3, "Testcase error: c2 is c3") - s1 = str("a") s2 = str("b") - s3 = str("b") # Same instance as s2. - self.assertIs(s2, s3, "Testcase error: s2 is not s3") + c1 = StrSubclass("a") + c2 = StrSubclass("b") + c3 = StrSubclass("b") + self.assert_total_order(s1, s1, 0) + self.assert_total_order(s1, s2, -1) self.assert_total_order(c1, c1, 0) self.assert_total_order(c1, c2, -1) self.assert_total_order(c2, c3, 0) - self.assert_total_order(s1, s1, 0) - self.assert_total_order(s1, s2, -1) - self.assert_total_order(s2, s3, 0) - - self.assert_total_order(c1, s2, -1) - self.assert_total_order(c2, s3, 0) - self.assert_total_order(s1, c2, -1) self.assert_total_order(s2, c3, 0) + self.assert_total_order(c1, s2, -1) + self.assert_total_order(c2, s2, 0) def test_numbers(self): """ Test comparison for number types. From 9c953e03f053d417e3b1c890492fec86c15b3e4f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 14:01:36 -0400 Subject: [PATCH 11/21] Edit more docstrings, remove duplicate ints. --- Lib/test/test_compare.py | 48 ++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 458d4666ed3474..283c56045039bd 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -1,6 +1,4 @@ -""" -Test equality and order comparisons. -""" +"Test equality and order comparisons." import unittest from test.support import ALWAYS_EQ from fractions import Fraction @@ -8,10 +6,9 @@ class ComparisonSimpleTest(unittest.TestCase): - """ - A testcase that verifies the behavior of equality and order comparisons for - some simple cases. - """ + """Test equality and order comparisons for some simple cases.""" + # TODO Does addition of FullTest below obsolete any of these? + class Empty: def __repr__(self): return '' @@ -124,14 +121,13 @@ def test_issue_1393(self): class ComparisonFullTest(unittest.TestCase): - """ - A testcase that verifies the behavior of equality and ordering - comparisons for built-in types and user-defined classes that implement - relevant combinations of rich comparison methods. + """Test equality and ordering comparisons for built-in types and + user-defined classes that implement relevant combinations of rich + comparison methods. """ class CompBase: - """ Base class for classes with rich comparison methods. + """Base class for classes with rich comparison methods. The "x" attribute should be set to an underlying value to compare. @@ -208,18 +204,15 @@ def __ge__(self, other): CompLe, CompGe, CompLeGe) # less/greater-or-equal group def create_sorted_instances(self, class_, values): - """ - Create a number of objects of type `class_` and return them in a list. + """Create objects of type `class_` and return them in a list. `values` is a list of values that determines the value of data attribute `x` of each object. - The main feature of this function is that the objects in the result - list are sorted by their identity, and afterwards the values of the - `values` list are assigned to these objects. Testcases can utilize this - to assign decreasing values to objects with increasing identities, - which in turn allows asserting that order comparison is performed by - value and not by identity. + Objects in the returned list are sorted by their identity. They + assigned values in `values` list order. By assign decreasing + values to objects with increasing identities, testcases can assert + that order comparison is performed by value and not by identity. """ # Create a list of instances with the default constructor. @@ -449,15 +442,10 @@ class StrSubclass(str): self.assert_total_order(c2, s2, 0) def test_numbers(self): - """ Test comparison for number types. - """ - i1 = int(10001) - i2 = int(10002) - i3 = i1 + 1 # Same value, different instance than i2. - i4 = i2 - 1 # Same value, different instance than i1. - i5 = 42 - self.assertIsNot(i2, i3, "Testcase error: i2 is i3") - self.assertIsNot(i1, i4, "Testcase error: i1 is i4") + """ Compare number types.""" + + i1 = 10001 + i2 = 10002 f1 = 1.1 f2 = 2.1 @@ -494,8 +482,6 @@ def test_numbers(self): # Same types. self.assert_total_order(i1, i1, 0) self.assert_total_order(i1, i2, -1) - self.assert_total_order(i2, i3, 0) - self.assert_total_order(i3, i4, +1) self.assert_total_order(f1, f1, 0) self.assert_total_order(f1, f2, -1) From 6ca8fcb121e9b14145dde2210925eae0bb45a3cf Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 14:04:08 -0400 Subject: [PATCH 12/21] Edit create_sorted_instances. --- Lib/test/test_compare.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 283c56045039bd..40f2da7b6bcf6d 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -215,16 +215,11 @@ def create_sorted_instances(self, class_, values): that order comparison is performed by value and not by identity. """ - # Create a list of instances with the default constructor. instances = [class_() for __ in range(len(values))] - - # Sort the instance list by identity. instances.sort(key=id) - # Assign the provided values to the instances. - for i, inst in enumerate(instances): - inst.x = values[i] - + for inst, value in zip(instances, values): + inst.x = value return instances def assert_equality_only(self, a, b, equal): From 05a195b5336e6fdf92d9bf535c8c6fbde2af29b8 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 14:35:27 -0400 Subject: [PATCH 13/21] Edit assert_total_order and subtests. --- Lib/test/test_compare.py | 60 +++++++++++++++------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 40f2da7b6bcf6d..28f7f0c037ae6d 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -223,13 +223,10 @@ def create_sorted_instances(self, class_, values): return instances def assert_equality_only(self, a, b, equal): - """ Assert equality result and that ordering is not implemented. + """Assert equality result and that ordering is not implemented. a, b: Instances to be tested (of same or different type). - - equal: Boolean indicating the expected equality comparison result: - True means: a == b - False means: a != b + equal: Boolean indicating the expected equality comparison results. """ self.assertEqual(a == b, equal) self.assertEqual(b == a, equal) @@ -253,36 +250,25 @@ def assert_equality_only(self, a, b, equal): b >= a def assert_total_order(self, a, b, comp, a_meth=None, b_meth=None): - """ - Perform assertions on total ordering comparison of two instances. - - This function implements the knowledge about how equality and ordering - comparisons are supposed to work. + """Test total ordering comparison of two instances. a, b: Instances to be tested (of same or different type). - comp: Integer indicating the expected order comparison result, - for operations that are supported by the classes: - <0 means: a < b - 0 means: a == b - >0 means: a > b - - a_meth, b_meth: Tuple of rich comparison method names (without - leading and trailing underscores) that are expected to be - available for the corresponding instance. This information - is only needed for instances of user-defined classes, - when some operations are not implemented. - Possible values for the tuple items are: - "eq", "ne", "lt", "le", "gt", "ge". - """ + comp: -1, 0, or 1 indicates that the expected order comparison + result for operations that are supported by the classes is + a <, ==, or > b. - args = dict(a=a, b=b, comp=comp, a_meth=a_meth, b_meth=b_meth) - self.assert_eq_comparison_subtest(**args) - self.assert_ne_comparison_subtest(**args) - self.assert_lt_comparison_subtest(**args) - self.assert_le_comparison_subtest(**args) - self.assert_gt_comparison_subtest(**args) - self.assert_ge_comparison_subtest(**args) + a_meth, b_meth: Either None, indicating that all rich comparison + methods are available, aa for builtins, or the tuple (subset) + of "eq", "ne", "lt", "le", "gt", and "ge" that are available + for the corresponding instance (of a user-defined class). + """ + self.assert_eq_subtest(a, b, comp, a_meth, b_meth) + self.assert_ne_subtest(a, b, comp, a_meth, b_meth) + self.assert_lt_subtest(a, b, comp, a_meth, b_meth) + self.assert_le_subtest(a, b, comp, a_meth, b_meth) + self.assert_gt_subtest(a, b, comp, a_meth, b_meth) + self.assert_ge_subtest(a, b, comp, a_meth, b_meth) # The body of each subtest has form # if value-based comparison methods: @@ -290,7 +276,7 @@ def assert_total_order(self, a, b, comp, a_meth=None, b_meth=None): # else: no value-based comparison # expect default behavior of object. - def assert_eq_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + def assert_eq_subtest(self, a, b, comp, a_meth, b_meth): """ Test "==" comparison. The comparison is performed in both directions of the operands. """ @@ -301,7 +287,7 @@ def assert_eq_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): self.assertEqual(a == b, a is b) self.assertEqual(b == a, a is b) - def assert_ne_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + def assert_ne_subtest(self, a, b, comp, a_meth, b_meth): """ Test "!=" comparison. The comparison is performed in both directions of the operands. """ @@ -312,7 +298,7 @@ def assert_ne_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): self.assertEqual(a != b, a is not b) self.assertEqual(b != a, a is not b) - def assert_lt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + def assert_lt_subtest(self, a, b, comp, a_meth, b_meth): """ Test "<" comparison. The comparison is performed in both directions of the operands. """ @@ -325,7 +311,7 @@ def assert_lt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): with self.assertRaisesRegex(TypeError, "not supported"): b > a - def assert_le_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + def assert_le_subtest(self, a, b, comp, a_meth, b_meth): """ Test "<=" comparison. The comparison is performed in both directions of the operands. """ @@ -338,7 +324,7 @@ def assert_le_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): with self.assertRaisesRegex(TypeError, "not supported"): b >= a - def assert_gt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + def assert_gt_subtest(self, a, b, comp, a_meth, b_meth): """ Test ">" comparison. The comparison is performed in both directions of the operands. """ @@ -351,7 +337,7 @@ def assert_gt_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): with self.assertRaisesRegex(TypeError, "not supported"): b < a - def assert_ge_comparison_subtest(self, *, a, b, comp, a_meth, b_meth): + def assert_ge_subtest(self, a, b, comp, a_meth, b_meth): """ Test ">=" comparison. The comparison is performed in both directions of the operands. """ From 3f74d0e7fca42bcc1dac7e069b8644754142f25a Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 14:41:22 -0400 Subject: [PATCH 14/21] Condense redundant subtest docstrings. --- Lib/test/test_compare.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 28f7f0c037ae6d..8ee940a4dabb44 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -272,14 +272,11 @@ def assert_total_order(self, a, b, comp, a_meth=None, b_meth=None): # The body of each subtest has form # if value-based comparison methods: - # expect what the testcase defined; + # expect what the testcase defined for a op b and b rop a; # else: no value-based comparison - # expect default behavior of object. + # expect default behavior of object for a op b and b rop a. def assert_eq_subtest(self, a, b, comp, a_meth, b_meth): - """ Test "==" comparison. - The comparison is performed in both directions of the operands. - """ if a_meth is None or "eq" in a_meth or "eq" in b_meth: self.assertEqual(a == b, comp == 0) self.assertEqual(b == a, comp == 0) @@ -288,9 +285,6 @@ def assert_eq_subtest(self, a, b, comp, a_meth, b_meth): self.assertEqual(b == a, a is b) def assert_ne_subtest(self, a, b, comp, a_meth, b_meth): - """ Test "!=" comparison. - The comparison is performed in both directions of the operands. - """ if a_meth is None or not {"ne", "eq"}.isdisjoint(a_meth + b_meth): self.assertEqual(a != b, comp != 0) self.assertEqual(b != a, comp != 0) @@ -299,9 +293,6 @@ def assert_ne_subtest(self, a, b, comp, a_meth, b_meth): self.assertEqual(b != a, a is not b) def assert_lt_subtest(self, a, b, comp, a_meth, b_meth): - """ Test "<" comparison. - The comparison is performed in both directions of the operands. - """ if a_meth is None or "lt" in a_meth or "gt" in b_meth: self.assertEqual(a < b, comp < 0) self.assertEqual(b > a, comp < 0) @@ -312,9 +303,6 @@ def assert_lt_subtest(self, a, b, comp, a_meth, b_meth): b > a def assert_le_subtest(self, a, b, comp, a_meth, b_meth): - """ Test "<=" comparison. - The comparison is performed in both directions of the operands. - """ if a_meth is None or "le" in a_meth or "ge" in b_meth: self.assertEqual(a <= b, comp <= 0) self.assertEqual(b >= a, comp <= 0) @@ -325,9 +313,6 @@ def assert_le_subtest(self, a, b, comp, a_meth, b_meth): b >= a def assert_gt_subtest(self, a, b, comp, a_meth, b_meth): - """ Test ">" comparison. - The comparison is performed in both directions of the operands. - """ if a_meth is None or "gt" in a_meth or "lt" in b_meth: self.assertEqual(a > b, comp > 0) self.assertEqual(b < a, comp > 0) @@ -338,9 +323,6 @@ def assert_gt_subtest(self, a, b, comp, a_meth, b_meth): b < a def assert_ge_subtest(self, a, b, comp, a_meth, b_meth): - """ Test ">=" comparison. - The comparison is performed in both directions of the operands. - """ if a_meth is None or "ge" in a_meth or "le" in b_meth: self.assertEqual(a >= b, comp >= 0) self.assertEqual(b <= a, comp >= 0) From a0c6b5a519ce76919681b8cf50510bdcb6e5abe0 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 15:11:24 -0400 Subject: [PATCH 15/21] Finish editing test_numbers. --- Lib/test/test_compare.py | 89 ++++++++-------------------------------- 1 file changed, 17 insertions(+), 72 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 8ee940a4dabb44..771fdc2d79b470 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -407,93 +407,38 @@ class StrSubclass(str): def test_numbers(self): """ Compare number types.""" - i1 = 10001 - i2 = 10002 - - f1 = 1.1 - f2 = 2.1 - f3 = f1 + 1 # Same value, different instance than f2. - f4 = f2 - 1 # Same value, different instance than f1. - f5 = 42.0 - self.assertIsNot(f2, f3, "Testcase error: f2 is f3") - self.assertIsNot(f1, f4, "Testcase error: f1 is f4") - - c1 = 1+1j - c2 = 2+2j - c3 = 2+2j # Same value, different instance than c2. - c4 = 1+1j # Same value, different instance than c1. - c5 = 42+0j - self.assertIsNot(c2, c3, "Testcase error: c2 is c3") - self.assertIsNot(c1, c4, "Testcase error: c1 is c4") - - q1 = Fraction(1, 2) - q2 = Fraction(2, 3) - q3 = Fraction(2, 3) # Same value, different instance than q2. - q4 = Fraction(1, 2) # Same value, different instance than q1. - q5 = Fraction(84, 2) - self.assertIsNot(q2, q3, "Testcase error: q2 is q3") - self.assertIsNot(q1, q4, "Testcase error: q1 is q4") - - d1 = Decimal('1.2') - d2 = Decimal('2.3') - d3 = Decimal('2.3') # Same value, different instance than d2. - d4 = Decimal('1.2') # Same value, different instance than d1. - d5 = Decimal('42.0') - self.assertIsNot(d2, d3, "Testcase error: d2 is d3") - self.assertIsNot(d1, d4, "Testcase error: d1 is d4") - # Same types. + i1 = 1001 + i2 = 1002 self.assert_total_order(i1, i1, 0) self.assert_total_order(i1, i2, -1) + f1 = 1001.0 + f2 = 1001.1 self.assert_total_order(f1, f1, 0) self.assert_total_order(f1, f2, -1) - self.assert_total_order(f2, f3, 0) - self.assert_total_order(f3, f4, +1) - - self.assert_equality_only(c1, c1, True) - self.assert_equality_only(c1, c2, False) - self.assert_equality_only(c2, c3, True) - self.assert_equality_only(c3, c4, False) + q1 = Fraction(2002, 2) + q2 = Fraction(2003, 2) self.assert_total_order(q1, q1, 0) self.assert_total_order(q1, q2, -1) - self.assert_total_order(q2, q3, 0) - self.assert_total_order(q3, q4, +1) + d1 = Decimal('1001.0') + d2 = Decimal('1001.1') self.assert_total_order(d1, d1, 0) self.assert_total_order(d1, d2, -1) - self.assert_total_order(d2, d3, 0) - self.assert_total_order(d3, d4, +1) - - # Mixing types. - self.assert_total_order(i5, f5, 0) - self.assert_equality_only(i5, c5, True) - self.assert_total_order(i5, q5, 0) - self.assert_total_order(i5, d5, 0) - - self.assert_equality_only(f5, c5, True) - self.assert_total_order(f5, q5, 0) - self.assert_total_order(f5, d5, 0) - - self.assert_equality_only(c5, q5, True) - self.assert_equality_only(c5, d5, True) - self.assert_total_order(q5, d5, 0) - - self.assert_total_order(i1, f1, +1) - self.assert_equality_only(i1, c1, False) - self.assert_total_order(i1, q1, +1) - self.assert_total_order(i1, d1, +1) - - self.assert_equality_only(f1, c1, False) - self.assert_total_order(f1, q1, +1) - self.assert_total_order(f1, d1, -1) + c1 = 1001+0j + c2 = 1001+1j + self.assert_equality_only(c1, c1, True) + self.assert_equality_only(c1, c2, False) - self.assert_equality_only(c1, q1, False) - self.assert_equality_only(c1, d1, False) - self.assert_total_order(q1, d1, -1) + # Mixing types. + for n1, n2 in ((i1,f1), (i1,q1), (i1,d1), (f1,q1), (f1,d1), (q1,d1)): + self.assert_total_order(n1, n2, 0) + for n1 in (i1, f1, q1, d1): + self.assert_equality_only(n1, c1, True) def test_sequences(self): """ Test comparison for sequences (list, tuple, range). From ba12da34993e5f5df98f57ab9d5c44af74960ead Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 15:17:34 -0400 Subject: [PATCH 16/21] Fix test_sequences by removing duplicate objects and 'is' tests. --- Lib/test/test_compare.py | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 771fdc2d79b470..ddc361cd8a80e7 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -445,42 +445,19 @@ def test_sequences(self): """ l1 = [1, 2] l2 = [2, 3] - l3 = [2, 3] # Same value, different instance than l2. - l4 = [1, 2] # Same value, different instance than l1. - self.assertIsNot(l2, l3, "Testcase error: l2 is l3") - self.assertIsNot(l1, l4, "Testcase error: l1 is l4") + self.assert_total_order(l1, l1, 0) + self.assert_total_order(l1, l2, -1) t1 = (1, 2) t2 = (2, 3) - t3 = (2, 3) # Same value, different instance than t2. - t4 = (1, 2) # Same value, different instance than t1. - self.assertIsNot(t2, t3, "Testcase error: t2 is t3") - self.assertIsNot(t1, t4, "Testcase error: t1 is t4") - - r1 = range(1, 2) - r2 = range(2, 2) - r3 = range(2, 2) # Same value, different instance than r2. - r4 = range(1, 2) # Same value, different instance than r1. - self.assertIsNot(r2, r3, "Testcase error: r2 is r3") - self.assertIsNot(r1, r4, "Testcase error: r1 is r4") - - # Same types. self.assert_total_order(t1, t1, 0) self.assert_total_order(t1, t2, -1) - self.assert_total_order(t2, t3, 0) - self.assert_total_order(t3, t4, +1) - - self.assert_total_order(l1, l1, 0) - self.assert_total_order(l1, l2, -1) - self.assert_total_order(l2, l3, 0) - self.assert_total_order(l3, l4, +1) + r1 = range(1, 2) + r2 = range(2, 2) self.assert_equality_only(r1, r1, True) self.assert_equality_only(r1, r2, False) - self.assert_equality_only(r2, r3, True) - self.assert_equality_only(r3, r4, False) - # Mixing types. self.assert_equality_only(t1, l1, False) self.assert_equality_only(l1, r1, False) self.assert_equality_only(r1, t1, False) From 84546733d0304c680339f256e77f18a2ce5d633c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 15:31:43 -0400 Subject: [PATCH 17/21] Fix test_bytes like test_sequences. --- Lib/test/test_compare.py | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index ddc361cd8a80e7..e95a3bc92d9844 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -462,44 +462,23 @@ def test_sequences(self): self.assert_equality_only(l1, r1, False) self.assert_equality_only(r1, t1, False) - def test_binary_sequences(self): - """ Test comparison for binary sequences (bytes, bytearray). + def test_bytes(self): + """ Test comparison for bytes and bytearray). """ bs1 = b'a1' bs2 = b'b2' - bs3 = b'b' + b'2' # Same value, different instance than bs2. - bs4 = b'a' + b'1' # Same value, different instance than bs1. - self.assertIsNot(bs2, bs3, "Testcase error: bs2 is bs3") - self.assertIsNot(bs1, bs4, "Testcase error: bs1 is bs4") - - ba1 = bytearray(b'a1') - ba2 = bytearray(b'b2') - ba3 = bytearray(b'b2') # Same value, different instance than ba2. - ba4 = bytearray(b'a1') # Same value, different instance than ba1. - self.assertIsNot(ba2, ba3, "Testcase error: ba2 is ba3") - self.assertIsNot(ba1, ba4, "Testcase error: ba1 is ba4") - - # Same types. self.assert_total_order(bs1, bs1, 0) self.assert_total_order(bs1, bs2, -1) - self.assert_total_order(bs2, bs3, 0) - self.assert_total_order(bs3, bs4, +1) + ba1 = bytearray(b'a1') + ba2 = bytearray(b'b2') self.assert_total_order(ba1, ba1, 0) self.assert_total_order(ba1, ba2, -1) - self.assert_total_order(ba2, ba3, 0) - self.assert_total_order(ba3, ba4, +1) - # Mixing types. self.assert_total_order(bs1, ba1, 0) self.assert_total_order(bs1, ba2, -1) - self.assert_total_order(bs2, ba3, 0) - self.assert_total_order(bs3, ba4, +1) - self.assert_total_order(ba1, bs1, 0) self.assert_total_order(ba1, bs2, -1) - self.assert_total_order(ba2, bs3, 0) - self.assert_total_order(ba3, bs4, +1) def test_sets(self): """ Test comparison for sets (set, frozenset). From 1e84971275ce03d851c9c494be0e942159fb5679 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 15:41:34 -0400 Subject: [PATCH 18/21] More docstrings. --- Lib/test/test_compare.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index e95a3bc92d9844..78a4d91f1d3708 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -333,14 +333,14 @@ def assert_ge_subtest(self, a, b, comp, a_meth, b_meth): b <= a def test_objects(self): - """ Compare instances of type 'object'.""" + """Compare instances of type 'object'.""" a = object() b = object() self.assert_equality_only(a, a, True) self.assert_equality_only(a, b, False) def test_comp_classes_same(self): - """ Compare same-class instances with comparison methods.""" + """Compare same-class instances with comparison methods.""" for cls in self.all_comp_classes: with self.subTest(cls): @@ -365,7 +365,7 @@ def test_comp_classes_same(self): cls.meth, cls.meth) def test_comp_classes_different(self): - """ Compare different-class instances with comparison methods.""" + """Compare different-class instances with comparison methods.""" for cls_a in self.all_comp_classes: for cls_b in self.all_comp_classes: @@ -383,7 +383,7 @@ def test_comp_classes_different(self): a1, b2, -1, cls_a.meth, cls_b.meth) def test_str_subclass(self): - """ Compare instances of str and a subclass.""" + """Compare instances of str and a subclass.""" class StrSubclass(str): pass @@ -405,7 +405,7 @@ class StrSubclass(str): self.assert_total_order(c2, s2, 0) def test_numbers(self): - """ Compare number types.""" + """Compare number types.""" # Same types. i1 = 1001 @@ -441,8 +441,7 @@ def test_numbers(self): self.assert_equality_only(n1, c1, True) def test_sequences(self): - """ Test comparison for sequences (list, tuple, range). - """ + """Compare list, tuple, and range.""" l1 = [1, 2] l2 = [2, 3] self.assert_total_order(l1, l1, 0) @@ -463,8 +462,7 @@ def test_sequences(self): self.assert_equality_only(r1, t1, False) def test_bytes(self): - """ Test comparison for bytes and bytearray). - """ + """Compare bytes and bytearray.""" bs1 = b'a1' bs2 = b'b2' self.assert_total_order(bs1, bs1, 0) @@ -481,8 +479,7 @@ def test_bytes(self): self.assert_total_order(ba1, bs2, -1) def test_sets(self): - """ Test comparison for sets (set, frozenset). - """ + """Compare set and frozenset.""" s1 = {1, 2} s2 = {1, 2, 3} s3 = {1, 2, 3} # Same value, different instance than s2. @@ -520,7 +517,7 @@ def test_sets(self): self.assert_total_order(f3, s4, +1) def test_mappings(self): - """ Test comparison for mappings (dict). + """ Compare dict. """ d1 = {1: "a", 2: "b"} d2 = {2: "b", 3: "c"} From 019f7494d285bfbd78ac5e663e02090221d37b6f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 19 May 2023 15:49:46 -0400 Subject: [PATCH 19/21] Edit test_sets and test_mappings. --- Lib/test/test_compare.py | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 78a4d91f1d3708..9c8fa7b9e921d7 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -7,7 +7,6 @@ class ComparisonSimpleTest(unittest.TestCase): """Test equality and order comparisons for some simple cases.""" - # TODO Does addition of FullTest below obsolete any of these? class Empty: def __repr__(self): @@ -482,54 +481,28 @@ def test_sets(self): """Compare set and frozenset.""" s1 = {1, 2} s2 = {1, 2, 3} - s3 = {1, 2, 3} # Same value, different instance than s2. - s4 = {1, 2} # Same value, different instance than s1. - self.assertIsNot(s2, s3, "Testcase error: s2 is s3") - self.assertIsNot(s1, s4, "Testcase error: s1 is s4") - - f1 = frozenset({1, 2}) - f2 = frozenset({1, 2, 3}) - f3 = frozenset({1, 2, 3}) # Same value, different instance than f2. - f4 = frozenset({1, 2}) # Same value, different instance than f1. - self.assertIsNot(f2, f3, "Testcase error: f2 is f3") - self.assertIsNot(f1, f4, "Testcase error: f1 is f4") - - # Same types. self.assert_total_order(s1, s1, 0) self.assert_total_order(s1, s2, -1) - self.assert_total_order(s2, s3, 0) - self.assert_total_order(s3, s4, +1) + f1 = frozenset(s1) + f2 = frozenset(s2) self.assert_total_order(f1, f1, 0) self.assert_total_order(f1, f2, -1) - self.assert_total_order(f2, f3, 0) - self.assert_total_order(f3, f4, +1) - # Mixing types. self.assert_total_order(s1, f1, 0) self.assert_total_order(s1, f2, -1) - self.assert_total_order(s2, f3, 0) - self.assert_total_order(s3, f4, +1) - self.assert_total_order(f1, s1, 0) self.assert_total_order(f1, s2, -1) - self.assert_total_order(f2, s3, 0) - self.assert_total_order(f3, s4, +1) def test_mappings(self): """ Compare dict. """ d1 = {1: "a", 2: "b"} d2 = {2: "b", 3: "c"} - d3 = {3: "c", 2: "b"} # Same value, different instance than d2. - d4 = {1: "a", 2: "b"} # Same value, different instance than d1. - self.assertIsNot(d2, d3, "Testcase error: d2 is d3") - self.assertIsNot(d1, d4, "Testcase error: d1 is d4") - + d3 = {3: "c", 2: "b"} self.assert_equality_only(d1, d1, True) self.assert_equality_only(d1, d2, False) self.assert_equality_only(d2, d3, True) - self.assert_equality_only(d3, d4, False) if __name__ == '__main__': From 844723720dd3f21f3e0ea5ed9894cb212663e721 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 20 May 2023 11:09:51 -0400 Subject: [PATCH 20/21] Update Lib/test/test_compare.py Co-authored-by: Oleg Iarygin --- Lib/test/test_compare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 9c8fa7b9e921d7..56bfda67238093 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -1,4 +1,4 @@ -"Test equality and order comparisons." +"""Test equality and order comparisons.""" import unittest from test.support import ALWAYS_EQ from fractions import Fraction From 75e445d79d3f4f9af4140ca155a9bef4b25176f4 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 20 May 2023 11:10:31 -0400 Subject: [PATCH 21/21] Update Lib/test/test_compare.py Co-authored-by: Oleg Iarygin --- Lib/test/test_compare.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 56bfda67238093..8166b0eea306e3 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -269,11 +269,12 @@ def assert_total_order(self, a, b, comp, a_meth=None, b_meth=None): self.assert_gt_subtest(a, b, comp, a_meth, b_meth) self.assert_ge_subtest(a, b, comp, a_meth, b_meth) - # The body of each subtest has form - # if value-based comparison methods: - # expect what the testcase defined for a op b and b rop a; - # else: no value-based comparison - # expect default behavior of object for a op b and b rop a. + # The body of each subtest has form: + # + # if value-based comparison methods: + # expect what the testcase defined for a op b and b rop a; + # else: no value-based comparison + # expect default behavior of object for a op b and b rop a. def assert_eq_subtest(self, a, b, comp, a_meth, b_meth): if a_meth is None or "eq" in a_meth or "eq" in b_meth: