diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 0c76e07066f8a8..365d8b04395e3f 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -508,6 +508,23 @@ def test_embedded_null(self): widget.selection_range(0, 'end') self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def') + def test_settrace_gc(self): + # Regression test for https://github.com/python/cpython/issues/138791. + root = tkinter.Tk() + trace = lambda *_, **__: None + trace.evil = type(root.tk) + root.tk.settrace(trace) + root.destroy() + + def test_createtimerhandler_gc(self): + # Regression test for https://github.com/python/cpython/issues/138791. + root = tkinter.Tk() + func = lambda *_, **__: None + func.evil = type(root.tk.createtimerhandler(0, print)) + # Large timeout (in ms) so that the object is destroyed before. + root.tk.createtimerhandler(1234567, func) + root.destroy() + class WmTest(AbstractTkTest, unittest.TestCase): diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index f094286063e9f5..935ba67984ed09 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -2764,7 +2764,14 @@ static int Tktt_Clear(PyObject *op) { TkttObject *self = TkttObject_CAST(op); - Py_CLEAR(self->func); + if (self->token != NULL) { + Tcl_DeleteTimerHandler(self->token); + self->token = NULL; + } + if (self->func != NULL) { + Py_CLEAR(self->func); + Py_DECREF(op); /* See Tktt_New() */ + } return 0; } @@ -2783,6 +2790,9 @@ Tktt_Traverse(PyObject *op, visitproc visit, void *arg) { TkttObject *self = TkttObject_CAST(op); Py_VISIT(Py_TYPE(op)); + if (self->token != NULL) { + Py_VISIT(op); /* See Tktt_New() */ + } Py_VISIT(self->func); return 0; }