s
This commit is contained in:
@@ -74,6 +74,9 @@ def handle_io(
|
|||||||
) # wlist is a list of file descriptors to be monitored for write events
|
) # wlist is a list of file descriptors to be monitored for write events
|
||||||
stdout_buf = b""
|
stdout_buf = b""
|
||||||
stderr_buf = b""
|
stderr_buf = b""
|
||||||
|
# Buffers for incomplete lines (no trailing newline yet)
|
||||||
|
stdout_line_buf = ""
|
||||||
|
stderr_line_buf = ""
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
# Function to handle file descriptors
|
# Function to handle file descriptors
|
||||||
@@ -85,6 +88,40 @@ def handle_io(
|
|||||||
rlist.remove(fd)
|
rlist.remove(fd)
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
|
# Function to process output with proper carriage return handling
|
||||||
|
def process_output(
|
||||||
|
chunk: bytes, line_buf: str, extra: dict[str, str], cmdlog_func: Any
|
||||||
|
) -> str:
|
||||||
|
"""Process output chunk, handling carriage returns properly.
|
||||||
|
Returns the updated line buffer (incomplete lines).
|
||||||
|
"""
|
||||||
|
if not chunk:
|
||||||
|
return line_buf
|
||||||
|
|
||||||
|
# Decode the chunk and append to line buffer
|
||||||
|
decoded = chunk.decode("utf-8", "replace")
|
||||||
|
line_buf += decoded
|
||||||
|
|
||||||
|
# Split by newlines to get complete lines
|
||||||
|
lines = line_buf.split("\n")
|
||||||
|
|
||||||
|
# The last element might be an incomplete line
|
||||||
|
line_buf = lines[-1]
|
||||||
|
complete_lines = lines[:-1]
|
||||||
|
|
||||||
|
# Process each complete line
|
||||||
|
for line in complete_lines:
|
||||||
|
if "\r" in line:
|
||||||
|
# Handle carriage return: only keep the last segment after final \r
|
||||||
|
# This is what would be visible on a terminal
|
||||||
|
visible_line = line.split("\r")[-1]
|
||||||
|
if visible_line: # Only log non-empty lines
|
||||||
|
cmdlog_func(visible_line, extra=extra)
|
||||||
|
elif line: # Only log non-empty lines
|
||||||
|
cmdlog_func(line, extra=extra)
|
||||||
|
|
||||||
|
return line_buf
|
||||||
|
|
||||||
# Extra information passed to the logger
|
# Extra information passed to the logger
|
||||||
stdout_extra = {}
|
stdout_extra = {}
|
||||||
stderr_extra = {}
|
stderr_extra = {}
|
||||||
@@ -126,19 +163,9 @@ def handle_io(
|
|||||||
|
|
||||||
# If Log.STDOUT is set, log the stdout output
|
# If Log.STDOUT is set, log the stdout output
|
||||||
if ret and log in [Log.STDOUT, Log.BOTH]:
|
if ret and log in [Log.STDOUT, Log.BOTH]:
|
||||||
decoded = ret.decode("utf-8", "replace").rstrip("\n").rstrip()
|
stdout_line_buf = process_output(
|
||||||
# Handle carriage returns: split by \r and only keep the last segment of each line
|
ret, stdout_line_buf, stdout_extra, cmdlog.info
|
||||||
# This prevents progress indicators that use \r from creating multiple log lines
|
)
|
||||||
lines = []
|
|
||||||
for line in decoded.split("\n"):
|
|
||||||
if "\r" in line:
|
|
||||||
# Only keep the last segment after the final \r (what would be visible)
|
|
||||||
lines.append(line.split("\r")[-1])
|
|
||||||
else:
|
|
||||||
lines.append(line)
|
|
||||||
for line in lines:
|
|
||||||
if line: # Only log non-empty lines
|
|
||||||
cmdlog.info(line, extra=stdout_extra)
|
|
||||||
|
|
||||||
# If stdout file is set, stream the stdout output
|
# If stdout file is set, stream the stdout output
|
||||||
if ret and stdout:
|
if ret and stdout:
|
||||||
@@ -153,19 +180,9 @@ def handle_io(
|
|||||||
|
|
||||||
# If Log.STDERR is set, log the stderr output
|
# If Log.STDERR is set, log the stderr output
|
||||||
if ret and log in [Log.STDERR, Log.BOTH]:
|
if ret and log in [Log.STDERR, Log.BOTH]:
|
||||||
decoded = ret.decode("utf-8", "replace").rstrip("\n").rstrip()
|
stderr_line_buf = process_output(
|
||||||
# Handle carriage returns: split by \r and only keep the last segment of each line
|
ret, stderr_line_buf, stderr_extra, cmdlog.info
|
||||||
# This prevents progress indicators that use \r from creating multiple log lines
|
)
|
||||||
lines = []
|
|
||||||
for line in decoded.split("\n"):
|
|
||||||
if "\r" in line:
|
|
||||||
# Only keep the last segment after the final \r (what would be visible)
|
|
||||||
lines.append(line.split("\r")[-1])
|
|
||||||
else:
|
|
||||||
lines.append(line)
|
|
||||||
for line in lines:
|
|
||||||
if line: # Only log non-empty lines
|
|
||||||
cmdlog.info(line, extra=stderr_extra)
|
|
||||||
|
|
||||||
# If stderr file is set, stream the stderr output
|
# If stderr file is set, stream the stderr output
|
||||||
if ret and stderr:
|
if ret and stderr:
|
||||||
@@ -193,6 +210,24 @@ def handle_io(
|
|||||||
process.stdin.close()
|
process.stdin.close()
|
||||||
else:
|
else:
|
||||||
wlist.remove(process.stdin)
|
wlist.remove(process.stdin)
|
||||||
|
|
||||||
|
# Flush any remaining buffered lines at the end
|
||||||
|
if stdout_line_buf and log in [Log.STDOUT, Log.BOTH]:
|
||||||
|
if "\r" in stdout_line_buf:
|
||||||
|
visible_line = stdout_line_buf.split("\r")[-1]
|
||||||
|
if visible_line:
|
||||||
|
cmdlog.info(visible_line, extra=stdout_extra)
|
||||||
|
elif stdout_line_buf:
|
||||||
|
cmdlog.info(stdout_line_buf, extra=stdout_extra)
|
||||||
|
|
||||||
|
if stderr_line_buf and log in [Log.STDERR, Log.BOTH]:
|
||||||
|
if "\r" in stderr_line_buf:
|
||||||
|
visible_line = stderr_line_buf.split("\r")[-1]
|
||||||
|
if visible_line:
|
||||||
|
cmdlog.info(visible_line, extra=stderr_extra)
|
||||||
|
elif stderr_line_buf:
|
||||||
|
cmdlog.info(stderr_line_buf, extra=stderr_extra)
|
||||||
|
|
||||||
return stdout_buf.decode("utf-8", "replace"), stderr_buf.decode("utf-8", "replace")
|
return stdout_buf.decode("utf-8", "replace"), stderr_buf.decode("utf-8", "replace")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user