Prechádzať zdrojové kódy

Merge pull request #9084 from ThomasWaldmann/non-tty-progress-master

improved tty-less progress reporting (master)
TW 3 dní pred
rodič
commit
5df6021bc2

+ 10 - 1
src/borg/archive.py

@@ -149,6 +149,7 @@ Bytes sent to remote: {stats.tx_bytes}
     def show_progress(self, item=None, final=False, stream=None, dt=None):
     def show_progress(self, item=None, final=False, stream=None, dt=None):
         now = time.monotonic()
         now = time.monotonic()
         if dt is None or now - self.last_progress > dt:
         if dt is None or now - self.last_progress > dt:
+            stream = stream or sys.stderr
             self.last_progress = now
             self.last_progress = now
             if self.output_json:
             if self.output_json:
                 if not final:
                 if not final:
@@ -160,6 +161,14 @@ Bytes sent to remote: {stats.tx_bytes}
                 data.update({"time": time.time(), "type": "archive_progress", "finished": final})
                 data.update({"time": time.time(), "type": "archive_progress", "finished": final})
                 msg = json.dumps(data)
                 msg = json.dumps(data)
                 end = "\n"
                 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:
             else:
                 columns, lines = get_terminal_size()
                 columns, lines = get_terminal_size()
                 if not final:
                 if not final:
@@ -174,7 +183,7 @@ Bytes sent to remote: {stats.tx_bytes}
                 else:
                 else:
                     msg = " " * columns
                     msg = " " * columns
                 end = "\r"
                 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):
 def is_special(mode):

+ 40 - 15
src/borg/testsuite/archive_test.py

@@ -33,27 +33,52 @@ def test_stats_basic(stats):
     assert stats.usize == 20
     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):
+def test_stats_progress_tty(stats, monkeypatch, columns=80):
+    class TTYStringIO(StringIO):
+        def isatty(self):
+            return True
+
     monkeypatch.setenv("COLUMNS", str(columns))
     monkeypatch.setenv("COLUMNS", str(columns))
-    out = StringIO()
-    item = Item(path=item_path) if item_path else None
-    s = expected_output
+    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"
 
 
-    stats.update(update_size, unique=False)
-    stats.show_progress(item=item, stream=out)
+    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))
     buf = " " * (columns - len(s))
     assert out.getvalue() == s + buf + "\r"
     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):
 def test_stats_format(stats):
     assert (
     assert (
         str(stats)
         str(stats)

+ 2 - 2
src/borg/testsuite/archiver/create_cmd_test.py

@@ -634,7 +634,7 @@ def test_progress_on(archivers, request):
     create_regular_file(archiver.input_path, "file1", size=1024 * 80)
     create_regular_file(archiver.input_path, "file1", size=1024 * 80)
     cmd(archiver, "repo-create", RK_ENCRYPTION)
     cmd(archiver, "repo-create", RK_ENCRYPTION)
     output = cmd(archiver, "create", "test4", "input", "--progress")
     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):
 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)
     create_regular_file(archiver.input_path, "file1", size=1024 * 80)
     cmd(archiver, "repo-create", RK_ENCRYPTION)
     cmd(archiver, "repo-create", RK_ENCRYPTION)
     output = cmd(archiver, "create", "test5", "input")
     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):
 def test_file_status(archivers, request):