_test_pty_eof: use os.read, not array.fromfile
authorZac Medico <zmedico@gentoo.org>
Fri, 16 Dec 2011 22:37:37 +0000 (14:37 -0800)
committerZac Medico <zmedico@gentoo.org>
Fri, 16 Dec 2011 22:37:37 +0000 (14:37 -0800)
We have abandoned array.fromfile() due to bugs that exist in all known
versions of Python (including Python 2.7 and Python 3.2). See
PipeReaderArrayTestCase, for example.

pym/portage/tests/ebuild/test_pty_eof.py
pym/portage/util/_pty.py

index 0b0bbdb357e727f38c91d940498dbff5461f191e..fe218b6fa97eb0f1d8aba5083383250ce95fda6a 100644 (file)
@@ -1,14 +1,12 @@
 # Copyright 2009-2011 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
-import platform
-
 from portage.tests import TestCase
 from portage.util._pty import _can_test_pty_eof, _test_pty_eof
 
-class PtyEofFdopenBufferedTestCase(TestCase):
+class PtyEofTestCase(TestCase):
 
-       def testPtyEofFdopenBuffered(self):
+       def testPtyEof(self):
 
                if not _can_test_pty_eof():
                        skip_reason = "unsupported on this platform"
@@ -20,29 +18,6 @@ class PtyEofFdopenBufferedTestCase(TestCase):
                #   http://bugs.python.org/issue5380
                # Since it might not be fixed, mark as todo.
                self.todo = True
-               # The result is only valid if openpty does not raise EnvironmentError.
-               if _can_test_pty_eof():
-                       try:
-                               self.assertEqual(_test_pty_eof(fdopen_buffered=True), True)
-                       except EnvironmentError:
-                               pass
-
-class PtyEofFdopenUnBufferedTestCase(TestCase):
-       def testPtyEofFdopenUnBuffered(self):
-               # New development: It appears that array.fromfile() is usable
-               # with python3 as long as fdopen is called with a bufsize
-               # argument of 0.
-
-               if not _can_test_pty_eof():
-                       skip_reason = "unsupported on this platform"
-                       self.portage_skip = skip_reason
-                       self.fail(skip_reason)
-                       return
-
-               if platform.python_implementation() == 'PyPy':
-                       # https://bugs.pypy.org/issue956
-                       self.todo = True
-
                # The result is only valid if openpty does not raise EnvironmentError.
                if _can_test_pty_eof():
                        try:
index af0fc9d8afa157d174a085a18b08e3c7abb7d997..97a38c0c381ac194af3f3fed25a197def550eb7b 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright 2010-2011 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
-import array
+import errno
 import fcntl
 import platform
 import pty
@@ -28,19 +28,22 @@ def _can_test_pty_eof():
        """
        return platform.system() in ("Linux",)
 
-def _test_pty_eof(fdopen_buffered=False):
+def _test_pty_eof():
        """
-       Returns True if this issues is fixed for the currently
-       running version of python: http://bugs.python.org/issue5380
-       Raises an EnvironmentError from openpty() if it fails.
+       Returns True if EOF appears to be handled correctly with pty
+       devices. Raises an EnvironmentError from openpty() if it fails.
 
-       NOTE: This issue is only problematic when array.fromfile()
-       is used, rather than os.read(). However, array.fromfile()
-       is preferred since it is approximately 10% faster.
+       This used to be used to detect if the following issue was fixed
+       in the currently running version of python:
 
-       New development: It appears that array.fromfile() is usable
-       with python3 as long as fdopen is called with a bufsize
-       argument of 0.
+               http://bugs.python.org/issue5380
+
+       However, array.fromfile() use has since been abandoned due to
+       bugs that exist in all known versions of Python (including Python
+       2.7 and Python 3.2). See PipeReaderArrayTestCase, for example.
+       This is somewhat unfortunate, since the combination of 
+       array.fromfile() and array.tofile() is approximately 10% faster
+       than the combination of os.read() and os.write().
        """
 
        use_fork = False
@@ -86,39 +89,42 @@ def _test_pty_eof(fdopen_buffered=False):
        if pid is not None:
                os.waitpid(pid, 0)
 
-       if fdopen_buffered:
-               master_file = os.fdopen(master_fd, 'rb')
-       else:
-               master_file = os.fdopen(master_fd, 'rb', 0)
-       eof = False
        data = []
-       iwtd = [master_file]
+       iwtd = [master_fd]
        owtd = []
        ewtd = []
 
-       while not eof:
+       while True:
 
                events = select.select(iwtd, owtd, ewtd)
                if not events[0]:
-                       eof = True
+                       # EOF
                        break
 
-               buf = array.array('B')
+               buf = None
                try:
-                       buf.fromfile(master_file, 1024)
-               except (EOFError, IOError):
-                       eof = True
-
-               if not buf:
-                       eof = True
+                       buf = os.read(master_fd, 1024)
+               except OSError as e:
+                       # EIO happens with pty on Linux after the
+                       # slave end of the pty has been closed.
+                       if e.errno == errno.EIO:
+                               # EOF: return empty string of bytes
+                               buf = b''
+                       elif e.errno == errno.EAGAIN:
+                               # EAGAIN: return None
+                               buf = None
+                       else:
+                               raise
+
+               if buf is None:
+                       pass
+               elif not buf:
+                       # EOF
+                       break
                else:
-                       try:
-                               # Python >=3.2
-                               data.append(buf.tobytes())
-                       except AttributeError:
-                               data.append(buf.tostring())
+                       data.append(buf)
 
-       master_file.close()
+       os.close(master_fd)
 
        return test_string == _unicode_decode(b''.join(data), encoding='utf_8', errors='strict')