]> granicus.if.org Git - python/commitdiff
asyncio, tulip issue 209: Fix subprocess for close_fds=False on Python 3.3
authorVictor Stinner <victor.stinner@gmail.com>
Thu, 11 Dec 2014 22:30:17 +0000 (23:30 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Thu, 11 Dec 2014 22:30:17 +0000 (23:30 +0100)
Mark the write end of the stdin pipe as non-inheritable.

Lib/asyncio/unix_events.py
Lib/test/test_asyncio/test_subprocess.py

index d5db4d55fa0c14881580f255cf4c3b597646dc8d..d1461fd02485222ed5dc66a4570276f863de56b5 100644 (file)
@@ -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)
index 9060b9d3ed329ff8d858f81b6e3ca50e59b04420..5c0a2c85e312074298f483d7c95522bcbb839c37 100644 (file)
@@ -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