]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-140826: Compare winreg.HKEYType by the internal handle value (GH-140843)
authorAN Long <aisk@users.noreply.github.com>
Mon, 3 Nov 2025 08:14:22 +0000 (17:14 +0900)
committerGitHub <noreply@github.com>
Mon, 3 Nov 2025 08:14:22 +0000 (10:14 +0200)
Doc/library/winreg.rst
Lib/test/test_winreg.py
Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst [new file with mode: 0644]
PC/winreg.c

index 83c49876d267dbb7b9fd4a05e600931566d813e2..df8fb83a01899766f566a59f7ba8505d11a9bdf0 100644 (file)
@@ -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.
index 6f2a6ac900be824e72a09a89623a50e8068417df..733d30b3922d35d73d9de40ab59c0a4f42bb2e73 100644 (file)
@@ -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 (file)
index 0000000..875d15f
--- /dev/null
@@ -0,0 +1,2 @@
+Now :class:`!winreg.HKEYType` objects are compared by their underlying Windows
+registry handle value instead of their object identity.
index 8633f29670e2633f2634e5517ed7de05f3c49337..c7bc74728f1ff9a65fff44e2c6a08845619b18b1 100644 (file)
@@ -181,13 +181,38 @@ PyHKEY_strFunc(PyObject *ob)
     return PyUnicode_FromFormat("<PyHKEY:%p>", 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},