diff --git a/Lib/pty.py b/Lib/pty.py --- a/Lib/pty.py +++ b/Lib/pty.py @@ -137,7 +137,7 @@ def _copy(master_fd, master_read=_read, if master_fd in rfds: data = master_read(master_fd) if not data: # Reached EOF. - fds.remove(master_fd) + return else: os.write(STDOUT_FILENO, data) if STDIN_FILENO in rfds: @@ -153,7 +153,15 @@ def spawn(argv, master_read=_read, stdin argv = (argv,) pid, master_fd = fork() if pid == CHILD: - os.execlp(argv[0], *argv) + try: + os.execlp(argv[0], *argv) + except: + # If we wanted to be really clever, we would use + # the same method as subprocess() to pass the error + # back to the parent. For now just dump stack trace. + traceback.print_exc() + finally: + os._exit(1) try: mode = tty.tcgetattr(STDIN_FILENO) tty.setraw(STDIN_FILENO) @@ -163,6 +171,10 @@ def spawn(argv, master_read=_read, stdin try: _copy(master_fd, master_read, stdin_read) except OSError: + # Some OSes never return an EOF on pty, just raise + # an error instead. + pass + finally: if restore: tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -281,7 +281,7 @@ class SmallPtyTests(unittest.TestCase): socketpair[1].close() os.close(write_to_stdin_fd) - # Expect two select calls, the last one will cause IndexError + # Expect two select calls, then a normal return on master EOF pty.select = self._mock_select self.select_rfds_lengths.append(2) self.select_rfds_results.append([mock_stdin_fd, masters[0]]) @@ -289,8 +289,7 @@ class SmallPtyTests(unittest.TestCase): # both encountered an EOF before the second select call. self.select_rfds_lengths.append(0) - with self.assertRaises(IndexError): - pty._copy(masters[0]) + pty._copy(masters[0]) def tearDownModule():