From 8ef12852a27f624138a212f742645ce0c6f81660 Mon Sep 17 00:00:00 2001 From: AN Long Date: Wed, 3 Sep 2025 00:59:45 +0900 Subject: [PATCH 1/6] Add winreg.DeleteTree --- Lib/test/test_winreg.py | 15 +++++++++ PC/clinic/winreg.c.h | 75 ++++++++++++++++++++++++++++++++++++++++- PC/winreg.c | 40 ++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) 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/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 45d54e3c90289a..71a1af916c7711 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -1633,6 +1633,75 @@ 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.\n" +"\n" +"The key must be opened with appropriate access rights (DELETE,\n" +"KEY_ENUMERATE_SUB_KEYS, and KEY_QUERY_VALUE).\n" +"\n" +"Requires Windows Vista or later. On older systems, NotImplementedError is raised."); + +#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 +1840,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=72b697dd1415071a input=a9049054013a1b77]*/ diff --git a/PC/winreg.c b/PC/winreg.c index 9bbacb0f50bd63..bd014dd6e69ef3 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=440b81a7bc3fe183]*/ +{ + 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 From b7780303515743836878235c8b4a6e4322f90ac3 Mon Sep 17 00:00:00 2001 From: AN Long Date: Wed, 3 Sep 2025 01:06:17 +0900 Subject: [PATCH 2/6] Update document --- Doc/library/winreg.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index b3a824fb69a49f..8044b9ce5b25fd 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -173,6 +173,27 @@ 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. + + If the function succeeds, the entire key tree is removed. + If the function fails, an :exc:`OSError` exception is raised. + + .. audit-event:: winreg.DeleteTree key,sub_key winreg.DeleteTree + + .. versionadded:: next + + .. function:: DeleteValue(key, value) Removes a named value from a registry key. From 52b1b10fb8cf322b5a40c3a422f204f89043e3e5 Mon Sep 17 00:00:00 2001 From: AN Long Date: Wed, 3 Sep 2025 01:07:51 +0900 Subject: [PATCH 3/6] Add news entry --- .../next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst 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..d22b6bcbe3d5af --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst @@ -0,0 +1 @@ +Add :meth:`winreg.DeleteTree`. From 3fb56e510037070322d3a910c8cd52ced6c3a6b7 Mon Sep 17 00:00:00 2001 From: AN Long Date: Wed, 3 Sep 2025 01:09:42 +0900 Subject: [PATCH 4/6] Update document --- Doc/library/winreg.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index 8044b9ce5b25fd..61056d41cf7a69 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -186,9 +186,6 @@ This module offers the following functions: This function deletes a key and all its descendants. If *sub_key* is ``None``, all subkeys and values of the specified key are deleted. - If the function succeeds, the entire key tree is removed. - If the function fails, an :exc:`OSError` exception is raised. - .. audit-event:: winreg.DeleteTree key,sub_key winreg.DeleteTree .. versionadded:: next From 9f4e5821aeb01648f05394cc46f20ee6377454fd Mon Sep 17 00:00:00 2001 From: AN Long Date: Wed, 3 Sep 2025 01:16:58 +0900 Subject: [PATCH 5/6] Update generated files --- PC/clinic/winreg.c.h | 9 ++------- PC/winreg.c | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 71a1af916c7711..d76a8d8aef8cb8 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -1646,12 +1646,7 @@ PyDoc_STRVAR(winreg_DeleteTree__doc__, " 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.\n" -"\n" -"The key must be opened with appropriate access rights (DELETE,\n" -"KEY_ENUMERATE_SUB_KEYS, and KEY_QUERY_VALUE).\n" -"\n" -"Requires Windows Vista or later. On older systems, NotImplementedError is raised."); +"all subkeys and values of the specified key are deleted."); #define WINREG_DELETETREE_METHODDEF \ {"DeleteTree", _PyCFunction_CAST(winreg_DeleteTree), METH_FASTCALL, winreg_DeleteTree__doc__}, @@ -1847,4 +1842,4 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) #ifndef WINREG_QUERYREFLECTIONKEY_METHODDEF #define WINREG_QUERYREFLECTIONKEY_METHODDEF #endif /* !defined(WINREG_QUERYREFLECTIONKEY_METHODDEF) */ -/*[clinic end generated code: output=72b697dd1415071a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ce7e8e38884851fb input=a9049054013a1b77]*/ diff --git a/PC/winreg.c b/PC/winreg.c index bd014dd6e69ef3..8633f29670e263 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -2037,7 +2037,7 @@ all subkeys and values of the specified key are deleted. static PyObject * winreg_DeleteTree_impl(PyObject *module, HKEY key, const wchar_t *sub_key) -/*[clinic end generated code: output=c34395ee59290501 input=440b81a7bc3fe183]*/ +/*[clinic end generated code: output=c34395ee59290501 input=419ef9bb8b06e4bf]*/ { LONG rc; From c34b39ac3bbe34e45591d1bc7e7dc5bb095d5826 Mon Sep 17 00:00:00 2001 From: AN Long Date: Wed, 3 Sep 2025 01:38:08 +0900 Subject: [PATCH 6/6] Update news entry --- .../next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index d22b6bcbe3d5af..1f9eadcccf223f 100644 --- 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 @@ -1 +1 @@ -Add :meth:`winreg.DeleteTree`. +Add :func:`winreg.DeleteTree`.