Index: Lib/test/test_subprocess.py =================================================================== --- Lib/test/test_subprocess.py (révision 87645) +++ Lib/test/test_subprocess.py (copie de travail) @@ -903,6 +903,35 @@ self.assertStderrEqual(stderr, b'') self.assertEqual(p.wait(), -signal.SIGTERM) + def test_close_std_fds(self): + # Issue #10806: test that subprocess pipes still work properly with + # all standard fds closed. + o0 = os.dup(0) + o1 = os.dup(1) + o2 = os.dup(2) + try: + os.close(0) + os.close(1) + os.close(2) + + out, err = subprocess.Popen([sys.executable, "-c", + 'import sys;' + 'sys.stdout.write("apple");' + 'sys.stdout.flush();' + 'sys.stderr.write("orange")'], + stdin=o0, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + err = support.strip_python_stderr(err) + self.assertEqual((out, err), (b'apple', b'orange')) + finally: + os.dup2(o0, 0) + os.dup2(o1, 1) + os.dup2(o2, 2) + os.close(o0) + os.close(o1) + os.close(o2) + def test_surrogates_error_message(self): def prepare(): raise ValueError("surrogate:\uDCff") Index: Lib/subprocess.py =================================================================== --- Lib/subprocess.py (révision 87645) +++ Lib/subprocess.py (copie de travail) @@ -393,22 +393,22 @@ # POSIX defines PIPE_BUF as >= 512. _PIPE_BUF = getattr(select, 'PIPE_BUF', 512) + _FD_CLOEXEC = getattr(fcntl, 'FD_CLOEXEC', 1) + + def _set_cloexec(fd, cloexec): + old = fcntl.fcntl(fd, fcntl.F_GETFD) + if cloexec: + fcntl.fcntl(fd, fcntl.F_SETFD, old | _FD_CLOEXEC) + else: + fcntl.fcntl(fd, fcntl.F_SETFD, old & ~_FD_CLOEXEC) + if _posixsubprocess: _create_pipe = _posixsubprocess.cloexec_pipe else: def _create_pipe(): - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - fds = os.pipe() - - old = fcntl.fcntl(fds[0], fcntl.F_GETFD) - fcntl.fcntl(fds[0], fcntl.F_SETFD, old | cloexec_flag) - old = fcntl.fcntl(fds[1], fcntl.F_GETFD) - fcntl.fcntl(fds[1], fcntl.F_SETFD, old | cloexec_flag) - + _set_cloexec(fds[0], True) + _set_cloexec(fds[1], True) return fds __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", @@ -1194,23 +1194,26 @@ os.close(errpipe_read) # Dup fds for child - if p2cread != -1: - os.dup2(p2cread, 0) - if c2pwrite != -1: - os.dup2(c2pwrite, 1) - if errwrite != -1: - os.dup2(errwrite, 2) + closed = set() + def _dup2(a, b): + # dup2() removes the CLOEXEC flag but + # we must do it ourselves if dup2() + # would be a no-op (issue #10806). + if a == b: + _set_cloexec(a, False) + elif a != -1: + os.dup2(a, b) + _dup2(p2cread, 0) + _dup2(c2pwrite, 1) + _dup2(errwrite, 2) # Close pipe fds. Make sure we don't close the # same fd more than once, or standard fds. - if p2cread != -1 and p2cread not in (0,): - os.close(p2cread) - if (c2pwrite != -1 and - c2pwrite not in (p2cread, 1)): - os.close(c2pwrite) - if (errwrite != -1 and - errwrite not in (p2cread, c2pwrite, 2)): - os.close(errwrite) + closed = set() + for fd in [p2cread, c2pwrite, errwrite]: + if fd > 2 and fd not in closed: + os.close(fd) + closed.add(fd) # Close all other fds, if asked for if close_fds: Index: Modules/_posixsubprocess.c =================================================================== --- Modules/_posixsubprocess.c (révision 87645) +++ Modules/_posixsubprocess.c (copie de travail) @@ -70,13 +70,25 @@ POSIX_CALL(close(errpipe_read)); /* Dup fds for child. */ - if (p2cread != -1) { + if (p2cread == 0) { + int old = fcntl(p2cread, F_GETFD); + if (old != -1) + fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC); + } else if (p2cread != -1) { POSIX_CALL(dup2(p2cread, 0)); /* stdin */ } - if (c2pwrite != -1) { + if (c2pwrite == 1) { + int old = fcntl(c2pwrite, F_GETFD); + if (old != -1) + fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); + } else if (c2pwrite != -1) { POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ } - if (errwrite != -1) { + if (errwrite == 2) { + int old = fcntl(errwrite, F_GETFD); + if (old != -1) + fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); + } else if (errwrite != -1) { POSIX_CALL(dup2(errwrite, 2)); /* stderr */ }