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

Also add tests for ordering and hashing.
This commit is contained in:
Antoine Pitrou 2012-11-11 19:36:51 +01:00
parent 19e568d254
commit e11fecb5a9
3 changed files with 93 additions and 24 deletions

View file

@ -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):
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):
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

View file

@ -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.

View file

@ -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);