From 4cc14fc1dc2634f243f86a6d2993e89779ae4117 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Tue, 2 Mar 2010 09:47:26 +0000 Subject: Move _pty module to portage.util._pty. svn path=/main/trunk/; revision=15515 --- pym/_emerge/AbstractEbuildProcess.py | 2 +- pym/_emerge/EbuildFetcher.py | 2 +- pym/portage/package/ebuild/_pty.py | 205 ------------------------------- pym/portage/package/ebuild/doebuild.py | 2 +- pym/portage/tests/ebuild/test_pty_eof.py | 2 +- pym/portage/util/_pty.py | 205 +++++++++++++++++++++++++++++++ 6 files changed, 209 insertions(+), 209 deletions(-) delete mode 100644 pym/portage/package/ebuild/_pty.py create mode 100644 pym/portage/util/_pty.py (limited to 'pym') diff --git a/pym/_emerge/AbstractEbuildProcess.py b/pym/_emerge/AbstractEbuildProcess.py index 0d8be6fbf..64846ff8c 100644 --- a/pym/_emerge/AbstractEbuildProcess.py +++ b/pym/_emerge/AbstractEbuildProcess.py @@ -5,7 +5,7 @@ from _emerge.SpawnProcess import SpawnProcess import portage from portage import os -from portage.package.ebuild._pty import _create_pty_or_pipe +from portage.util._pty import _create_pty_or_pipe class AbstractEbuildProcess(SpawnProcess): diff --git a/pym/_emerge/EbuildFetcher.py b/pym/_emerge/EbuildFetcher.py index eb423ff04..062ef4726 100644 --- a/pym/_emerge/EbuildFetcher.py +++ b/pym/_emerge/EbuildFetcher.py @@ -11,7 +11,7 @@ from portage import _encodings from portage import _unicode_encode import codecs from portage.elog.messages import eerror -from portage.package.ebuild._pty import _create_pty_or_pipe +from portage.util._pty import _create_pty_or_pipe class EbuildFetcher(SpawnProcess): diff --git a/pym/portage/package/ebuild/_pty.py b/pym/portage/package/ebuild/_pty.py deleted file mode 100644 index c48584f58..000000000 --- a/pym/portage/package/ebuild/_pty.py +++ /dev/null @@ -1,205 +0,0 @@ -# Copyright 2010 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Id$ - -import array -import fcntl -import platform -import pty -import select -import sys -import termios - -from portage import os, _unicode_decode, _unicode_encode -from portage.output import get_term_size, set_term_size -from portage.process import spawn_bash -from portage.util import writemsg - -def _can_test_pty_eof(): - """ - The _test_pty_eof() function seems to hang on most - kernels other than Linux. - This was reported for the following kernels which used to work fine - without this EOF test: Darwin, AIX, FreeBSD. They seem to hang on - the slave_file.close() call. Note that Python's implementation of - openpty on Solaris already caused random hangs without this EOF test - and hence is globally disabled. - @rtype: bool - @returns: True if _test_pty_eof() won't hang, False otherwise. - """ - return platform.system() in ("Linux",) - -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. - """ - - use_fork = False - - test_string = 2 * "blah blah blah\n" - test_string = _unicode_decode(test_string, - encoding='utf_8', errors='strict') - - # may raise EnvironmentError - master_fd, slave_fd = pty.openpty() - - # Non-blocking mode is required for Darwin kernel. - fcntl.fcntl(master_fd, fcntl.F_SETFL, - fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK) - - # Disable post-processing of output since otherwise weird - # things like \n -> \r\n transformations may occur. - mode = termios.tcgetattr(slave_fd) - mode[1] &= ~termios.OPOST - termios.tcsetattr(slave_fd, termios.TCSANOW, mode) - - # Simulate a subprocess writing some data to the - # slave end of the pipe, and then exiting. - pid = None - if use_fork: - pids = spawn_bash(_unicode_encode("echo -n '%s'" % test_string, - encoding='utf_8', errors='strict'), env=os.environ, - fd_pipes={0:sys.stdin.fileno(), 1:slave_fd, 2:slave_fd}, - returnpid=True) - if isinstance(pids, int): - os.close(master_fd) - os.close(slave_fd) - raise EnvironmentError('spawn failed') - pid = pids[0] - else: - os.write(slave_fd, _unicode_encode(test_string, - encoding='utf_8', errors='strict')) - os.close(slave_fd) - - # If using a fork, we must wait for the child here, - # in order to avoid a race condition that would - # lead to inconsistent results. - if pid is not None: - os.waitpid(pid, 0) - - master_file = os.fdopen(master_fd, 'rb') - eof = False - data = [] - iwtd = [master_file] - owtd = [] - ewtd = [] - - while not eof: - - events = select.select(iwtd, owtd, ewtd) - if not events[0]: - eof = True - break - - buf = array.array('B') - try: - buf.fromfile(master_file, 1024) - except EOFError: - eof = True - except IOError: - # This is where data loss occurs. - eof = True - - if not buf: - eof = True - else: - data.append(_unicode_decode(buf.tostring(), - encoding='utf_8', errors='strict')) - - master_file.close() - - return test_string == ''.join(data) - -# If _test_pty_eof() can't be used for runtime detection of -# http://bugs.python.org/issue5380, openpty can't safely be used -# unless we can guarantee that the current version of python has -# been fixed (affects all current versions of python3). When -# this issue is fixed in python3, we can add another sys.hexversion -# conditional to enable openpty support in the fixed versions. -if sys.hexversion >= 0x3000000 and not _can_test_pty_eof(): - _disable_openpty = True -else: - # Disable the use of openpty on Solaris as it seems Python's openpty - # implementation doesn't play nice on Solaris with Portage's - # behaviour causing hangs/deadlocks. - # Additional note for the future: on Interix, pipes do NOT work, so - # _disable_openpty on Interix must *never* be True - _disable_openpty = platform.system() in ("SunOS",) -_tested_pty = False - -if not _can_test_pty_eof(): - # Skip _test_pty_eof() on systems where it hangs. - _tested_pty = True - -_fbsd_test_pty = platform.system() == 'FreeBSD' - -def _create_pty_or_pipe(copy_term_size=None): - """ - Try to create a pty and if then fails then create a normal - pipe instead. - - @param copy_term_size: If a tty file descriptor is given - then the term size will be copied to the pty. - @type copy_term_size: int - @rtype: tuple - @returns: A tuple of (is_pty, master_fd, slave_fd) where - is_pty is True if a pty was successfully allocated, and - False if a normal pipe was allocated. - """ - - got_pty = False - - global _disable_openpty, _fbsd_test_pty, _tested_pty - if not (_tested_pty or _disable_openpty): - try: - if not _test_pty_eof(): - _disable_openpty = True - except EnvironmentError as e: - _disable_openpty = True - writemsg("openpty failed: '%s'\n" % str(e), - noiselevel=-1) - del e - _tested_pty = True - - if _fbsd_test_pty and not _disable_openpty: - # Test for python openpty breakage after freebsd7 to freebsd8 - # upgrade, which results in a 'Function not implemented' error - # and the process being killed. - pid = os.fork() - if pid == 0: - pty.openpty() - os._exit(os.EX_OK) - pid, status = os.waitpid(pid, 0) - if (status & 0xff) == 140: - _disable_openpty = True - _fbsd_test_pty = False - - if _disable_openpty: - master_fd, slave_fd = os.pipe() - else: - try: - master_fd, slave_fd = pty.openpty() - got_pty = True - except EnvironmentError as e: - _disable_openpty = True - writemsg("openpty failed: '%s'\n" % str(e), - noiselevel=-1) - del e - master_fd, slave_fd = os.pipe() - - if got_pty: - # Disable post-processing of output since otherwise weird - # things like \n -> \r\n transformations may occur. - mode = termios.tcgetattr(slave_fd) - mode[1] &= ~termios.OPOST - termios.tcsetattr(slave_fd, termios.TCSANOW, mode) - - if got_pty and \ - copy_term_size is not None and \ - os.isatty(copy_term_size): - rows, columns = get_term_size() - set_term_size(rows, columns, slave_fd) - - return (got_pty, master_fd, slave_fd) diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py index e316c808b..d25ebb693 100644 --- a/pym/portage/package/ebuild/doebuild.py +++ b/pym/portage/package/ebuild/doebuild.py @@ -49,10 +49,10 @@ from portage.manifest import Manifest from portage.output import style_to_ansi_code from portage.package.ebuild.fetch import fetch from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs -from portage.package.ebuild._pty import _create_pty_or_pipe from portage.util import apply_recursive_permissions, \ apply_secpass_permissions, noiselimit, normalize_path, \ writemsg, writemsg_stdout, write_atomic +from portage.util._pty import _create_pty_or_pipe from portage.versions import _pkgsplit def doebuild_environment(myebuild, mydo, myroot, mysettings, diff --git a/pym/portage/tests/ebuild/test_pty_eof.py b/pym/portage/tests/ebuild/test_pty_eof.py index b29c8f95f..6280eb263 100644 --- a/pym/portage/tests/ebuild/test_pty_eof.py +++ b/pym/portage/tests/ebuild/test_pty_eof.py @@ -4,7 +4,7 @@ import portage from portage.tests import TestCase -from portage.package.ebuild._pty import _can_test_pty_eof, _test_pty_eof +from portage.util._pty import _can_test_pty_eof, _test_pty_eof class PtyEofTestCase(TestCase): diff --git a/pym/portage/util/_pty.py b/pym/portage/util/_pty.py new file mode 100644 index 000000000..c48584f58 --- /dev/null +++ b/pym/portage/util/_pty.py @@ -0,0 +1,205 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import array +import fcntl +import platform +import pty +import select +import sys +import termios + +from portage import os, _unicode_decode, _unicode_encode +from portage.output import get_term_size, set_term_size +from portage.process import spawn_bash +from portage.util import writemsg + +def _can_test_pty_eof(): + """ + The _test_pty_eof() function seems to hang on most + kernels other than Linux. + This was reported for the following kernels which used to work fine + without this EOF test: Darwin, AIX, FreeBSD. They seem to hang on + the slave_file.close() call. Note that Python's implementation of + openpty on Solaris already caused random hangs without this EOF test + and hence is globally disabled. + @rtype: bool + @returns: True if _test_pty_eof() won't hang, False otherwise. + """ + return platform.system() in ("Linux",) + +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. + """ + + use_fork = False + + test_string = 2 * "blah blah blah\n" + test_string = _unicode_decode(test_string, + encoding='utf_8', errors='strict') + + # may raise EnvironmentError + master_fd, slave_fd = pty.openpty() + + # Non-blocking mode is required for Darwin kernel. + fcntl.fcntl(master_fd, fcntl.F_SETFL, + fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK) + + # Disable post-processing of output since otherwise weird + # things like \n -> \r\n transformations may occur. + mode = termios.tcgetattr(slave_fd) + mode[1] &= ~termios.OPOST + termios.tcsetattr(slave_fd, termios.TCSANOW, mode) + + # Simulate a subprocess writing some data to the + # slave end of the pipe, and then exiting. + pid = None + if use_fork: + pids = spawn_bash(_unicode_encode("echo -n '%s'" % test_string, + encoding='utf_8', errors='strict'), env=os.environ, + fd_pipes={0:sys.stdin.fileno(), 1:slave_fd, 2:slave_fd}, + returnpid=True) + if isinstance(pids, int): + os.close(master_fd) + os.close(slave_fd) + raise EnvironmentError('spawn failed') + pid = pids[0] + else: + os.write(slave_fd, _unicode_encode(test_string, + encoding='utf_8', errors='strict')) + os.close(slave_fd) + + # If using a fork, we must wait for the child here, + # in order to avoid a race condition that would + # lead to inconsistent results. + if pid is not None: + os.waitpid(pid, 0) + + master_file = os.fdopen(master_fd, 'rb') + eof = False + data = [] + iwtd = [master_file] + owtd = [] + ewtd = [] + + while not eof: + + events = select.select(iwtd, owtd, ewtd) + if not events[0]: + eof = True + break + + buf = array.array('B') + try: + buf.fromfile(master_file, 1024) + except EOFError: + eof = True + except IOError: + # This is where data loss occurs. + eof = True + + if not buf: + eof = True + else: + data.append(_unicode_decode(buf.tostring(), + encoding='utf_8', errors='strict')) + + master_file.close() + + return test_string == ''.join(data) + +# If _test_pty_eof() can't be used for runtime detection of +# http://bugs.python.org/issue5380, openpty can't safely be used +# unless we can guarantee that the current version of python has +# been fixed (affects all current versions of python3). When +# this issue is fixed in python3, we can add another sys.hexversion +# conditional to enable openpty support in the fixed versions. +if sys.hexversion >= 0x3000000 and not _can_test_pty_eof(): + _disable_openpty = True +else: + # Disable the use of openpty on Solaris as it seems Python's openpty + # implementation doesn't play nice on Solaris with Portage's + # behaviour causing hangs/deadlocks. + # Additional note for the future: on Interix, pipes do NOT work, so + # _disable_openpty on Interix must *never* be True + _disable_openpty = platform.system() in ("SunOS",) +_tested_pty = False + +if not _can_test_pty_eof(): + # Skip _test_pty_eof() on systems where it hangs. + _tested_pty = True + +_fbsd_test_pty = platform.system() == 'FreeBSD' + +def _create_pty_or_pipe(copy_term_size=None): + """ + Try to create a pty and if then fails then create a normal + pipe instead. + + @param copy_term_size: If a tty file descriptor is given + then the term size will be copied to the pty. + @type copy_term_size: int + @rtype: tuple + @returns: A tuple of (is_pty, master_fd, slave_fd) where + is_pty is True if a pty was successfully allocated, and + False if a normal pipe was allocated. + """ + + got_pty = False + + global _disable_openpty, _fbsd_test_pty, _tested_pty + if not (_tested_pty or _disable_openpty): + try: + if not _test_pty_eof(): + _disable_openpty = True + except EnvironmentError as e: + _disable_openpty = True + writemsg("openpty failed: '%s'\n" % str(e), + noiselevel=-1) + del e + _tested_pty = True + + if _fbsd_test_pty and not _disable_openpty: + # Test for python openpty breakage after freebsd7 to freebsd8 + # upgrade, which results in a 'Function not implemented' error + # and the process being killed. + pid = os.fork() + if pid == 0: + pty.openpty() + os._exit(os.EX_OK) + pid, status = os.waitpid(pid, 0) + if (status & 0xff) == 140: + _disable_openpty = True + _fbsd_test_pty = False + + if _disable_openpty: + master_fd, slave_fd = os.pipe() + else: + try: + master_fd, slave_fd = pty.openpty() + got_pty = True + except EnvironmentError as e: + _disable_openpty = True + writemsg("openpty failed: '%s'\n" % str(e), + noiselevel=-1) + del e + master_fd, slave_fd = os.pipe() + + if got_pty: + # Disable post-processing of output since otherwise weird + # things like \n -> \r\n transformations may occur. + mode = termios.tcgetattr(slave_fd) + mode[1] &= ~termios.OPOST + termios.tcsetattr(slave_fd, termios.TCSANOW, mode) + + if got_pty and \ + copy_term_size is not None and \ + os.isatty(copy_term_size): + rows, columns = get_term_size() + set_term_size(rows, columns, slave_fd) + + return (got_pty, master_fd, slave_fd) -- cgit v1.2.3-1-g7c22