mirror of https://github.com/python/cpython.git
(arre, arigo) SF bug #1350060
Give a consistent behavior for comparison and hashing of method objects (both user- and built-in methods). Now compares the 'self' recursively. The hash was already asking for the hash of 'self'.
This commit is contained in:
parent
996710fd44
commit
fd01d7933b
|
@ -368,3 +368,37 @@ class I:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
print "attribute error for I.__init__ got masked"
|
print "attribute error for I.__init__ got masked"
|
||||||
|
|
||||||
|
|
||||||
|
# Test comparison and hash of methods
|
||||||
|
class A:
|
||||||
|
def __init__(self, x):
|
||||||
|
self.x = x
|
||||||
|
def f(self):
|
||||||
|
pass
|
||||||
|
def g(self):
|
||||||
|
pass
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.x == other.x
|
||||||
|
def __hash__(self):
|
||||||
|
return self.x
|
||||||
|
class B(A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
a1 = A(1)
|
||||||
|
a2 = A(2)
|
||||||
|
assert a1.f == a1.f
|
||||||
|
assert a1.f != a2.f
|
||||||
|
assert a1.f != a1.g
|
||||||
|
assert a1.f == A(1).f
|
||||||
|
assert hash(a1.f) == hash(a1.f)
|
||||||
|
assert hash(a1.f) == hash(A(1).f)
|
||||||
|
|
||||||
|
assert A.f != a1.f
|
||||||
|
assert A.f != A.g
|
||||||
|
assert B.f == A.f
|
||||||
|
assert hash(B.f) == hash(A.f)
|
||||||
|
|
||||||
|
# the following triggers a SystemError in 2.4
|
||||||
|
a = A(hash(A.f.im_func)^(-1))
|
||||||
|
hash(a.f)
|
||||||
|
|
|
@ -4014,11 +4014,24 @@ def methodwrapper():
|
||||||
|
|
||||||
l = []
|
l = []
|
||||||
vereq(l.__add__, l.__add__)
|
vereq(l.__add__, l.__add__)
|
||||||
verify(l.__add__ != [].__add__)
|
vereq(l.__add__, [].__add__)
|
||||||
|
verify(l.__add__ != [5].__add__)
|
||||||
|
verify(l.__add__ != l.__mul__)
|
||||||
verify(l.__add__.__name__ == '__add__')
|
verify(l.__add__.__name__ == '__add__')
|
||||||
verify(l.__add__.__self__ is l)
|
verify(l.__add__.__self__ is l)
|
||||||
verify(l.__add__.__objclass__ is list)
|
verify(l.__add__.__objclass__ is list)
|
||||||
vereq(l.__add__.__doc__, list.__add__.__doc__)
|
vereq(l.__add__.__doc__, list.__add__.__doc__)
|
||||||
|
try:
|
||||||
|
hash(l.__add__)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise TestFailed("no TypeError from hash([].__add__)")
|
||||||
|
|
||||||
|
t = ()
|
||||||
|
t += (7,)
|
||||||
|
vereq(t.__add__, (7,).__add__)
|
||||||
|
vereq(hash(t.__add__), hash((7,).__add__))
|
||||||
|
|
||||||
def notimplemented():
|
def notimplemented():
|
||||||
# all binary methods should be able to return a NotImplemented
|
# all binary methods should be able to return a NotImplemented
|
||||||
|
|
|
@ -2221,9 +2221,17 @@ instancemethod_dealloc(register PyMethodObject *im)
|
||||||
static int
|
static int
|
||||||
instancemethod_compare(PyMethodObject *a, PyMethodObject *b)
|
instancemethod_compare(PyMethodObject *a, PyMethodObject *b)
|
||||||
{
|
{
|
||||||
if (a->im_self != b->im_self)
|
int cmp;
|
||||||
|
cmp = PyObject_Compare(a->im_func, b->im_func);
|
||||||
|
if (cmp)
|
||||||
|
return cmp;
|
||||||
|
|
||||||
|
if (a->im_self == b->im_self)
|
||||||
|
return 0;
|
||||||
|
if (a->im_self == NULL || b->im_self == NULL)
|
||||||
return (a->im_self < b->im_self) ? -1 : 1;
|
return (a->im_self < b->im_self) ? -1 : 1;
|
||||||
return PyObject_Compare(a->im_func, b->im_func);
|
else
|
||||||
|
return PyObject_Compare(a->im_self, b->im_self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -2299,7 +2307,10 @@ instancemethod_hash(PyMethodObject *a)
|
||||||
y = PyObject_Hash(a->im_func);
|
y = PyObject_Hash(a->im_func);
|
||||||
if (y == -1)
|
if (y == -1)
|
||||||
return -1;
|
return -1;
|
||||||
return x ^ y;
|
x = x ^ y;
|
||||||
|
if (x == -1)
|
||||||
|
x = -2;
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -901,16 +901,28 @@ wrapper_dealloc(wrapperobject *wp)
|
||||||
static int
|
static int
|
||||||
wrapper_compare(wrapperobject *a, wrapperobject *b)
|
wrapper_compare(wrapperobject *a, wrapperobject *b)
|
||||||
{
|
{
|
||||||
if (a->descr == b->descr) {
|
if (a->descr == b->descr)
|
||||||
if (a->self == b->self)
|
return PyObject_Compare(a->self, b->self);
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return (a->self < b->self) ? -1 : 1;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return (a->descr < b->descr) ? -1 : 1;
|
return (a->descr < b->descr) ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
wrapper_hash(wrapperobject *wp)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
x = _Py_HashPointer(wp->descr);
|
||||||
|
if (x == -1)
|
||||||
|
return -1;
|
||||||
|
y = PyObject_Hash(wp->self);
|
||||||
|
if (y == -1)
|
||||||
|
return -1;
|
||||||
|
x = x ^ y;
|
||||||
|
if (x == -1)
|
||||||
|
x = -2;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
wrapper_repr(wrapperobject *wp)
|
wrapper_repr(wrapperobject *wp)
|
||||||
{
|
{
|
||||||
|
@ -1008,7 +1020,7 @@ static PyTypeObject wrappertype = {
|
||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
0, /* tp_as_sequence */
|
0, /* tp_as_sequence */
|
||||||
0, /* tp_as_mapping */
|
0, /* tp_as_mapping */
|
||||||
0, /* tp_hash */
|
(hashfunc)wrapper_hash, /* tp_hash */
|
||||||
(ternaryfunc)wrapper_call, /* tp_call */
|
(ternaryfunc)wrapper_call, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
|
|
Loading…
Reference in New Issue