Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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.
  • Loading branch information...
commit e76147a83a7306d6d83057b982207ddab37eb942 1 parent 0836670
Aymeric Augustin authored February 25, 2013
3  django/db/backends/__init__.py
@@ -51,7 +51,8 @@ def __eq__(self, other):
51 51
     def __ne__(self, other):
52 52
         return not self == other
53 53
 
54  
-    __hash__ = object.__hash__
  54
+    def __hash__(self):
  55
+        return hash(self.alias)
55 56
 
56 57
     def get_connection_params(self):
57 58
         raise NotImplementedError
3  django/db/models/fields/__init__.py
@@ -135,7 +135,8 @@ def __lt__(self, other):
135 135
             return self.creation_counter < other.creation_counter
136 136
         return NotImplemented
137 137
 
138  
-    __hash__ = object.__hash__
  138
+    def __hash__(self):
  139
+        return hash(self.creation_counter)
139 140
 
140 141
     def __deepcopy__(self, memodict):
141 142
         # We don't have to deepcopy very much here, since most things are not
1  django/db/models/fields/files.py
@@ -30,7 +30,6 @@ def __ne__(self, other):
30 30
         return not self.__eq__(other)
31 31
 
32 32
     def __hash__(self):
33  
-        # Required because we defined a custom __eq__.
34 33
         return hash(self.name)
35 34
 
36 35
     # The standard File contains most of the necessary properties, but
3  django/dispatch/saferef.py
@@ -152,7 +152,8 @@ def __str__(self):
152 152
 
153 153
     __repr__ = __str__
154 154
 
155  
-    __hash__ = object.__hash__
  155
+    def __hash__(self):
  156
+        return hash(self.key)
156 157
 
157 158
     def __bool__( self ):
158 159
         """Whether we are still a valid reference"""
3  django/test/html.py
@@ -85,7 +85,8 @@ def __eq__(self, element):
85 85
             return False
86 86
         return True
87 87
 
88  
-    __hash__ = object.__hash__
  88
+    def __hash__(self):
  89
+        return hash((self.name,) + tuple(a for a in self.attributes))
89 90
 
90 91
     def __ne__(self, element):
91 92
         return not self.__eq__(element)
3  django/utils/functional.py
@@ -152,7 +152,8 @@ def __lt__(self, other):
152 152
                 other = other.__cast()
153 153
             return self.__cast() < other
154 154
 
155  
-        __hash__ = object.__hash__
  155
+        def __hash__(self):
  156
+            return hash(self.__cast())
156 157
 
157 158
         def __mod__(self, rhs):
158 159
             if self._delegate_bytes and not six.PY3:
24  tests/regressiontests/backends/tests.py
@@ -576,9 +576,11 @@ def test_default_connection_thread_local(self):
576 576
         different for each thread.
577 577
         Refs #17258.
578 578
         """
579  
-        connections_set = set()
  579
+        # Map connections by id because connections with identical aliases
  580
+        # have the same hash.
  581
+        connections_dict = {}
580 582
         connection.cursor()
581  
-        connections_set.add(connection)
  583
+        connections_dict[id(connection)] = connection
582 584
         def runner():
583 585
             # Passing django.db.connection between threads doesn't work while
584 586
             # connections[DEFAULT_DB_ALIAS] does.
@@ -588,19 +590,19 @@ def runner():
588 590
             # main thread.
589 591
             connection.allow_thread_sharing = True
590 592
             connection.cursor()
591  
-            connections_set.add(connection)
  593
+            connections_dict[id(connection)] = connection
592 594
         for x in range(2):
593 595
             t = threading.Thread(target=runner)
594 596
             t.start()
595 597
             t.join()
596 598
         # Check that each created connection got different inner connection.
597 599
         self.assertEqual(
598  
-            len(set([conn.connection for conn in connections_set])),
  600
+            len(set(conn.connection for conn in connections_dict.values())),
599 601
             3)
600 602
         # Finish by closing the connections opened by the other threads (the
601 603
         # connection opened in the main thread will automatically be closed on
602 604
         # teardown).
603  
-        for conn in connections_set:
  605
+        for conn in connections_dict.values() :
604 606
             if conn is not connection:
605 607
                 conn.close()
606 608
 
@@ -609,25 +611,27 @@ def test_connections_thread_local(self):
609 611
         Ensure that the connections are different for each thread.
610 612
         Refs #17258.
611 613
         """
612  
-        connections_set = set()
  614
+        # Map connections by id because connections with identical aliases
  615
+        # have the same hash.
  616
+        connections_dict = {}
613 617
         for conn in connections.all():
614  
-            connections_set.add(conn)
  618
+            connections_dict[id(conn)] = conn
615 619
         def runner():
616 620
             from django.db import connections
617 621
             for conn in connections.all():
618 622
                 # Allow thread sharing so the connection can be closed by the
619 623
                 # main thread.
620 624
                 conn.allow_thread_sharing = True
621  
-                connections_set.add(conn)
  625
+                connections_dict[id(conn)] = conn
622 626
         for x in range(2):
623 627
             t = threading.Thread(target=runner)
624 628
             t.start()
625 629
             t.join()
626  
-        self.assertEqual(len(connections_set), 6)
  630
+        self.assertEqual(len(connections_dict), 6)
627 631
         # Finish by closing the connections opened by the other threads (the
628 632
         # connection opened in the main thread will automatically be closed on
629 633
         # teardown).
630  
-        for conn in connections_set:
  634
+        for conn in connections_dict.values():
631 635
             if conn is not connection:
632 636
                 conn.close()
633 637
 

0 notes on commit e76147a

Please sign in to comment.
Something went wrong with that request. Please try again.