# Copyright 1999-2011 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from portage import os from _emerge.AbstractPollTask import AbstractPollTask from _emerge.PollConstants import PollConstants import fcntl import array class PipeReader(AbstractPollTask): """ Reads output from one or more files and saves it in memory, for retrieval via the getvalue() method. This is driven by the scheduler's poll() loop, so it runs entirely within the current process. """ __slots__ = ("input_files",) + \ ("_read_data", "_reg_ids") def _start(self): self._reg_ids = set() self._read_data = [] for k, f in self.input_files.items(): fcntl.fcntl(f.fileno(), fcntl.F_SETFL, fcntl.fcntl(f.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK) self._reg_ids.add(self.scheduler.register(f.fileno(), self._registered_events, self._output_handler)) self._registered = True def isAlive(self): return self._registered def _cancel(self): if self.returncode is None: self.returncode = 1 def _wait(self): if self.returncode is not None: return self.returncode if self._registered: self.scheduler.schedule(self._reg_ids) self._unregister() self.returncode = os.EX_OK return self.returncode def getvalue(self): """Retrieve the entire contents""" return b''.join(self._read_data) def close(self): """Free the memory buffer.""" self._read_data = None def _output_handler(self, fd, event): if event & PollConstants.POLLIN: for f in self.input_files.values(): if fd == f.fileno(): break buf = array.array('B') try: buf.fromfile(f, self._bufsize) except (EOFError, IOError): pass if buf: self._read_data.append(buf.tostring()) else: self._unregister() self.wait() self._unregister_if_appropriate(event) def _unregister(self): """ Unregister from the scheduler and close open files. """ self._registered = False if self._reg_ids is not None: for reg_id in self._reg_ids: self.scheduler.unregister(reg_id) self._reg_ids = None if self.input_files is not None: for f in self.input_files.values(): f.close() self.input_files = None