Commit b704eab5 authored by Antoine Pitrou's avatar Antoine Pitrou

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

Also add tests for hashing.
parent 027d6fce
...@@ -33,6 +33,27 @@ def create_unbound_method(): ...@@ -33,6 +33,27 @@ def create_unbound_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 __ne__(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):
...@@ -705,6 +726,54 @@ class ReferencesTestCase(TestBase): ...@@ -705,6 +726,54 @@ class ReferencesTestCase(TestBase):
self.assertEqual(b(), None) self.assertEqual(b(), None)
self.assertEqual(l, [a, b]) self.assertEqual(l, [a, b])
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_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):
...@@ -809,17 +878,6 @@ class SubclassableWeakrefTestCase(TestBase): ...@@ -809,17 +878,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
class RefCycle:
def __init__(self):
self.cycle = self
class MappingTestCase(TestBase): class MappingTestCase(TestBase):
COUNT = 10 COUNT = 10
......
...@@ -9,6 +9,8 @@ What's New in Python 2.7.4 ...@@ -9,6 +9,8 @@ What's New in Python 2.7.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.
......
...@@ -187,15 +187,19 @@ weakref_repr(PyWeakReference *self) ...@@ -187,15 +187,19 @@ weakref_repr(PyWeakReference *self)
static PyObject * static PyObject *
weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op) weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
{ {
if (op != Py_EQ || self->ob_type != other->ob_type) { if ((op != Py_EQ && op != Py_NE) || self->ob_type != other->ob_type) {
Py_INCREF(Py_NotImplemented); Py_INCREF(Py_NotImplemented);
return Py_NotImplemented; return Py_NotImplemented;
} }
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