bpo-40140: test_builtin.PtyTests registers SIGHUP handler (GH-19314)

test_builtin.PtyTests now registers an handler for SIGHUP signal.
Closing the PTY file descriptor can emit a SIGHUP signal: just ignore
it.

run_child() now also closes the PTY file descriptor before waiting
for the process completition, otherwise the test hangs on AIX.
This commit is contained in:
Victor Stinner 2020-04-03 00:40:25 +02:00 committed by GitHub
parent 3c3aa4516c
commit 7a51a7e19f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 21 additions and 8 deletions

View File

@ -1837,7 +1837,21 @@ class PtyTests(unittest.TestCase):
"""Tests that use a pseudo terminal to guarantee stdin and stdout are """Tests that use a pseudo terminal to guarantee stdin and stdout are
terminals in the test environment""" terminals in the test environment"""
@staticmethod
def handle_sighup(signum, frame):
# bpo-40140: if the process is the session leader, os.close(fd)
# of "pid, fd = pty.fork()" can raise SIGHUP signal:
# just ignore the signal.
pass
def run_child(self, child, terminal_input): def run_child(self, child, terminal_input):
old_sighup = signal.signal(signal.SIGHUP, self.handle_sighup)
try:
return self._run_child(child, terminal_input)
finally:
signal.signal(signal.SIGHUP, old_sighup)
def _run_child(self, child, terminal_input):
r, w = os.pipe() # Pipe test results from child back to parent r, w = os.pipe() # Pipe test results from child back to parent
try: try:
pid, fd = pty.fork() pid, fd = pty.fork()
@ -1893,13 +1907,12 @@ class PtyTests(unittest.TestCase):
self.fail("got %d lines in pipe but expected 2, child output was:\n%s" self.fail("got %d lines in pipe but expected 2, child output was:\n%s"
% (len(lines), child_output)) % (len(lines), child_output))
# Wait until the child process completes before closing the PTY to # bpo-40155: Close the PTY before waiting for the child process
# prevent sending SIGHUP to the child process. # completion, otherwise the child process hangs on AIX.
support.wait_process(pid, exitcode=0)
# Close the PTY
os.close(fd) os.close(fd)
support.wait_process(pid, exitcode=0)
return lines return lines
def check_input_tty(self, prompt, terminal_input, stdio_encoding=None): def check_input_tty(self, prompt, terminal_input, stdio_encoding=None):

View File

@ -70,7 +70,7 @@ class PtyTest(unittest.TestCase):
self.addCleanup(signal.signal, signal.SIGALRM, old_alarm) self.addCleanup(signal.signal, signal.SIGALRM, old_alarm)
old_sighup = signal.signal(signal.SIGHUP, self.handle_sighup) old_sighup = signal.signal(signal.SIGHUP, self.handle_sighup)
self.addCleanup(signal.signal, signal.SIGHUP, old_alarm) self.addCleanup(signal.signal, signal.SIGHUP, old_sighup)
# isatty() and close() can hang on some platforms. Set an alarm # isatty() and close() can hang on some platforms. Set an alarm
# before running the test to make sure we don't hang forever. # before running the test to make sure we don't hang forever.
@ -81,8 +81,8 @@ class PtyTest(unittest.TestCase):
self.fail("isatty hung") self.fail("isatty hung")
@staticmethod @staticmethod
def handle_sighup(sig, frame): def handle_sighup(signum, frame):
# if the process is the session leader, os.close(master_fd) # bpo-38547: if the process is the session leader, os.close(master_fd)
# of "master_fd, slave_name = pty.master_open()" raises SIGHUP # of "master_fd, slave_name = pty.master_open()" raises SIGHUP
# signal: just ignore the signal. # signal: just ignore the signal.
pass pass