Skip to content

Commit 25df3f0

Browse files
Drain the pty master in ScreenTests so endwin() does not hang on macOS
ScreenTests drives curses over a pseudo-terminal whose master was never read. On macOS (and other BSD-derived platforms) the tcdrain() that curses performs inside endwin() blocks until the master side is read, so the test hung until the timeout; Linux does not block there. Drain each pty master in a background daemon thread, joined (with a timeout) once both ends are closed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0028058 commit 25df3f0

1 file changed

Lines changed: 21 additions & 1 deletion

File tree

Lib/test/test_curses.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
import string
55
import sys
66
import tempfile
7+
import threading
78
import unittest
89
from unittest.mock import MagicMock
910

1011
from test.support import (requires, verbose, SaveSignals, cpython_only,
1112
check_disallow_instantiation, MISSING_C_DOCSTRINGS,
12-
gc_collect)
13+
gc_collect, SHORT_TIMEOUT)
1314
from test.support.import_helper import import_module
1415

1516
# Optionally test curses module. This currently requires that the
@@ -1784,8 +1785,27 @@ def tearDown(self):
17841785
pass
17851786
gc_collect()
17861787

1788+
@staticmethod
1789+
def _drain_pty(master):
1790+
# Read and discard whatever curses writes to the screen.
1791+
try:
1792+
while os.read(master, 1024):
1793+
pass
1794+
except OSError:
1795+
pass
1796+
17871797
def make_pty(self):
17881798
master, slave = os.openpty()
1799+
# Drain the master in the background. Otherwise writing to the slave
1800+
# -- and in particular the tcdrain() that curses performs inside
1801+
# endwin() -- blocks once the unread output fills the pty buffer on
1802+
# platforms such as macOS (Linux does not block here).
1803+
reader = threading.Thread(target=self._drain_pty, args=(master,),
1804+
daemon=True)
1805+
reader.start()
1806+
# Closing both ends makes the reader see EOF (or fail) and exit; join
1807+
# with a timeout so a stuck reader can never hang the test.
1808+
self.addCleanup(reader.join, SHORT_TIMEOUT)
17891809
self.addCleanup(os.close, master)
17901810
self.addCleanup(os.close, slave)
17911811
return slave

0 commit comments

Comments
 (0)