From 3dd14f4855c53b05382d7c80e8ae90eeaef4eb35 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 2 Apr 2023 19:43:09 +0200 Subject: [PATCH] ProgressIndicatorPercent: fix space computation for wide chars, fixes #3027 needs to use swidth() in case there are wide chars (like CJK) in the left part of the msg (e.g. an archive name). --- src/borg/helpers/progress.py | 4 +++- src/borg/testsuite/helpers.py | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/borg/helpers/progress.py b/src/borg/helpers/progress.py index fb2e0fc5..782c7fb3 100644 --- a/src/borg/helpers/progress.py +++ b/src/borg/helpers/progress.py @@ -144,10 +144,12 @@ class ProgressIndicatorPercent(ProgressIndicatorBase): # truncate the last argument, if no space is available if info is not None: if not self.json: + from ..platform import swidth # avoid circular import + # no need to truncate if we're not outputting to a terminal terminal_space = get_terminal_size(fallback=(-1, -1))[0] if terminal_space != -1: - space = terminal_space - len(self.msg % tuple([pct] + info[:-1] + [""])) + space = terminal_space - swidth(self.msg % tuple([pct] + info[:-1] + [""])) info[-1] = ellipsis_truncate(info[-1], space) return self.output(self.msg % tuple([pct] + info), justify=False, info=info) diff --git a/src/borg/testsuite/helpers.py b/src/borg/testsuite/helpers.py index 179e778b..df9b2ea2 100644 --- a/src/borg/testsuite/helpers.py +++ b/src/borg/testsuite/helpers.py @@ -45,7 +45,7 @@ from ..helpers import eval_escapes from ..helpers import safe_unlink from ..helpers import text_to_json, binary_to_json from ..helpers.passphrase import Passphrase, PasswordRetriesExceeded -from ..platform import is_cygwin, is_win32, is_darwin +from ..platform import is_cygwin, is_win32, is_darwin, swidth from . import BaseTestCase, FakeInputs, are_hardlinks_supported @@ -1017,6 +1017,29 @@ def test_progress_percentage_sameline(capfd, monkeypatch): assert err == " " * 4 + "\r" +@pytest.mark.skipif(is_win32, reason="no working swidth() implementation on this platform") +def test_progress_percentage_widechars(capfd, monkeypatch): + st = "スター・トレック" # "startrek" :-) + assert swidth(st) == 16 + path = "/カーク船長です。" # "Captain Kirk" + assert swidth(path) == 17 + spaces = " " * 4 # to avoid usage of "..." + width = len("100%") + 1 + swidth(st) + 1 + swidth(path) + swidth(spaces) + monkeypatch.setenv("COLUMNS", str(width)) + monkeypatch.setenv("LINES", "1") + pi = ProgressIndicatorPercent(100, step=5, start=0, msg=f"%3.0f%% {st} %s") + pi.logger.setLevel("INFO") + pi.show(0, info=[path]) + out, err = capfd.readouterr() + assert err == f" 0% {st} {path}{spaces}\r" + pi.show(100, info=[path]) + out, err = capfd.readouterr() + assert err == f"100% {st} {path}{spaces}\r" + pi.finish() + out, err = capfd.readouterr() + assert err == " " * width + "\r" + + def test_progress_percentage_step(capfd, monkeypatch): # run the test as if it was in a 4x1 terminal monkeypatch.setenv("COLUMNS", "4")