diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-12-11 22:30:17 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-12-11 22:30:17 (GMT) |
commit | 1e40f10886f1c83e47f69ab229b27ab2eceff939 (patch) | |
tree | c179e9c1d536065d17c2c3dbc09576272dbd2ce5 | |
parent | df75d5b402fa7d9377dd6e38f3ad7551de6746d5 (diff) | |
download | cpython-1e40f10886f1c83e47f69ab229b27ab2eceff939.zip cpython-1e40f10886f1c83e47f69ab229b27ab2eceff939.tar.gz cpython-1e40f10886f1c83e47f69ab229b27ab2eceff939.tar.bz2 |
asyncio, tulip issue 209: Fix subprocess for close_fds=False on Python 3.3
Mark the write end of the stdin pipe as non-inheritable.
-rw-r--r-- | Lib/asyncio/unix_events.py | 22 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_subprocess.py | 21 |
2 files changed, 43 insertions, 0 deletions
diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index d5db4d5..d1461fd 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -547,6 +547,22 @@ class _UnixWritePipeTransport(transports._FlowControlMixin, self._loop = None +if hasattr(os, 'set_inheritable'): + # Python 3.4 and newer + _set_inheritable = os.set_inheritable +else: + import fcntl + + def _set_inheritable(fd, inheritable): + cloexec_flag = getattr(fcntl, 'FD_CLOEXEC', 1) + + old = fcntl.fcntl(fd, fcntl.F_GETFD) + if not inheritable: + fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) + else: + fcntl.fcntl(fd, fcntl.F_SETFD, old & ~cloexec_flag) + + class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport): def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs): @@ -558,6 +574,12 @@ class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport): # other end). Notably this is needed on AIX, and works # just fine on other platforms. stdin, stdin_w = self._loop._socketpair() + + # Mark the write end of the stdin pipe as non-inheritable, + # needed by close_fds=False on Python 3.3 and older + # (Python 3.4 implements the PEP 446, socketpair returns + # non-inheritable sockets) + _set_inheritable(stdin_w.fileno(), False) self._proc = subprocess.Popen( args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr, universal_newlines=False, bufsize=bufsize, **kwargs) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 9060b9d..5c0a2c8 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -198,6 +198,27 @@ class SubprocessMixin: self.assertTrue(transport.pause_reading.called) self.assertTrue(transport.resume_reading.called) + def test_stdin_not_inheritable(self): + # Tulip issue #209: stdin must not be inheritable, otherwise + # the Process.communicate() hangs + @asyncio.coroutine + def len_message(message): + code = 'import sys; data = sys.stdin.read(); print(len(data))' + proc = yield from asyncio.create_subprocess_exec( + sys.executable, '-c', code, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + close_fds=False, + loop=self.loop) + stdout, stderr = yield from proc.communicate(message) + exitcode = yield from proc.wait() + return (stdout, exitcode) + + output, exitcode = self.loop.run_until_complete(len_message(b'abc')) + self.assertEqual(output.rstrip(), b'3') + self.assertEqual(exitcode, 0) + if sys.platform != 'win32': # Unix |