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).

(cherry picked from commit 3dd14f4855c53b05382d7c80e8ae90eeaef4eb35)
Thomas Waldmann 2 years ago
parent
commit
21a9458848
2 changed files with 27 additions and 1 deletions
  1. 3 1
      src/borg/helpers/progress.py
  2. 24 0
      src/borg/testsuite/helpers.py

+ 3 - 1
src/borg/helpers/progress.py

@@ -147,10 +147,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 - 0
src/borg/testsuite/helpers.py

@@ -30,6 +30,7 @@ from ..helpers import popen_with_error_handling
 from ..helpers import dash_open
 from ..helpers import iter_separated
 from ..helpers import eval_escapes
+from ..platform import is_win32, swidth
 
 from . import BaseTestCase, FakeInputs
 
@@ -896,6 +897,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')