blob: 1aa5ee3bfeff5cf9b7eefa5cae0d4581709ec1f7 [file] [log] [blame]
# 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