diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index b3a824fb69a49f..61056d41cf7a69 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -173,6 +173,24 @@ This module offers the following functions: See :ref:`above `. +.. 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 `. + + *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. diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 924a962781a75b..6f2a6ac900be82 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -517,6 +517,21 @@ def test_exception_numbers(self): 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 index 00000000000000..1f9eadcccf223f --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst @@ -0,0 +1 @@ +Add :func:`winreg.DeleteTree`. diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 45d54e3c90289a..d76a8d8aef8cb8 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -1633,6 +1633,70 @@ winreg_EnableReflectionKey(PyObject *module, PyObject *arg) #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 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) #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]*/ diff --git a/PC/winreg.c b/PC/winreg.c index 9bbacb0f50bd63..8633f29670e263 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -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