From b30dc28ef575f45f80d61d8a97c40ad5f5307c3d Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 2 Dec 2025 16:22:56 -0500 Subject: [PATCH 1/3] gh-140482: Avoid changing terminal settings in test_pty The previous test_spawn_doesnt_hang test had a few problems: * It would cause ENV CHANGED failures if other tests were running concurrently due to stty changes * Typing while the test was running could cause it to fail --- Lib/test/test_pty.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index a2018e864445e1..e88649a3d0b350 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -299,26 +299,20 @@ def test_master_read(self): @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_spawn_doesnt_hang(self): - self.addCleanup(unlink, TESTFN) - with open(TESTFN, 'wb') as f: - STDOUT_FILENO = 1 - dup_stdout = os.dup(STDOUT_FILENO) - os.dup2(f.fileno(), STDOUT_FILENO) - buf = b'' - def master_read(fd): - nonlocal buf - data = os.read(fd, 1024) - buf += data - return data - try: - pty.spawn([sys.executable, '-c', 'print("hi there")'], - master_read) - finally: - os.dup2(dup_stdout, STDOUT_FILENO) - os.close(dup_stdout) - self.assertEqual(buf, b'hi there\r\n') - with open(TESTFN, 'rb') as f: - self.assertEqual(f.read(), b'hi there\r\n') + # gh-140482: Do the test in a pty.fork() child to avoid messing + # with the interactive test runner's terminal settings. + pid, fd = pty.fork() + if pid == pty.CHILD: + pty.spawn([sys.executable, '-c', 'print("hi there")']) + os._exit(0) + + try: + (pid, status) = os.waitpid(pid, 0) + self.assertEqual(status, 0) + data = os.read(fd, 1024) + self.assertEqual(data, b"hi there\r\n") + finally: + os.close(fd) class SmallPtyTests(unittest.TestCase): """These tests don't spawn children or hang.""" From f5cc4a261a8cddf925ad4f4e1bdc904a5e65459b Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 2 Dec 2025 16:27:45 -0500 Subject: [PATCH 2/3] Fix lint --- Lib/test/test_pty.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index e88649a3d0b350..843313bc784418 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -3,7 +3,6 @@ is_android, is_apple_mobile, is_wasm32, reap_children, verbose, warnings_helper ) from test.support.import_helper import import_module -from test.support.os_helper import TESTFN, unlink # Skip these tests if termios is not available import_module('termios') From d89f1b7e870b5870ef4fd58128d41659e526227c Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 2 Dec 2025 17:45:10 -0500 Subject: [PATCH 3/3] Fix hang on macOS --- Lib/test/test_pty.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 843313bc784418..7e4f4828ce0f8d 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -306,10 +306,17 @@ def test_spawn_doesnt_hang(self): os._exit(0) try: + buf = bytearray() + try: + while (data := os.read(fd, 1024)) != b'': + buf.extend(data) + except OSError as e: + if e.errno != errno.EIO: + raise + (pid, status) = os.waitpid(pid, 0) self.assertEqual(status, 0) - data = os.read(fd, 1024) - self.assertEqual(data, b"hi there\r\n") + self.assertEqual(buf.take_bytes(), b"hi there\r\n") finally: os.close(fd)