[3.14] gh-139391: properly handle signal.signal() in UnixConsole when called from a non-main thread (GH-139392) (#139858)

gh-139391: properly handle `signal.signal()` in `UnixConsole` when called from a non-main thread (GH-139392)
(cherry picked from commit b8c8b8f1d3)

Co-authored-by: yihong <zouzou0208@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-10-09 18:10:51 +02:00 committed by GitHub
parent de87549eb8
commit 8761d6e644
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 2 deletions

View file

@ -390,7 +390,12 @@ def restore(self):
os.write(self.output_fd, b"\033[?7h") os.write(self.output_fd, b"\033[?7h")
if hasattr(self, "old_sigwinch"): if hasattr(self, "old_sigwinch"):
signal.signal(signal.SIGWINCH, self.old_sigwinch) try:
signal.signal(signal.SIGWINCH, self.old_sigwinch)
except ValueError as e:
import threading
if threading.current_thread() is threading.main_thread():
raise e
del self.old_sigwinch del self.old_sigwinch
def push_char(self, char: int | bytes) -> None: def push_char(self, char: int | bytes) -> None:

View file

@ -4,11 +4,12 @@
import signal import signal
import subprocess import subprocess
import sys import sys
import threading
import unittest import unittest
from functools import partial from functools import partial
from test import support from test import support
from test.support import os_helper, force_not_colorized_test_class from test.support import os_helper, force_not_colorized_test_class
from test.support import script_helper from test.support import script_helper, threading_helper
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, call, patch, ANY, Mock from unittest.mock import MagicMock, call, patch, ANY, Mock
@ -318,6 +319,17 @@ def test_restore_with_invalid_environ_on_macos(self, _os_write):
console.prepare() # needed to call restore() console.prepare() # needed to call restore()
console.restore() # this should succeed console.restore() # this should succeed
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def test_restore_in_thread(self, _os_write):
# gh-139391: ensure that console.restore() silently suppresses
# exceptions when calling signal.signal() from a non-main thread.
console = unix_console([])
console.old_sigwinch = signal.SIG_DFL
thread = threading.Thread(target=console.restore)
thread.start()
thread.join() # this should not raise
@unittest.skipIf(sys.platform == "win32", "No Unix console on Windows") @unittest.skipIf(sys.platform == "win32", "No Unix console on Windows")
class TestUnixConsoleEIOHandling(TestCase): class TestUnixConsoleEIOHandling(TestCase):

View file

@ -0,0 +1,3 @@
Fix an issue when, on non-Windows platforms, it was not possible to
gracefully exit a ``python -m asyncio`` process suspended by Ctrl+Z
and later resumed by :manpage:`fg` other than with :manpage:`kill`.