]> granicus.if.org Git - python/commitdiff
Exhaustively test dataclass hashing when no hash= value is provided. This is in antic...
authorEric V. Smith <ericvsmith@users.noreply.github.com>
Fri, 23 Feb 2018 18:01:31 +0000 (13:01 -0500)
committerGitHub <noreply@github.com>
Fri, 23 Feb 2018 18:01:31 +0000 (13:01 -0500)
Lib/test/test_dataclasses.py

index 736bc490867b0c01a9cd4e56429c8d44258d8f5d..9752f5502c7da093cd21513766194eb191bacfcd 100755 (executable)
@@ -2393,6 +2393,79 @@ class TestHash(unittest.TestCase):
         self.assertNotEqual(C(1), C(1))
         self.assertEqual(hash(C(1)), hash(C(1.0)))
 
+    def test_hash_no_args(self):
+        # Test dataclasses with no hash= argument.  This exists to
+        # make sure that when hash is changed, the default hashability
+        # keeps working.
+
+        class Base:
+            def __hash__(self):
+                return 301
+
+        # If frozen or eq is None, then use the default value (do not
+        # specify any value in the deecorator).
+        for frozen, eq,    base,   expected       in [
+            (None,  None,  object, 'unhashable'),
+            (None,  None,  Base,   'unhashable'),
+            (None,  False, object, 'object'),
+            (None,  False, Base,   'base'),
+            (None,  True,  object, 'unhashable'),
+            (None,  True,  Base,   'unhashable'),
+            (False, None,  object, 'unhashable'),
+            (False, None,  Base,   'unhashable'),
+            (False, False, object, 'object'),
+            (False, False, Base,   'base'),
+            (False, True,  object, 'unhashable'),
+            (False, True,  Base,   'unhashable'),
+            (True,  None,  object, 'tuple'),
+            (True,  None,  Base,   'tuple'),
+            (True,  False, object, 'object'),
+            (True,  False, Base,   'base'),
+            (True,  True,  object, 'tuple'),
+            (True,  True,  Base,   'tuple'),
+            ]:
+
+            with self.subTest(frozen=frozen, eq=eq, base=base, expected=expected):
+                # First, create the class.
+                if frozen is None and eq is None:
+                    @dataclass
+                    class C(base):
+                        i: int
+                elif frozen is None:
+                    @dataclass(eq=eq)
+                    class C(base):
+                        i: int
+                elif eq is None:
+                    @dataclass(frozen=frozen)
+                    class C(base):
+                        i: int
+                else:
+                    @dataclass(frozen=frozen, eq=eq)
+                    class C(base):
+                        i: int
+
+                # Now, make sure it hashes as expected.
+                if expected == 'unhashable':
+                    c = C(10)
+                    with self.assertRaisesRegex(TypeError, 'unhashable type'):
+                        hash(c)
+
+                elif expected == 'base':
+                    self.assertEqual(hash(C(10)), 301)
+
+                elif expected == 'object':
+                    # I'm not sure what test to use here.  object's
+                    # hash isn't based on id(), so calling hash()
+                    # won't tell us much.  So, just check the function
+                    # used is object's.
+                    self.assertIs(C.__hash__, object.__hash__)
+
+                elif expected == 'tuple':
+                    self.assertEqual(hash(C(42)), hash((42,)))
+
+                else:
+                    assert False, f'unknown value for expected={expected!r}'
+
 
 if __name__ == '__main__':
     unittest.main()