# Copyright 1999-2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import fcntl import sys from portage import os from _emerge.AbstractPollTask import AbstractPollTask 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", "_use_array") def _start(self): self._read_data = [] for f in self.input_files.values(): fd = f if isinstance(f, int) else f.fileno() fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) # FD_CLOEXEC is enabled by default in Python >=3.4. if sys.hexversion < 0x3040000: try: fcntl.FD_CLOEXEC except AttributeError: pass else: fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.fcntl(fd, fcntl.F_GETFD) | fcntl.FD_CLOEXEC) if self._use_array: self.scheduler.add_reader(fd, self._array_output_handler, f) else: self.scheduler.add_reader(fd, self._output_handler, fd) self._registered = True def _cancel(self): self._unregister() if self.returncode is None: self.returncode = self._cancelled_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): while True: data = self._read_buf(fd) if data is None: break if data: self._read_data.append(data) else: self._unregister() self.returncode = self.returncode or os.EX_OK self._async_wait() break def _array_output_handler(self, f): while True: data = self._read_array(f) if data is None: break if data: self._read_data.append(data) else: self._unregister() self.returncode = self.returncode or os.EX_OK self._async_wait() break return True def _unregister(self): """ Unregister from the scheduler and close open files. """ self._registered = False if self.input_files is not None: for f in self.input_files.values(): if isinstance(f, int): self.scheduler.remove_reader(f) os.close(f) else: self.scheduler.remove_reader(f.fileno()) f.close() self.input_files = None