From 08806a68f2ab0d92853c08ce50a907f10c0a4c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Sat, 24 Oct 2026 14:57:12 +0800 Subject: [PATCH] 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. --- Lib/test/test_uuid.py | 28 ++++++++++++++++++++++++++++ Modules/_uuidmodule.c | 2 ++ 2 files changed, 30 insertions(+) mode change 100755 => 100644 Lib/test/test_uuid.py diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py old mode 100755 new mode 100644 index ee6e57b93966a23..bbbc7fbad7e7b42 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -311,6 +311,34 @@ def __str__(self): self.assertEqual(repr(CustomUUID(int=1)), "CustomUUID('custom uuid')") + def test_hash_subclass_and_reinit(self): + class CustomUUID(self.uuid.UUID): + pass + + with support.swap_item(sys.modules, 'uuid', self.uuid): + for value in (0, 1, 0x12345678123456781234567812345678): + with self.subTest(value=value): + u = self.uuid.UUID(int=value) + expected = hash(u) + + s = CustomUUID(int=value) + self.assertEqual(hash(s), expected) + self.assertIn(u, {s}) + + s = CustomUUID.__new__(CustomUUID) + s.__setstate__({'int': value}) + self.assertEqual(hash(s), expected) + + u = self.uuid.UUID(int=value + 1) + hash(u) + u.__init__(int=value) + self.assertEqual(hash(u), expected) + + u = self.uuid.UUID(int=value + 1) + hash(u) + u.__setstate__({'int': value}) + self.assertEqual(hash(u), expected) + def test_exceptions(self): badvalue = lambda f: self.assertRaises(ValueError, f) badtype = lambda f: self.assertRaises(TypeError, f) diff --git a/Modules/_uuidmodule.c b/Modules/_uuidmodule.c index e957071a4ecef95..00402280d6e2c94 100644 --- a/Modules/_uuidmodule.c +++ b/Modules/_uuidmodule.c @@ -628,6 +628,7 @@ _uuid_UUID___init___impl(uuidobject *self, PyObject *hex, PyObject *bytes, self->bytes[6] |= (uint8_t)(version_num << 4); } + self->cached_hash = -1; Py_XSETREF(self->is_safe, Py_XNewRef(is_safe)); return 0; @@ -1655,6 +1656,7 @@ _uuid_UUID___setstate___impl(uuidobject *self, PyObject *state) if (result < 0) { return NULL; } + self->cached_hash = -1; int has_is_safe = PySequence_Contains(state, &_Py_ID(is_safe)); if (has_is_safe < 0) {