From 702d39921cb95883d3b7590eadd65b03f76d0f92 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 25 Feb 2013 22:53:08 +0100 Subject: [PATCH] [1.5.x] Fixed #19634 -- Added proper __hash__ methods. Classes overriding __eq__ need a __hash__ such that equal objects have the same hash. Thanks akaariai for the report and regebro for the patch. Backport of e76147a from master. --- django/db/backends/__init__.py | 3 ++- django/db/models/fields/__init__.py | 3 ++- django/db/models/fields/files.py | 1 - django/dispatch/saferef.py | 3 ++- django/test/html.py | 3 ++- django/utils/functional.py | 3 ++- tests/regressiontests/backends/tests.py | 12 +++++++----- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index f7661a76860..1decce02163 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -47,7 +47,8 @@ class BaseDatabaseWrapper(object): def __ne__(self, other): return not self == other - __hash__ = object.__hash__ + def __hash__(self): + return hash(self.alias) def _commit(self): if self.connection is not None: diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 94abfd784cd..67d2531ad48 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -135,7 +135,8 @@ class Field(object): return self.creation_counter < other.creation_counter return NotImplemented - __hash__ = object.__hash__ + def __hash__(self): + return hash(self.creation_counter) def __deepcopy__(self, memodict): # We don't have to deepcopy very much here, since most things are not diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index e0cc27a081d..e631f177e92 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -30,7 +30,6 @@ class FieldFile(File): return not self.__eq__(other) def __hash__(self): - # Required because we defined a custom __eq__. return hash(self.name) # The standard File contains most of the necessary properties, but diff --git a/django/dispatch/saferef.py b/django/dispatch/saferef.py index 7423669c968..c7731d41feb 100644 --- a/django/dispatch/saferef.py +++ b/django/dispatch/saferef.py @@ -152,7 +152,8 @@ class BoundMethodWeakref(object): __repr__ = __str__ - __hash__ = object.__hash__ + def __hash__(self): + return hash(self.key) def __bool__( self ): """Whether we are still a valid reference""" diff --git a/django/test/html.py b/django/test/html.py index 2f6e4c94817..cda9dfab270 100644 --- a/django/test/html.py +++ b/django/test/html.py @@ -85,7 +85,8 @@ class Element(object): return False return True - __hash__ = object.__hash__ + def __hash__(self): + return hash((self.name,) + tuple(a for a in self.attributes)) def __ne__(self, element): return not self.__eq__(element) diff --git a/django/utils/functional.py b/django/utils/functional.py index 505931e158b..1dcbd55664c 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -150,7 +150,8 @@ def lazy(func, *resultclasses): other = other.__cast() return self.__cast() < other - __hash__ = object.__hash__ + def __hash__(self): + return hash(self.__cast()) def __mod__(self, rhs): if self._delegate_bytes and not six.PY3: diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index 1188da56ba6..b92d3b92317 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -581,25 +581,27 @@ class ThreadTests(TestCase): Ensure that the connections are different for each thread. Refs #17258. """ - connections_set = set() + # Map connections by id because connections with identical aliases + # have the same hash. + connections_dict = {} for conn in connections.all(): - connections_set.add(conn) + connections_dict[id(conn)] = conn def runner(): from django.db import connections for conn in connections.all(): # Allow thread sharing so the connection can be closed by the # main thread. conn.allow_thread_sharing = True - connections_set.add(conn) + connections_dict[id(conn)] = conn for x in range(2): t = threading.Thread(target=runner) t.start() t.join() - self.assertEqual(len(connections_set), 6) + self.assertEqual(len(connections_dict), 6) # Finish by closing the connections opened by the other threads (the # connection opened in the main thread will automatically be closed on # teardown). - for conn in connections_set: + for conn in connections_dict.values(): if conn != connection: conn.close()