def __rand__(self, cmd): with cmd.bgrun(retcode=self.retcode, stdin=None, stdout=PIPE, stderr=PIPE) as p: outbuf = [] errbuf = [] out = p.stdout err = p.stderr buffers = {out: outbuf, err: errbuf} tee_to = {out: sys.stdout, err: sys.stderr} while p.poll() is None: ready, _, _ = select((out, err), (), ()) for fd in ready: buf = buffers[fd] data, text = read_fd_decode_safely(fd, 4096) if not data: # eof continue # Python conveniently line-buffers stdout and stderr for # us, so all we need to do is write to them # This will automatically add up to three bytes if it cannot be decoded tee_to[fd].write(text) # And then "unbuffered" is just flushing after each write if not self.buffered: tee_to[fd].flush() buf.append(data) stdout = ''.join([x.decode('utf-8') for x in outbuf]) stderr = ''.join([x.decode('utf-8') for x in errbuf]) return p.returncode, stdout, stderr
def __rand__(self, cmd): with cmd.bgrun(retcode=self.retcode, stdin=None, stdout=PIPE, stderr=PIPE) as p: outbuf = [] errbuf = [] out = p.stdout err = p.stderr buffers = {out: outbuf, err: errbuf} tee_to = {out: sys.stdout, err: sys.stderr} while p.poll() is None: ready, _, _ = select((out, err), (), ()) for fd in ready: buf = buffers[fd] data, text = read_fd_decode_safely(fd, 4096) if not data: # eof continue # Python conveniently line-buffers stdout and stderr for # us, so all we need to do is write to them # This will automatically add up to three bytes if it cannot be decoded tee_to[fd].write(text) # And then "unbuffered" is just flushing after each write if not self.buffered: tee_to[fd].flush() buf.append(data) stdout = "".join([x.decode("utf-8") for x in outbuf]) stderr = "".join([x.decode("utf-8") for x in errbuf]) return p.returncode, stdout, stderr
def tee(process: PlumbumLocalPopen, buffered: bool = True) -> tp.Tuple[int, str, str]: """ Adapted from plumbum's TEE implementation. Plumbum's TEE does not allow access to the underlying popen object, which we need to properly handle keyboard interrupts. Therefore, we just copy the relevant portion of plumbum's implementation and create the popen object by ourself. """ outbuf: tp.List[bytes] = [] errbuf: tp.List[bytes] = [] out = process.stdout err = process.stderr buffers = {out: outbuf, err: errbuf} tee_to = {out: sys.stdout, err: sys.stderr} done = False while not done: # After the process exits, we have to do one more # round of reading in order to drain any data in the # pipe buffer. Thus, we check poll() here, # unconditionally enter the read loop, and only then # break out of the outer loop if the process has # exited. done = process.poll() is not None # We continue this loop until we've done a full # `select()` call without collecting any input. This # ensures that our final pass -- after process exit -- # actually drains the pipe buffers, even if it takes # multiple calls to read(). progress = True while progress: progress = False ready, _, _ = select((out, err), (), ()) # logging.info(f"Streams ready: {[r.fileno() for r in ready]}") for file_descriptor in ready: buf = buffers[file_descriptor] data, text = read_fd_decode_safely(file_descriptor, 4096) if not data: # eof continue progress = True # Python conveniently line-buffers stdout and stderr for # us, so all we need to do is write to them # This will automatically add up to three bytes if it cannot be # decoded tee_to[file_descriptor].write(text) # And then "unbuffered" is just flushing after each write if not buffered: tee_to[file_descriptor].flush() buf.append(data) stdout = "".join([x.decode("utf-8") for x in outbuf]) stderr = "".join([x.decode("utf-8") for x in errbuf]) return process.returncode, stdout, stderr
def __rand__(self, cmd): with cmd.bgrun( retcode=self.retcode, stdin=None, stdout=PIPE, stderr=PIPE, timeout=self.timeout, ) as p: outbuf = [] errbuf = [] out = p.stdout err = p.stderr buffers = {out: outbuf, err: errbuf} tee_to = {out: sys.stdout, err: sys.stderr} done = False while not done: # After the process exits, we have to do one more # round of reading in order to drain any data in the # pipe buffer. Thus, we check poll() here, # unconditionally enter the read loop, and only then # break out of the outer loop if the process has # exited. done = p.poll() is not None # We continue this loop until we've done a full # `select()` call without collecting any input. This # ensures that our final pass -- after process exit -- # actually drains the pipe buffers, even if it takes # multiple calls to read(). progress = True while progress: progress = False ready, _, _ = select((out, err), (), ()) for fd in ready: buf = buffers[fd] data, text = read_fd_decode_safely(fd, 4096) if not data: # eof continue progress = True # Python conveniently line-buffers stdout and stderr for # us, so all we need to do is write to them # This will automatically add up to three bytes if it cannot be decoded tee_to[fd].write(text) # And then "unbuffered" is just flushing after each write if not self.buffered: tee_to[fd].flush() buf.append(data) stdout = "".join([x.decode("utf-8") for x in outbuf]) stderr = "".join([x.decode("utf-8") for x in errbuf]) return p.returncode, stdout, stderr
def __rand__(self, cmd): with cmd.bgrun( retcode=self.retcode, stdin=None, stdout=PIPE, stderr=PIPE, timeout=self.timeout) as p: outbuf = [] errbuf = [] out = p.stdout err = p.stderr buffers = {out: outbuf, err: errbuf} tee_to = {out: sys.stdout, err: sys.stderr} done = False while not done: # After the process exits, we have to do one more # round of reading in order to drain any data in the # pipe buffer. Thus, we check poll() here, # unconditionally enter the read loop, and only then # break out of the outer loop if the process has # exited. done = (p.poll() is not None) # We continue this loop until we've done a full # `select()` call without collecting any input. This # ensures that our final pass -- after process exit -- # actually drains the pipe buffers, even if it takes # multiple calls to read(). progress = True while progress: progress = False ready, _, _ = select((out, err), (), ()) for fd in ready: buf = buffers[fd] data, text = read_fd_decode_safely(fd, 4096) if not data: # eof continue progress = True # Python conveniently line-buffers stdout and stderr for # us, so all we need to do is write to them # This will automatically add up to three bytes if it cannot be decoded tee_to[fd].write(text) # And then "unbuffered" is just flushing after each write if not self.buffered: tee_to[fd].flush() buf.append(data) stdout = ''.join([x.decode('utf-8') for x in outbuf]) stderr = ''.join([x.decode('utf-8') for x in errbuf]) return p.returncode, stdout, stderr