diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index 1d90ac10ad77ad..31ef1457ef3944 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -595,6 +595,11 @@ def test_returning_deeply_nested(self): interp.eval('set x a; for {set i 0} {$i < %d} {incr i} {set x [list $x]}' % support.exceeds_recursion_limit()) self.assertRaises(RecursionError, interp.getvar, 'x') + interp = None + + # gh-161353: Clean up explicitly per-thread state to avoid leaking an + # event handle on Windows when test_tcl is run with -R 3:3 option. + _tkinter._finalize_thread() def test_user_command(self): result = None diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 9a09687e55b981..9540839b9682bc 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -3584,6 +3584,20 @@ _tkinter_getbusywaitinterval_impl(PyObject *module) return Tkinter_busywaitinterval; } +/*[clinic input] +_tkinter._finalize_thread + +Clean up per-thread state. +[clinic start generated code]*/ + +static PyObject * +_tkinter__finalize_thread_impl(PyObject *module) +/*[clinic end generated code: output=4ed0d3be4d1f8de8 input=ea68096ee9b83d23]*/ +{ + Tcl_FinalizeThread(); + Py_RETURN_NONE; +} + #include "clinic/_tkinter.c.h" static PyMethodDef Tktt_methods[] = @@ -3681,6 +3695,7 @@ static PyMethodDef moduleMethods[] = _TKINTER_CREATE_METHODDEF _TKINTER_SETBUSYWAITINTERVAL_METHODDEF _TKINTER_GETBUSYWAITINTERVAL_METHODDEF + _TKINTER__FINALIZE_THREAD_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_tkinter.c.h b/Modules/clinic/_tkinter.c.h index 1e197167409a03..22529aaf7bee44 100644 --- a/Modules/clinic/_tkinter.c.h +++ b/Modules/clinic/_tkinter.c.h @@ -956,6 +956,24 @@ _tkinter_getbusywaitinterval(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(_tkinter__finalize_thread__doc__, +"_finalize_thread($module, /)\n" +"--\n" +"\n" +"Clean up per-thread state."); + +#define _TKINTER__FINALIZE_THREAD_METHODDEF \ + {"_finalize_thread", (PyCFunction)_tkinter__finalize_thread, METH_NOARGS, _tkinter__finalize_thread__doc__}, + +static PyObject * +_tkinter__finalize_thread_impl(PyObject *module); + +static PyObject * +_tkinter__finalize_thread(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _tkinter__finalize_thread_impl(module); +} + #ifndef _TKINTER_TKAPP_CREATEFILEHANDLER_METHODDEF #define _TKINTER_TKAPP_CREATEFILEHANDLER_METHODDEF #endif /* !defined(_TKINTER_TKAPP_CREATEFILEHANDLER_METHODDEF) */ @@ -963,4 +981,4 @@ _tkinter_getbusywaitinterval(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef _TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF #define _TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF #endif /* !defined(_TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF) */ -/*[clinic end generated code: output=229ae8ed00ee4e40 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c94e363a6f9de67d input=a9049054013a1b77]*/