Commit e11fecb5 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #16453: Fix equality testing of dead weakref objects.

Also add tests for ordering and hashing.
parent 19e568d2
...@@ -32,6 +32,27 @@ def create_bound_method(): ...@@ -32,6 +32,27 @@ def create_bound_method():
return C().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): class TestBase(unittest.TestCase):
def setUp(self): def setUp(self):
...@@ -692,6 +713,69 @@ class ReferencesTestCase(TestBase): ...@@ -692,6 +713,69 @@ class ReferencesTestCase(TestBase):
self.assertEqual(a(), None) self.assertEqual(a(), None)
self.assertEqual(l, [a]) 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): class SubclassableWeakrefTestCase(TestBase):
...@@ -796,27 +880,6 @@ class SubclassableWeakrefTestCase(TestBase): ...@@ -796,27 +880,6 @@ class SubclassableWeakrefTestCase(TestBase):
self.assertEqual(self.cbcalled, 0) 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): class MappingTestCase(TestBase):
COUNT = 10 COUNT = 10
......
...@@ -10,6 +10,8 @@ What's New in Python 3.2.4 ...@@ -10,6 +10,8 @@ What's New in Python 3.2.4
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #16453: Fix equality testing of dead weakref objects.
- Issue #9535: Fix pending signals that have been received but not yet - Issue #9535: Fix pending signals that have been received but not yet
handled by Python to not persist after os.fork() in the child process. handled by Python to not persist after os.fork() in the child process.
......
...@@ -195,9 +195,13 @@ weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op) ...@@ -195,9 +195,13 @@ weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
} }
if (PyWeakref_GET_OBJECT(self) == Py_None if (PyWeakref_GET_OBJECT(self) == Py_None
|| PyWeakref_GET_OBJECT(other) == Py_None) { || PyWeakref_GET_OBJECT(other) == Py_None) {
PyObject *res = self==other ? Py_True : Py_False; int res = (self == other);
Py_INCREF(res); if (op == Py_NE)
return res; res = !res;
if (res)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
} }
return PyObject_RichCompare(PyWeakref_GET_OBJECT(self), return PyObject_RichCompare(PyWeakref_GET_OBJECT(self),
PyWeakref_GET_OBJECT(other), op); PyWeakref_GET_OBJECT(other), op);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment