Browse Source

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).
Thomas Waldmann 2 years ago
parent
commit
3dd14f4855
2 changed files with 27 additions and 2 deletions
  1. 3 1
      src/borg/helpers/progress.py
  2. 24 1
      src/borg/testsuite/helpers.py

+ 3 - 1
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)
 

+ 24 - 1
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")