From 248ce9fa8c7a1ed1912e0db31172c8cd2c298b37 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 3 Nov 2025 17:14:22 +0900 Subject: [PATCH] gh-140826: Compare winreg.HKEYType by the internal handle value (GH-140843) --- Doc/library/winreg.rst | 8 +++-- Lib/test/test_winreg.py | 27 +++++++++++++++ ...-11-01-00-34-53.gh-issue-140826.JEDd7U.rst | 2 ++ PC/winreg.c | 34 ++++++++++++++++--- 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index 83c49876d267..df8fb83a0189 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -771,8 +771,9 @@ Handle objects provide semantics for :meth:`~object.__bool__` -- thus :: will print ``Yes`` if the handle is currently valid (has not been closed or detached). -The object also support comparison semantics, so handle objects will compare -true if they both reference the same underlying Windows handle value. +The object also support equality comparison semantics, so handle objects will +compare equal if they both reference the same underlying Windows handle value. +Closed handle objects (those with a handle value of zero) always compare equal. Handle objects can be converted to an integer (e.g., using the built-in :func:`int` function), in which case the underlying Windows handle value is @@ -815,3 +816,6 @@ integer handle, and also disconnect the Windows handle from the handle object. will automatically close *key* when control leaves the :keyword:`with` block. +.. versionchanged:: next + Handle objects are now compared by their underlying Windows handle value + instead of object identity for equality comparisons. diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 6f2a6ac900be..733d30b3922d 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -209,6 +209,33 @@ class BaseWinregTests(unittest.TestCase): access=KEY_ALL_ACCESS) as okey: self.assertTrue(okey.handle != 0) + def test_hkey_comparison(self): + """Test HKEY comparison by handle value rather than object identity.""" + key1 = OpenKey(HKEY_CURRENT_USER, None) + key2 = OpenKey(HKEY_CURRENT_USER, None) + key3 = OpenKey(HKEY_LOCAL_MACHINE, None) + + self.addCleanup(CloseKey, key1) + self.addCleanup(CloseKey, key2) + self.addCleanup(CloseKey, key3) + + self.assertEqual(key1.handle, key2.handle) + self.assertTrue(key1 == key2) + self.assertFalse(key1 != key2) + + self.assertTrue(key1 != key3) + self.assertFalse(key1 == key3) + + # Closed keys should be equal (all have handle=0) + CloseKey(key1) + CloseKey(key2) + CloseKey(key3) + + self.assertEqual(key1.handle, 0) + self.assertEqual(key2.handle, 0) + self.assertEqual(key3.handle, 0) + self.assertEqual(key2, key3) + class LocalWinregTests(BaseWinregTests): diff --git a/Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst b/Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst new file mode 100644 index 000000000000..875d15f2f891 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst @@ -0,0 +1,2 @@ +Now :class:`!winreg.HKEYType` objects are compared by their underlying Windows +registry handle value instead of their object identity. diff --git a/PC/winreg.c b/PC/winreg.c index 8633f29670e2..c7bc74728f1f 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -181,13 +181,38 @@ PyHKEY_strFunc(PyObject *ob) return PyUnicode_FromFormat("", pyhkey->hkey); } -static int -PyHKEY_compareFunc(PyObject *ob1, PyObject *ob2) +static PyObject * +PyHKEY_richcompare(PyObject *ob1, PyObject *ob2, int op) { + /* Both objects must be PyHKEY objects from the same module */ + if (Py_TYPE(ob1) != Py_TYPE(ob2)) { + Py_RETURN_NOTIMPLEMENTED; + } + PyHKEYObject *pyhkey1 = (PyHKEYObject *)ob1; PyHKEYObject *pyhkey2 = (PyHKEYObject *)ob2; - return pyhkey1 == pyhkey2 ? 0 : - (pyhkey1 < pyhkey2 ? -1 : 1); + HKEY hkey1 = pyhkey1->hkey; + HKEY hkey2 = pyhkey2->hkey; + int result; + + switch (op) { + case Py_EQ: + result = (hkey1 == hkey2); + break; + case Py_NE: + result = (hkey1 != hkey2); + break; + default: + /* Only support equality comparisons, not ordering */ + Py_RETURN_NOTIMPLEMENTED; + } + + if (result) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } } static Py_hash_t @@ -365,6 +390,7 @@ static PyType_Slot pyhkey_type_slots[] = { {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_hash, PyHKEY_hashFunc}, {Py_tp_str, PyHKEY_strFunc}, + {Py_tp_richcompare, PyHKEY_richcompare}, // Number protocol {Py_nb_add, PyHKEY_binaryFailureFunc}, -- 2.47.3