https://github.com/python/cpython/commit/4532b36db5b9a43f63bf295aaf51df426e4c488d
commit: 4532b36db5b9a43f63bf295aaf51df426e4c488d
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-09-23T13:09:32+03:00
summary:

gh-157672: Keep Python's signal handlers when Tk is initialized on macOS 
(GH-157673)

Tk on macOS replaces the SIGINT, SIGHUP and SIGTERM handlers with its
own, which exits the process, so Ctrl-C terminated a tkinter program
instead of raising KeyboardInterrupt.  Save the handlers installed by
Python before Tk_Init() and restore them after it.

This affects Tk 8.6.11 to 8.6.18 and 9.0 to 9.0.4.  Tk itself is fixed
in 8.6.19 and 9.0.5.

files:
A Misc/NEWS.d/next/Library/2026-09-17-14-00-00.gh-issue-157672.tksigs.rst
M Lib/test/test_tkinter/test_misc.py
M Modules/_tkinter.c

diff --git a/Lib/test/test_tkinter/test_misc.py 
b/Lib/test/test_tkinter/test_misc.py
index c9d402e11a8826f..5c1eea786698595 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -3,6 +3,7 @@
 import os
 import gc
 import platform
+import signal
 import sys
 import textwrap
 import time
@@ -13,6 +14,7 @@
 import enum
 from test import support
 from test.support import os_helper
+from test.support.isolation import runInSubprocess
 from test.support.script_helper import assert_python_ok
 from test.test_tkinter.support import setUpModule  # noqa: F401
 from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
@@ -2149,6 +2151,18 @@ def _info_commands(widget, pattern=None):
     return widget.tk.splitlist(widget.tk.call('info', 'commands', pattern))
 
 
+class SignalTest(unittest.TestCase):
+
+    @runInSubprocess()
+    def test_sigint_handler(self):
+        # gh-157672: Tk on macOS replaced the SIGINT handler with its own,
+        # which exits the process.
+        root = tkinter.Tk()
+        self.addCleanup(root.destroy)
+        with self.assertRaises(KeyboardInterrupt):
+            signal.raise_signal(signal.SIGINT)
+
+
 class TclObjTypeTest(AbstractTkTest, unittest.TestCase):
     # See FromObj() in Modules/_tkinter.c: Tcl object types are converted to
     # appropriate Python types.  These conversions only happen in the object
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-17-14-00-00.gh-issue-157672.tksigs.rst 
b/Misc/NEWS.d/next/Library/2026-09-17-14-00-00.gh-issue-157672.tksigs.rst
new file mode 100644
index 000000000000000..a751a2f9dc406b1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-17-14-00-00.gh-issue-157672.tksigs.rst
@@ -0,0 +1,4 @@
+Fix :mod:`tkinter` on macOS: creating a :class:`~tkinter.Tk` instance no
+longer replaces the Python handlers of SIGINT, SIGHUP and SIGTERM with Tk's
+handler which exits the process, so Ctrl-C raises :exc:`KeyboardInterrupt`
+again.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index f5fb1841f0d5940..746c39e6d3f4a20 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -33,6 +33,8 @@ Copyright (C) 1994 Steen Lumholt.
 #include "pycore_long.h"          // _PyLong_IsNegative()
 #include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8String
 
+#include <signal.h>               // SIGINT
+
 #ifdef MS_WINDOWS
 #  include <windows.h>
 #endif
@@ -227,7 +229,25 @@ Tkinter_TkInit(Tcl_Interp *interp)
        does not search. Mount the DLL using Zipfs if possible.  */
     mount_tk_dll_zip();
 #endif
-    return Tk_Init(interp);
+#ifdef __APPLE__
+    /* Tk on macOS replaces the handlers of these signals with its own,
+       which exits the process.  Keep the handlers installed by Python
+       (gh-157672). */
+    static const int signals[] = {SIGINT, SIGHUP, SIGTERM};
+    PyOS_sighandler_t handlers[Py_ARRAY_LENGTH(signals)];
+    for (size_t i = 0; i < Py_ARRAY_LENGTH(signals); i++) {
+        handlers[i] = PyOS_getsig(signals[i]);
+    }
+#endif
+    int result = Tk_Init(interp);
+#ifdef __APPLE__
+    for (size_t i = 0; i < Py_ARRAY_LENGTH(signals); i++) {
+        if (handlers[i] != SIG_DFL) {
+            PyOS_setsig(signals[i], handlers[i]);
+        }
+    }
+#endif
+    return result;
 }
 
 /* The threading situation is complicated.  Tcl is not thread-safe, except

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to