WIP: Fix carriage return
This commit is contained in:
@@ -126,9 +126,19 @@ 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]:
|
||||||
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:
|
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 stdout file is set, stream the stdout output
|
||||||
if ret and stdout:
|
if ret and stdout:
|
||||||
@@ -143,9 +153,19 @@ 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]:
|
||||||
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:
|
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 stderr file is set, stream the stderr output
|
||||||
if ret and stderr:
|
if ret and stderr:
|
||||||
|
|||||||
59
pkgs/clan-cli/clan_lib/cmd/test_carriage_return.py
Normal file
59
pkgs/clan-cli/clan_lib/cmd/test_carriage_return.py
Normal 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
|
||||||
Reference in New Issue
Block a user