[Python-checkins] cpython: Switch subprocess stdin to a socketpair, attempting to fix issue #19293 (AIX

guido.van.rossum python-checkins at python.org
Tue Oct 22 05:37:24 CEST 2013


http://hg.python.org/cpython/rev/2a0bda8d283d
changeset:   86557:2a0bda8d283d
user:        Guido van Rossum <guido at dropbox.com>
date:        Mon Oct 21 20:37:14 2013 -0700
summary:
  Switch subprocess stdin to a socketpair, attempting to fix issue #19293 (AIX hang).

files:
  Lib/asyncio/unix_events.py                |  29 +++++++++-
  Lib/test/test_asyncio/test_unix_events.py |   7 ++
  2 files changed, 32 insertions(+), 4 deletions(-)


diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py
--- a/Lib/asyncio/unix_events.py
+++ b/Lib/asyncio/unix_events.py
@@ -213,6 +213,9 @@
         self._loop = loop
         self._pipe = pipe
         self._fileno = pipe.fileno()
+        mode = os.fstat(self._fileno).st_mode
+        if not (stat.S_ISFIFO(mode) or stat.S_ISSOCK(mode)):
+            raise ValueError("Pipe transport is for pipes/sockets only.")
         _set_nonblocking(self._fileno)
         self._protocol = protocol
         self._closing = False
@@ -275,21 +278,29 @@
         self._loop = loop
         self._pipe = pipe
         self._fileno = pipe.fileno()
-        if not stat.S_ISFIFO(os.fstat(self._fileno).st_mode):
-            raise ValueError("Pipe transport is for pipes only.")
+        mode = os.fstat(self._fileno).st_mode
+        is_socket = stat.S_ISSOCK(mode)
+        is_pipe = stat.S_ISFIFO(mode)
+        if not (is_socket or is_pipe):
+            raise ValueError("Pipe transport is for pipes/sockets only.")
         _set_nonblocking(self._fileno)
         self._protocol = protocol
         self._buffer = []
         self._conn_lost = 0
         self._closing = False  # Set when close() or write_eof() called.
-        self._loop.add_reader(self._fileno, self._read_ready)
+
+        # On AIX, the reader trick only works for sockets.
+        # On other platforms it works for pipes and sockets.
+        # (Exception: OS X 10.4?  Issue #19294.)
+        if is_socket or not sys.platform.startswith("aix"):
+            self._loop.add_reader(self._fileno, self._read_ready)
 
         self._loop.call_soon(self._protocol.connection_made, self)
         if waiter is not None:
             self._loop.call_soon(waiter.set_result, None)
 
     def _read_ready(self):
-        # pipe was closed by peer
+        # Pipe was closed by peer.
         self._close()
 
     def write(self, data):
@@ -435,8 +446,15 @@
         self._loop = loop
 
         self._pipes = {}
+        stdin_w = None
         if stdin == subprocess.PIPE:
             self._pipes[STDIN] = None
+            # Use a socket pair for stdin, since not all platforms
+            # support selecting read events on the write end of a
+            # socket (which we use in order to detect closing of the
+            # other end).  Notably this is needed on AIX, and works
+            # just fine on other platforms.
+            stdin, stdin_w = self._loop._socketpair()
         if stdout == subprocess.PIPE:
             self._pipes[STDOUT] = None
         if stderr == subprocess.PIPE:
@@ -448,6 +466,9 @@
         self._proc = subprocess.Popen(
             args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
             universal_newlines=False, bufsize=bufsize, **kwargs)
+        if stdin_w is not None:
+            stdin.close()
+            self._proc.stdin = open(stdin_w.detach(), 'rb', buffering=bufsize)
         self._extra['subprocess'] = self._proc
 
     def close(self):
diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py
--- a/Lib/test/test_asyncio/test_unix_events.py
+++ b/Lib/test/test_asyncio/test_unix_events.py
@@ -312,6 +312,13 @@
         fcntl_patcher.start()
         self.addCleanup(fcntl_patcher.stop)
 
+        fstat_patcher = unittest.mock.patch('os.fstat')
+        m_fstat = fstat_patcher.start()
+        st = unittest.mock.Mock()
+        st.st_mode = stat.S_IFIFO
+        m_fstat.return_value = st
+        self.addCleanup(fstat_patcher.stop)
+
     def test_ctor(self):
         tr = unix_events._UnixReadPipeTransport(
             self.loop, self.pipe, self.protocol)

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list