diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2012-11-11 18:36:51 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2012-11-11 18:36:51 (GMT) |
commit | e11fecb5a97595a91506eeba8fd5a185d35ace9e (patch) | |
tree | 9506989d45e8ead66f521f64892448289ca05ce0 /Lib | |
parent | 19e568d254bea8202703302d0ada9bc93f99331a (diff) | |
download | cpython-e11fecb5a97595a91506eeba8fd5a185d35ace9e.zip cpython-e11fecb5a97595a91506eeba8fd5a185d35ace9e.tar.gz cpython-e11fecb5a97595a91506eeba8fd5a185d35ace9e.tar.bz2 |
Issue #16453: Fix equality testing of dead weakref objects.
Also add tests for ordering and hashing.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_weakref.py | 105 |
1 files changed, 84 insertions, 21 deletions
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 74b9a87..92a4713 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -32,6 +32,27 @@ def create_bound_method(): return C().method +class Object: + def __init__(self, arg): + self.arg = arg + def __repr__(self): + return "<Object %r>" % self.arg + def __eq__(self, other): + if isinstance(other, Object): + return self.arg == other.arg + return NotImplemented + def __lt__(self, other): + if isinstance(other, Object): + return self.arg < other.arg + return NotImplemented + def __hash__(self): + return hash(self.arg) + +class RefCycle: + def __init__(self): + self.cycle = self + + class TestBase(unittest.TestCase): def setUp(self): @@ -692,6 +713,69 @@ class ReferencesTestCase(TestBase): self.assertEqual(a(), None) self.assertEqual(l, [a]) + def test_equality(self): + # Alive weakrefs defer equality testing to their underlying object. + x = Object(1) + y = Object(1) + z = Object(2) + a = weakref.ref(x) + b = weakref.ref(y) + c = weakref.ref(z) + d = weakref.ref(x) + # Note how we directly test the operators here, to stress both + # __eq__ and __ne__. + self.assertTrue(a == b) + self.assertFalse(a != b) + self.assertFalse(a == c) + self.assertTrue(a != c) + self.assertTrue(a == d) + self.assertFalse(a != d) + del x, y, z + gc.collect() + for r in a, b, c: + # Sanity check + self.assertIs(r(), None) + # Dead weakrefs compare by identity: whether `a` and `d` are the + # same weakref object is an implementation detail, since they pointed + # to the same original object and didn't have a callback. + # (see issue #16453). + self.assertFalse(a == b) + self.assertTrue(a != b) + self.assertFalse(a == c) + self.assertTrue(a != c) + self.assertEqual(a == d, a is d) + self.assertEqual(a != d, a is not d) + + def test_ordering(self): + # weakrefs cannot be ordered, even if the underlying objects can. + ops = [operator.lt, operator.gt, operator.le, operator.ge] + x = Object(1) + y = Object(1) + a = weakref.ref(x) + b = weakref.ref(y) + for op in ops: + self.assertRaises(TypeError, op, a, b) + # Same when dead. + del x, y + gc.collect() + for op in ops: + self.assertRaises(TypeError, op, a, b) + + def test_hashing(self): + # Alive weakrefs hash the same as the underlying object + x = Object(42) + y = Object(42) + a = weakref.ref(x) + b = weakref.ref(y) + self.assertEqual(hash(a), hash(42)) + del x, y + gc.collect() + # Dead weakrefs: + # - retain their hash is they were hashed when alive; + # - otherwise, cannot be hashed. + self.assertEqual(hash(a), hash(42)) + self.assertRaises(TypeError, hash, b) + class SubclassableWeakrefTestCase(TestBase): @@ -796,27 +880,6 @@ class SubclassableWeakrefTestCase(TestBase): self.assertEqual(self.cbcalled, 0) -class Object: - def __init__(self, arg): - self.arg = arg - def __repr__(self): - return "<Object %r>" % self.arg - def __eq__(self, other): - if isinstance(other, Object): - return self.arg == other.arg - return NotImplemented - def __lt__(self, other): - if isinstance(other, Object): - return self.arg < other.arg - return NotImplemented - def __hash__(self): - return hash(self.arg) - -class RefCycle: - def __init__(self): - self.cycle = self - - class MappingTestCase(TestBase): COUNT = 10 |