mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	(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
					
				
					 4 changed files with 81 additions and 11 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Armin Rigo
						Armin Rigo