diff -r 19ed42e84030 Lib/subprocess.py --- a/Lib/subprocess.py Thu Oct 25 10:47:46 2012 +0100 +++ b/Lib/subprocess.py Sun Oct 28 18:31:32 2012 -0700 @@ -824,6 +824,21 @@ except EnvironmentError: # Ignore EBADF or other errors pass + + # make sure the child pipes are closed as well + to_close = [] + if stdin == PIPE: + to_close.append(p2cread) + if stdout == PIPE: + to_close.append(c2pwrite) + if stderr == PIPE: + to_close.append(errwrite) + for fd in to_close: + try: + os.close(fd) + except OSError: + pass + raise diff -r 19ed42e84030 Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Thu Oct 25 10:47:46 2012 +0100 +++ b/Lib/test/test_subprocess.py Sun Oct 28 18:31:32 2012 -0700 @@ -68,6 +68,24 @@ self.assertEqual(actual, expected, msg) +class PopenTestException(Exception): + pass + + +class PopenExecuteChildRaises(subprocess.Popen): + """ POpen subclass for testing cleanup of subprocess.PIPE filehandles when + _execute_child fails + """ + def _execute_child(self, args, executable, preexec_fn, close_fds, + pass_fds, cwd, env, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, + restore_signals, start_new_session): + raise PopenTestException("Forced Exception for Test") + + class ProcessTestCase(BaseTestCase): def test_call_seq(self): @@ -995,6 +1013,23 @@ process.communicate() + # This test is Linux specific for simplicity to at least have + # some coverage. It is not a platform specific bug. + @unittest.skipUnless(os.path.isdir('/proc/%d/fd' % os.getpid()), + "Linux specific") + # Test for the fd leak reported in http://bugs.python.org/issue16327 + def test_failed_child_execute_fd_leak(self): + fd_directory = '/proc/%d/fd' % os.getpid() + num_fds_before_popen = len(os.listdir(fd_directory)) + with self.assertRaises(PopenTestException): + PopenExecuteChildRaises( + [sys.executable, "-c", "print()"], stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + num_fds_after_exception = len(os.listdir(fd_directory)) + self.assertEqual(num_fds_before_popen, num_fds_after_exception) + + # context manager class _SuppressCoreFiles(object): """Try to prevent core files from being created."""