Skip to content

Commit 08806a6

Browse files
committed
gh-139122: Reset the cached hash of C UUIDs in __init__ and __setstate__
Subclass instances are allocated zeroed, so cached_hash started at 0 and hash() returned 0. Re-initializing an existing UUID also kept the old hash.
1 parent 7dd8b47 commit 08806a6

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

‎Lib/test/test_uuid.py‎

100755100644
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,34 @@ def __str__(self):
311311

312312
self.assertEqual(repr(CustomUUID(int=1)), "CustomUUID('custom uuid')")
313313

314+
def test_hash_subclass_and_reinit(self):
315+
class CustomUUID(self.uuid.UUID):
316+
pass
317+
318+
with support.swap_item(sys.modules, 'uuid', self.uuid):
319+
for value in (0, 1, 0x12345678123456781234567812345678):
320+
with self.subTest(value=value):
321+
u = self.uuid.UUID(int=value)
322+
expected = hash(u)
323+
324+
s = CustomUUID(int=value)
325+
self.assertEqual(hash(s), expected)
326+
self.assertIn(u, {s})
327+
328+
s = CustomUUID.__new__(CustomUUID)
329+
s.__setstate__({'int': value})
330+
self.assertEqual(hash(s), expected)
331+
332+
u = self.uuid.UUID(int=value + 1)
333+
hash(u)
334+
u.__init__(int=value)
335+
self.assertEqual(hash(u), expected)
336+
337+
u = self.uuid.UUID(int=value + 1)
338+
hash(u)
339+
u.__setstate__({'int': value})
340+
self.assertEqual(hash(u), expected)
341+
314342
def test_exceptions(self):
315343
badvalue = lambda f: self.assertRaises(ValueError, f)
316344
badtype = lambda f: self.assertRaises(TypeError, f)

‎Modules/_uuidmodule.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ _uuid_UUID___init___impl(uuidobject *self, PyObject *hex, PyObject *bytes,
628628
self->bytes[6] |= (uint8_t)(version_num << 4);
629629
}
630630

631+
self->cached_hash = -1;
631632
Py_XSETREF(self->is_safe, Py_XNewRef(is_safe));
632633

633634
return 0;
@@ -1655,6 +1656,7 @@ _uuid_UUID___setstate___impl(uuidobject *self, PyObject *state)
16551656
if (result < 0) {
16561657
return NULL;
16571658
}
1659+
self->cached_hash = -1;
16581660

16591661
int has_is_safe = PySequence_Contains(state, &_Py_ID(is_safe));
16601662
if (has_is_safe < 0) {

0 commit comments

Comments
 (0)