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.
This commit is contained in:
parent
0836670c5c
commit
e76147a83a
|
@ -51,7 +51,8 @@ class BaseDatabaseWrapper(object):
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self == other
|
return not self == other
|
||||||
|
|
||||||
__hash__ = object.__hash__
|
def __hash__(self):
|
||||||
|
return hash(self.alias)
|
||||||
|
|
||||||
def get_connection_params(self):
|
def get_connection_params(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -135,7 +135,8 @@ class Field(object):
|
||||||
return self.creation_counter < other.creation_counter
|
return self.creation_counter < other.creation_counter
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
__hash__ = object.__hash__
|
def __hash__(self):
|
||||||
|
return hash(self.creation_counter)
|
||||||
|
|
||||||
def __deepcopy__(self, memodict):
|
def __deepcopy__(self, memodict):
|
||||||
# We don't have to deepcopy very much here, since most things are not
|
# We don't have to deepcopy very much here, since most things are not
|
||||||
|
|
|
@ -30,7 +30,6 @@ class FieldFile(File):
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
# Required because we defined a custom __eq__.
|
|
||||||
return hash(self.name)
|
return hash(self.name)
|
||||||
|
|
||||||
# The standard File contains most of the necessary properties, but
|
# The standard File contains most of the necessary properties, but
|
||||||
|
|
|
@ -152,7 +152,8 @@ class BoundMethodWeakref(object):
|
||||||
|
|
||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
||||||
__hash__ = object.__hash__
|
def __hash__(self):
|
||||||
|
return hash(self.key)
|
||||||
|
|
||||||
def __bool__( self ):
|
def __bool__( self ):
|
||||||
"""Whether we are still a valid reference"""
|
"""Whether we are still a valid reference"""
|
||||||
|
|
|
@ -85,7 +85,8 @@ class Element(object):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
__hash__ = object.__hash__
|
def __hash__(self):
|
||||||
|
return hash((self.name,) + tuple(a for a in self.attributes))
|
||||||
|
|
||||||
def __ne__(self, element):
|
def __ne__(self, element):
|
||||||
return not self.__eq__(element)
|
return not self.__eq__(element)
|
||||||
|
|
|
@ -152,7 +152,8 @@ def lazy(func, *resultclasses):
|
||||||
other = other.__cast()
|
other = other.__cast()
|
||||||
return self.__cast() < other
|
return self.__cast() < other
|
||||||
|
|
||||||
__hash__ = object.__hash__
|
def __hash__(self):
|
||||||
|
return hash(self.__cast())
|
||||||
|
|
||||||
def __mod__(self, rhs):
|
def __mod__(self, rhs):
|
||||||
if self._delegate_bytes and not six.PY3:
|
if self._delegate_bytes and not six.PY3:
|
||||||
|
|
|
@ -576,9 +576,11 @@ class ThreadTests(TestCase):
|
||||||
different for each thread.
|
different for each thread.
|
||||||
Refs #17258.
|
Refs #17258.
|
||||||
"""
|
"""
|
||||||
connections_set = set()
|
# Map connections by id because connections with identical aliases
|
||||||
|
# have the same hash.
|
||||||
|
connections_dict = {}
|
||||||
connection.cursor()
|
connection.cursor()
|
||||||
connections_set.add(connection)
|
connections_dict[id(connection)] = connection
|
||||||
def runner():
|
def runner():
|
||||||
# Passing django.db.connection between threads doesn't work while
|
# Passing django.db.connection between threads doesn't work while
|
||||||
# connections[DEFAULT_DB_ALIAS] does.
|
# connections[DEFAULT_DB_ALIAS] does.
|
||||||
|
@ -588,19 +590,19 @@ class ThreadTests(TestCase):
|
||||||
# main thread.
|
# main thread.
|
||||||
connection.allow_thread_sharing = True
|
connection.allow_thread_sharing = True
|
||||||
connection.cursor()
|
connection.cursor()
|
||||||
connections_set.add(connection)
|
connections_dict[id(connection)] = connection
|
||||||
for x in range(2):
|
for x in range(2):
|
||||||
t = threading.Thread(target=runner)
|
t = threading.Thread(target=runner)
|
||||||
t.start()
|
t.start()
|
||||||
t.join()
|
t.join()
|
||||||
# Check that each created connection got different inner connection.
|
# Check that each created connection got different inner connection.
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(set([conn.connection for conn in connections_set])),
|
len(set(conn.connection for conn in connections_dict.values())),
|
||||||
3)
|
3)
|
||||||
# Finish by closing the connections opened by the other threads (the
|
# Finish by closing the connections opened by the other threads (the
|
||||||
# connection opened in the main thread will automatically be closed on
|
# connection opened in the main thread will automatically be closed on
|
||||||
# teardown).
|
# teardown).
|
||||||
for conn in connections_set:
|
for conn in connections_dict.values() :
|
||||||
if conn is not connection:
|
if conn is not connection:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
@ -609,25 +611,27 @@ class ThreadTests(TestCase):
|
||||||
Ensure that the connections are different for each thread.
|
Ensure that the connections are different for each thread.
|
||||||
Refs #17258.
|
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():
|
for conn in connections.all():
|
||||||
connections_set.add(conn)
|
connections_dict[id(conn)] = conn
|
||||||
def runner():
|
def runner():
|
||||||
from django.db import connections
|
from django.db import connections
|
||||||
for conn in connections.all():
|
for conn in connections.all():
|
||||||
# Allow thread sharing so the connection can be closed by the
|
# Allow thread sharing so the connection can be closed by the
|
||||||
# main thread.
|
# main thread.
|
||||||
conn.allow_thread_sharing = True
|
conn.allow_thread_sharing = True
|
||||||
connections_set.add(conn)
|
connections_dict[id(conn)] = conn
|
||||||
for x in range(2):
|
for x in range(2):
|
||||||
t = threading.Thread(target=runner)
|
t = threading.Thread(target=runner)
|
||||||
t.start()
|
t.start()
|
||||||
t.join()
|
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
|
# Finish by closing the connections opened by the other threads (the
|
||||||
# connection opened in the main thread will automatically be closed on
|
# connection opened in the main thread will automatically be closed on
|
||||||
# teardown).
|
# teardown).
|
||||||
for conn in connections_set:
|
for conn in connections_dict.values():
|
||||||
if conn is not connection:
|
if conn is not connection:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue