mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #16453: Fix equality testing of dead weakref objects.
Also add tests for hashing.
This commit is contained in:
parent
027d6fcebd
commit
b704eab599
3 changed files with 79 additions and 15 deletions
|
@ -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):
|
||||||
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):
|
||||||
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
|
||||||
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)
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue