Merge pull request #9084 from ThomasWaldmann/non-tty-progress-master
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / posix_tests (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run

improved tty-less progress reporting (master)
This commit is contained in:
TW 2025-10-16 03:21:17 +02:00 committed by GitHub
commit 5df6021bc2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 19 deletions

View file

@ -149,6 +149,7 @@ Bytes sent to remote: {stats.tx_bytes}
def show_progress(self, item=None, final=False, stream=None, dt=None):
now = time.monotonic()
if dt is None or now - self.last_progress > dt:
stream = stream or sys.stderr
self.last_progress = now
if self.output_json:
if not final:
@ -160,6 +161,14 @@ Bytes sent to remote: {stats.tx_bytes}
data.update({"time": time.time(), "type": "archive_progress", "finished": final})
msg = json.dumps(data)
end = "\n"
elif not stream.isatty():
# Non-TTY output: use normal linefeeds and do not truncate the path.
if not final:
msg = "{0.osize_fmt} O {0.usize_fmt} U {0.nfiles} N ".format(self)
msg += remove_surrogates(item.path) if item else ""
else:
msg = ""
end = "\n"
else:
columns, lines = get_terminal_size()
if not final:
@ -174,7 +183,7 @@ Bytes sent to remote: {stats.tx_bytes}
else:
msg = " " * columns
end = "\r"
print(msg, end=end, file=stream or sys.stderr, flush=True)
print(msg, end=end, file=stream, flush=True)
def is_special(mode):

View file

@ -33,26 +33,51 @@ def test_stats_basic(stats):
assert stats.usize == 20
@pytest.mark.parametrize(
"item_path, update_size, expected_output",
[
("", 0, "20 B O 20 B U 1 N "), # test unchanged 'stats' fixture
("foo", 10**3, "1.02 kB O 20 B U 1 N foo"), # test updated original size and set item path
# test long item path which exceeds 80 characters
("foo" * 40, 10**3, "1.02 kB O 20 B U 1 N foofoofoofoofoofoofoofoofo...foofoofoofoofoofoofoofoofoofoo"),
],
)
def test_stats_progress(item_path, update_size, expected_output, stats, monkeypatch, columns=80):
monkeypatch.setenv("COLUMNS", str(columns))
out = StringIO()
item = Item(path=item_path) if item_path else None
s = expected_output
def test_stats_progress_tty(stats, monkeypatch, columns=80):
class TTYStringIO(StringIO):
def isatty(self):
return True
stats.update(update_size, unique=False)
stats.show_progress(item=item, stream=out)
monkeypatch.setenv("COLUMNS", str(columns))
out = TTYStringIO()
stats.show_progress(stream=out)
s = "20 B O 20 B U 1 N "
buf = " " * (columns - len(s))
assert out.getvalue() == s + buf + "\r"
out = TTYStringIO()
stats.update(10**3, unique=False)
stats.show_progress(item=Item(path="foo"), final=False, stream=out)
s = "1.02 kB O 20 B U 1 N foo"
buf = " " * (columns - len(s))
assert out.getvalue() == s + buf + "\r"
out = TTYStringIO()
stats.show_progress(item=Item(path="foo" * 40), final=False, stream=out)
s = "1.02 kB O 20 B U 1 N foofoofoofoofoofoofoofoofo...foofoofoofoofoofoofoofoofoofoo"
buf = " " * (columns - len(s))
assert out.getvalue() == s + buf + "\r"
def test_stats_progress_file(stats, monkeypatch):
out = StringIO()
stats.show_progress(stream=out)
s = "20 B O 20 B U 1 N "
assert out.getvalue() == s + "\n"
out = StringIO()
stats.update(10**3, unique=False)
path = "foo"
stats.show_progress(item=Item(path=path), final=False, stream=out)
s = f"1.02 kB O 20 B U 1 N {path}"
assert out.getvalue() == s + "\n"
out = StringIO()
path = "foo" * 40
stats.show_progress(item=Item(path=path), final=False, stream=out)
s = f"1.02 kB O 20 B U 1 N {path}"
assert out.getvalue() == s + "\n"
def test_stats_format(stats):
assert (

View file

@ -634,7 +634,7 @@ def test_progress_on(archivers, request):
create_regular_file(archiver.input_path, "file1", size=1024 * 80)
cmd(archiver, "repo-create", RK_ENCRYPTION)
output = cmd(archiver, "create", "test4", "input", "--progress")
assert "\r" in output
assert "0 B O 0 B U 0 N" in output
def test_progress_off(archivers, request):
@ -642,7 +642,7 @@ def test_progress_off(archivers, request):
create_regular_file(archiver.input_path, "file1", size=1024 * 80)
cmd(archiver, "repo-create", RK_ENCRYPTION)
output = cmd(archiver, "create", "test5", "input")
assert "\r" not in output
assert "0 B O 0 B U 0 N" not in output
def test_file_status(archivers, request):