From 9f84f1333bb1b6ee32083fba39da5ce616f2657c Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 16 Dec 2011 14:37:37 -0800 Subject: _test_pty_eof: use os.read, not array.fromfile 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 | 29 +------------ pym/portage/util/_pty.py | 70 +++++++++++++++++--------------- 2 files changed, 40 insertions(+), 59 deletions(-) diff --git a/pym/portage/tests/ebuild/test_pty_eof.py b/pym/portage/tests/ebuild/test_pty_eof.py index 0b0bbdb35..fe218b6fa 100644 --- a/pym/portage/tests/ebuild/test_pty_eof.py +++ b/pym/portage/tests/ebuild/test_pty_eof.py @@ -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: diff --git a/pym/portage/util/_pty.py b/pym/portage/util/_pty.py index af0fc9d8a..97a38c0c3 100644 --- a/pym/portage/util/_pty.py +++ b/pym/portage/util/_pty.py @@ -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') -- cgit v1.2.3-1-g7c22