diff options
author | Armin Rigo <arigo@tunes.org> | 2006-06-08 10:56:24 (GMT) |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2006-06-08 10:56:24 (GMT) |
commit | fd01d7933bc3e9fd64d81961fbb7eabddcc82bc3 (patch) | |
tree | 04841c9342f5a07bd7436f8520aa2d54f0e54580 | |
parent | 996710fd44426f43d54034ebf3ee2355fca18f6a (diff) | |
download | cpython-fd01d7933bc3e9fd64d81961fbb7eabddcc82bc3.zip cpython-fd01d7933bc3e9fd64d81961fbb7eabddcc82bc3.tar.gz cpython-fd01d7933bc3e9fd64d81961fbb7eabddcc82bc3.tar.bz2 |
(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'.
-rw-r--r-- | Lib/test/test_class.py | 34 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 15 | ||||
-rw-r--r-- | Objects/classobject.c | 17 | ||||
-rw-r--r-- | Objects/descrobject.c | 26 |
4 files changed, 81 insertions, 11 deletions
diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 92c220e..6c91deb 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -368,3 +368,37 @@ except AttributeError, x: pass else: 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) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 32796bf..ca91042 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4014,11 +4014,24 @@ def methodwrapper(): l = [] 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__.__self__ is l) verify(l.__add__.__objclass__ is list) 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(): # all binary methods should be able to return a NotImplemented diff --git a/Objects/classobject.c b/Objects/classobject.c index 6d2c648d..9e57269 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -2221,9 +2221,17 @@ instancemethod_dealloc(register PyMethodObject *im) static int 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 PyObject_Compare(a->im_func, b->im_func); + else + return PyObject_Compare(a->im_self, b->im_self); } static PyObject * @@ -2299,7 +2307,10 @@ instancemethod_hash(PyMethodObject *a) y = PyObject_Hash(a->im_func); if (y == -1) return -1; - return x ^ y; + x = x ^ y; + if (x == -1) + x = -2; + return x; } static int diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 561ba4a5..606ef05 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -901,16 +901,28 @@ wrapper_dealloc(wrapperobject *wp) static int wrapper_compare(wrapperobject *a, wrapperobject *b) { - if (a->descr == b->descr) { - if (a->self == b->self) - return 0; - else - return (a->self < b->self) ? -1 : 1; - } + if (a->descr == b->descr) + return PyObject_Compare(a->self, b->self); else 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 * wrapper_repr(wrapperobject *wp) { @@ -1008,7 +1020,7 @@ static PyTypeObject wrappertype = { 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)wrapper_hash, /* tp_hash */ (ternaryfunc)wrapper_call, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ |