# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import array import errno import logging import os from portage.util import writemsg_level from _emerge.AsynchronousTask import AsynchronousTask class AbstractPollTask(AsynchronousTask): __slots__ = ("scheduler",) + \ ("_registered",) _bufsize = 4096 @property def _exceptional_events(self): return self.scheduler.IO_ERR | self.scheduler.IO_NVAL @property def _registered_events(self): return self.scheduler.IO_IN | self.scheduler.IO_HUP | \ self._exceptional_events def isAlive(self): return bool(self._registered) def _read_array(self, f, event): """ NOTE: array.fromfile() is used here only for testing purposes, because it has bugs in all known versions of Python (including Python 2.7 and Python 3.2). See PipeReaderArrayTestCase. | POLLIN | RETURN | BIT | VALUE | --------------------------------------------------- | 1 | Read self._bufsize into an instance of | | array.array('B') and return it, handling | | EOFError and IOError. An empty array | | indicates EOF. | --------------------------------------------------- | 0 | None """ buf = None if event & self.scheduler.IO_IN: buf = array.array('B') try: buf.fromfile(f, self._bufsize) except EOFError: pass except TypeError: # Python 3.2: # TypeError: read() didn't return bytes pass except IOError 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 pass elif e.errno == errno.EAGAIN: # EAGAIN: return None buf = None else: raise if buf is not None: try: # Python >=3.2 buf = buf.tobytes() except AttributeError: buf = buf.tostring() return buf def _read_buf(self, fd, event): """ | POLLIN | RETURN | BIT | VALUE | --------------------------------------------------- | 1 | Read self._bufsize into a string of bytes, | | handling EAGAIN and EIO. An empty string | | of bytes indicates EOF. | --------------------------------------------------- | 0 | None """ # NOTE: array.fromfile() is no longer used here because it has # bugs in all known versions of Python (including Python 2.7 # and Python 3.2). buf = None if event & self.scheduler.IO_IN: try: buf = os.read(fd, self._bufsize) 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 return buf def _unregister(self): raise NotImplementedError(self) def _log_poll_exception(self, event): writemsg_level( "!!! %s received strange poll event: %s\n" % \ (self.__class__.__name__, event,), level=logging.ERROR, noiselevel=-1) def _unregister_if_appropriate(self, event): if self._registered: if event & self._exceptional_events: self._log_poll_exception(event) self._unregister() self.cancel() self.wait() elif event & self.scheduler.IO_HUP: self._unregister() self.wait() def _wait(self): if self.returncode is not None: return self.returncode self._wait_loop() return self.returncode def _wait_loop(self, timeout=None): if timeout is None: while self._registered: self.scheduler.iteration() return def timeout_cb(): timeout_cb.timed_out = True return False timeout_cb.timed_out = False timeout_cb.timeout_id = self.scheduler.timeout_add(timeout, timeout_cb) try: while self._registered and not timeout_cb.timed_out: self.scheduler.iteration() finally: self.scheduler.unregister(timeout_cb.timeout_id)