WIP: Fix carriage return

This commit is contained in:
a-kenji
2025-10-07 09:16:22 +02:00
parent 1cb7c7d25f
commit 27d9a805d9
2 changed files with 83 additions and 4 deletions

View File

@@ -126,9 +126,19 @@ def handle_io(
# If Log.STDOUT is set, log the stdout output
if ret and log in [Log.STDOUT, Log.BOTH]:
lines = ret.decode("utf-8", "replace").rstrip("\n").rstrip().split("\n")
decoded = ret.decode("utf-8", "replace").rstrip("\n").rstrip()
# Handle carriage returns: split by \r and only keep the last segment of each line
# 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:
cmdlog.info(line, extra=stdout_extra)
if line: # Only log non-empty lines
cmdlog.info(line, extra=stdout_extra)
# If stdout file is set, stream the stdout output
if ret and stdout:
@@ -143,9 +153,19 @@ def handle_io(
# If Log.STDERR is set, log the stderr output
if ret and log in [Log.STDERR, Log.BOTH]:
lines = ret.decode("utf-8", "replace").rstrip("\n").rstrip().split("\n")
decoded = ret.decode("utf-8", "replace").rstrip("\n").rstrip()
# Handle carriage returns: split by \r and only keep the last segment of each line
# 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:
cmdlog.info(line, extra=stderr_extra)
if line: # Only log non-empty lines
cmdlog.info(line, extra=stderr_extra)
# If stderr file is set, stream the stderr output
if ret and stderr:

View File

@@ -0,0 +1,59 @@
import logging
from pathlib import Path
from clan_lib.cmd import Log, RunOpts, run
def test_carriage_return_handling(caplog: logging.LogRecord) -> None:
"""Test that carriage returns are handled properly to avoid duplicate progress lines."""
# Set logging to capture INFO level
caplog.set_level(logging.INFO)
# Run a command that simulates mkfs.ext4 progress output with carriage returns
result = run(
[
"bash",
"-c",
'printf "Progress: 1/5\\rProgress: 2/5\\rProgress: 3/5\\rProgress: 4/5\\rProgress: 5/5\\n"',
],
RunOpts(log=Log.STDOUT, cwd=Path.cwd()),
)
# Check that the command succeeded
assert result.returncode == 0
# Check that only the final progress line was logged, not all intermediate ones
log_messages = [record.message for record in caplog.records]
# Should only see the final "Progress: 5/5" message, not all the intermediate ones
assert "Progress: 5/5" in log_messages
# Count how many "Progress:" messages there are - should be only 1
progress_messages = [msg for msg in log_messages if "Progress:" in msg]
assert len(progress_messages) == 1, f"Expected 1 progress message, got {len(progress_messages)}: {progress_messages}"
def test_carriage_return_multiple_lines(caplog: logging.LogRecord) -> None:
"""Test carriage returns on multiple separate lines."""
caplog.set_level(logging.INFO)
# Simulate multiple lines with progress indicators
result = run(
[
"bash",
"-c",
'printf "Line 1\\nProgress: 10%%\\rProgress: 50%%\\rProgress: 100%%\\nLine 3\\n"',
],
RunOpts(log=Log.STDOUT, cwd=Path.cwd()),
)
assert result.returncode == 0
log_messages = [record.message for record in caplog.records]
# Should see Line 1, Progress: 100%, and Line 3
assert "Line 1" in log_messages
assert "Progress: 100%" in log_messages
assert "Line 3" in log_messages
# Should NOT see the intermediate progress messages
assert "Progress: 10%" not in log_messages
assert "Progress: 50%" not in log_messages