]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-138314: Add winreg.DeleteTree (GH-138388)
authorAN Long <aisk@users.noreply.github.com>
Tue, 2 Sep 2025 17:04:57 +0000 (02:04 +0900)
committerGitHub <noreply@github.com>
Tue, 2 Sep 2025 17:04:57 +0000 (17:04 +0000)
Doc/library/winreg.rst
Lib/test/test_winreg.py
Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst [new file with mode: 0644]
PC/clinic/winreg.c.h
PC/winreg.c

index b3a824fb69a49f1cc91ed63950a67abbc4925b2e..61056d41cf7a69df7400953ed2d46e02d0db3971 100644 (file)
@@ -173,6 +173,24 @@ This module offers the following functions:
       See :ref:`above <exception-changed>`.
 
 
+.. function:: DeleteTree(key, sub_key=None)
+
+   Deletes the specified key and all its subkeys and values recursively.
+
+   *key* is an already open key, or one of the predefined
+   :ref:`HKEY_* constants <hkey-constants>`.
+
+   *sub_key* is a string that names the subkey to delete. If ``None``,
+   deletes all subkeys and values of the specified key.
+
+   This function deletes a key and all its descendants. If *sub_key* is
+   ``None``, all subkeys and values of the specified key are deleted.
+
+   .. audit-event:: winreg.DeleteTree key,sub_key winreg.DeleteTree
+
+   .. versionadded:: next
+
+
 .. function:: DeleteValue(key, value)
 
    Removes a named value from a registry key.
index 924a962781a75bfaf5a446660414e27cf582064e..6f2a6ac900be824e72a09a89623a50e8068417df 100644 (file)
@@ -517,6 +517,21 @@ class Win64WinregTests(BaseWinregTests):
         with self.assertRaises(FileNotFoundError) as ctx:
             QueryValue(HKEY_CLASSES_ROOT, 'some_value_that_does_not_exist')
 
+    def test_delete_tree(self):
+        with CreateKey(HKEY_CURRENT_USER, test_key_name) as main_key:
+            with CreateKey(main_key, "subkey1") as subkey1:
+                SetValueEx(subkey1, "value1", 0, REG_SZ, "test_value1")
+                with CreateKey(subkey1, "subsubkey1") as subsubkey1:
+                    SetValueEx(subsubkey1, "value2", 0, REG_DWORD, 42)
+
+            with CreateKey(main_key, "subkey2") as subkey2:
+                SetValueEx(subkey2, "value3", 0, REG_SZ, "test_value3")
+
+        DeleteTree(HKEY_CURRENT_USER, test_key_name)
+
+        with self.assertRaises(OSError):
+            OpenKey(HKEY_CURRENT_USER, test_key_name)
+
 
 if __name__ == "__main__":
     if not REMOTE_NAME:
diff --git a/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst b/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst
new file mode 100644 (file)
index 0000000..1f9eadc
--- /dev/null
@@ -0,0 +1 @@
+Add :func:`winreg.DeleteTree`.
index 45d54e3c90289a4f27bbf13458be81306e2bf1d0..d76a8d8aef8cb8bd6dff77ed86cd8d638aa37108 100644 (file)
@@ -1633,6 +1633,70 @@ exit:
 
 #if (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM))
 
+PyDoc_STRVAR(winreg_DeleteTree__doc__,
+"DeleteTree($module, key, sub_key=None, /)\n"
+"--\n"
+"\n"
+"Deletes the specified key and all its subkeys and values recursively.\n"
+"\n"
+"  key\n"
+"    An already open key, or any one of the predefined HKEY_* constants.\n"
+"  sub_key\n"
+"    A string that names the subkey to delete. If None, deletes all subkeys\n"
+"    and values of the specified key.\n"
+"\n"
+"This function deletes a key and all its descendants. If sub_key is None,\n"
+"all subkeys and values of the specified key are deleted.");
+
+#define WINREG_DELETETREE_METHODDEF    \
+    {"DeleteTree", _PyCFunction_CAST(winreg_DeleteTree), METH_FASTCALL, winreg_DeleteTree__doc__},
+
+static PyObject *
+winreg_DeleteTree_impl(PyObject *module, HKEY key, const wchar_t *sub_key);
+
+static PyObject *
+winreg_DeleteTree(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    HKEY key;
+    const wchar_t *sub_key = NULL;
+
+    if (!_PyArg_CheckPositional("DeleteTree", nargs, 1, 2)) {
+        goto exit;
+    }
+    if (!clinic_HKEY_converter(_PyModule_GetState(module), args[0], &key)) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    if (args[1] == Py_None) {
+        sub_key = NULL;
+    }
+    else if (PyUnicode_Check(args[1])) {
+        sub_key = PyUnicode_AsWideCharString(args[1], NULL);
+        if (sub_key == NULL) {
+            goto exit;
+        }
+    }
+    else {
+        _PyArg_BadArgument("DeleteTree", "argument 2", "str or None", args[1]);
+        goto exit;
+    }
+skip_optional:
+    return_value = winreg_DeleteTree_impl(module, key, sub_key);
+
+exit:
+    /* Cleanup for sub_key */
+    PyMem_Free((void *)sub_key);
+
+    return return_value;
+}
+
+#endif /* (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) */
+
+#if (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM))
+
 PyDoc_STRVAR(winreg_QueryReflectionKey__doc__,
 "QueryReflectionKey($module, key, /)\n"
 "--\n"
@@ -1771,7 +1835,11 @@ exit:
     #define WINREG_ENABLEREFLECTIONKEY_METHODDEF
 #endif /* !defined(WINREG_ENABLEREFLECTIONKEY_METHODDEF) */
 
+#ifndef WINREG_DELETETREE_METHODDEF
+    #define WINREG_DELETETREE_METHODDEF
+#endif /* !defined(WINREG_DELETETREE_METHODDEF) */
+
 #ifndef WINREG_QUERYREFLECTIONKEY_METHODDEF
     #define WINREG_QUERYREFLECTIONKEY_METHODDEF
 #endif /* !defined(WINREG_QUERYREFLECTIONKEY_METHODDEF) */
-/*[clinic end generated code: output=be4b6857b95558b5 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=ce7e8e38884851fb input=a9049054013a1b77]*/
index 9bbacb0f50bd63c88544d147163bb94c50c2d907..8633f29670e2633f2634e5517ed7de05f3c49337 100644 (file)
@@ -2019,6 +2019,45 @@ winreg_EnableReflectionKey_impl(PyObject *module, HKEY key)
     Py_RETURN_NONE;
 }
 
+/*[clinic input]
+winreg.DeleteTree
+
+    key: HKEY
+        An already open key, or any one of the predefined HKEY_* constants.
+    sub_key: Py_UNICODE(accept={str, NoneType}) = None
+        A string that names the subkey to delete. If None, deletes all subkeys
+        and values of the specified key.
+    /
+
+Deletes the specified key and all its subkeys and values recursively.
+
+This function deletes a key and all its descendants. If sub_key is None,
+all subkeys and values of the specified key are deleted.
+[clinic start generated code]*/
+
+static PyObject *
+winreg_DeleteTree_impl(PyObject *module, HKEY key, const wchar_t *sub_key)
+/*[clinic end generated code: output=c34395ee59290501 input=419ef9bb8b06e4bf]*/
+{
+    LONG rc;
+
+    if (PySys_Audit("winreg.DeleteTree", "nu",
+                    (Py_ssize_t)key, sub_key) < 0) {
+        return NULL;
+    }
+
+    Py_BEGIN_ALLOW_THREADS
+    rc = RegDeleteTreeW(key, sub_key);
+    Py_END_ALLOW_THREADS
+
+    if (rc != ERROR_SUCCESS) {
+        PyErr_SetFromWindowsErrWithFunction(rc, "RegDeleteTreeW");
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
 /*[clinic input]
 winreg.QueryReflectionKey
 
@@ -2077,6 +2116,7 @@ static struct PyMethodDef winreg_methods[] = {
     WINREG_DELETEKEY_METHODDEF
     WINREG_DELETEKEYEX_METHODDEF
     WINREG_DELETEVALUE_METHODDEF
+    WINREG_DELETETREE_METHODDEF
     WINREG_DISABLEREFLECTIONKEY_METHODDEF
     WINREG_ENABLEREFLECTIONKEY_METHODDEF
     WINREG_ENUMKEY_METHODDEF