Files
Raptor/validation/subprocess_utils.py
T
NiccoloN 57f0cca8c0
Validate Operations / validate-operations (push) Has been cancelled
remove duplicated code
quieter validation scripts (with optional verbose flag)
2026-05-11 15:52:26 +02:00

105 lines
3.3 KiB
Python

import errno
import os
import pty
import selectors
import subprocess
MAX_ERROR_OUTPUT_BYTES = 8192
def _read_chunk(fd, treat_eio_as_eof=False):
try:
return os.read(fd, 4096)
except OSError as exc:
if treat_eio_as_eof and exc.errno == errno.EIO:
return b""
raise
def _stream_output(fd, process, reporter, treat_eio_as_eof=False, stream_output=True):
selector = selectors.DefaultSelector()
recent_output = bytearray()
captured_output = bytearray()
try:
selector.register(fd, selectors.EVENT_READ)
while selector.get_map():
for key, _ in selector.select():
data = _read_chunk(key.fileobj, treat_eio_as_eof=treat_eio_as_eof)
if not data:
selector.unregister(key.fileobj)
os.close(key.fileobj)
continue
if stream_output:
reporter._clear()
os.write(1, data)
reporter._render()
captured_output.extend(data)
if stream_output:
recent_output.extend(data)
if len(recent_output) > MAX_ERROR_OUTPUT_BYTES:
del recent_output[:-MAX_ERROR_OUTPUT_BYTES]
finally:
selector.close()
return_code = process.wait()
if return_code != 0:
error_output = captured_output if not stream_output else recent_output
raise subprocess.CalledProcessError(return_code, process.args, output=bytes(error_output))
return bytes(captured_output)
def run_command_with_reporter(cmd, cwd=None, reporter=None, capture_output=False):
if reporter is None:
if capture_output:
completed = subprocess.run(
cmd,
cwd=cwd,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
return completed.stdout.decode("utf-8", errors="replace")
subprocess.run(cmd, cwd=cwd, check=True)
return None
stream_output = bool(getattr(reporter, "verbose", False))
if not stream_output:
process = subprocess.Popen(
cmd,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
assert process.stdout is not None
output = _stream_output(process.stdout.fileno(), process, reporter, stream_output=False)
return output.decode("utf-8", errors="replace") if capture_output else None
try:
master_fd, slave_fd = pty.openpty()
except OSError:
process = subprocess.Popen(
cmd,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
assert process.stdout is not None
output = _stream_output(process.stdout.fileno(), process, reporter)
return output.decode("utf-8", errors="replace") if capture_output else None
try:
process = subprocess.Popen(
cmd,
cwd=cwd,
stdout=slave_fd,
stderr=slave_fd,
)
finally:
os.close(slave_fd)
output = _stream_output(master_fd, process, reporter, treat_eio_as_eof=True)
return output.decode("utf-8", errors="replace") if capture_output else None