Rev 5060: (mbp) adjust spinner and repainting of progress bar (Martin Pool) in file:///home/pqm/archives/thelove/bzr/2.2/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Jul 19 10:26:06 BST 2010


At file:///home/pqm/archives/thelove/bzr/2.2/

------------------------------------------------------------
revno: 5060 [merge]
revision-id: pqm at pqm.ubuntu.com-20100719092604-n967139gtzttqxe5
parent: pqm at pqm.ubuntu.com-20100716172956-0gj9grxjxvppcayb
parent: mbp at canonical.com-20100717164644-uu4uhamdunf3mxzc
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: 2.2
timestamp: Mon 2010-07-19 10:26:04 +0100
message:
  (mbp) adjust spinner and repainting of progress bar (Martin Pool)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/progress.py             progress.py-20050610070202-df9faaab791964c0
  bzrlib/tests/test_progress.py  test_progress.py-20060308160359-978c397bc79b7fda
  bzrlib/tests/test_ui.py        test_ui.py-20051130162854-458e667a7414af09
  bzrlib/ui/text.py              text.py-20051130153916-2e438cffc8afc478
=== modified file 'NEWS'
--- a/NEWS	2010-07-16 12:53:03 +0000
+++ b/NEWS	2010-07-17 16:39:20 +0000
@@ -33,6 +33,10 @@
 * Don't traceback trying to unversion children files of an already
   unversioned directory.  (Vincent Ladeuil, #494221)
 
+* Progress bars prefer to truncate the text message rather than the
+  counters.  The spinner is shown between the network transfer indicator
+  and the progress message.  (Martin Pool)
+
 * Recursive binding for checkouts is now detected by bzr. A clear error
   message is shown to the user. (Parth Malwankar, #405192)
 
@@ -49,6 +53,8 @@
 API Changes
 ***********
 
+* Delete ``ProgressTask.note``, which was deprecated in 2.1.
+
 Internals
 *********
 

=== modified file 'bzrlib/progress.py'
--- a/bzrlib/progress.py	2010-04-30 11:03:59 +0000
+++ b/bzrlib/progress.py	2010-07-17 16:39:20 +0000
@@ -152,19 +152,6 @@
                 own_fraction = 0.0
             return self._parent_task._overall_completion_fraction(own_fraction)
 
-    @deprecated_method(deprecated_in((2, 1, 0)))
-    def note(self, fmt_string, *args):
-        """Record a note without disrupting the progress bar.
-        
-        Deprecated: use ui_factory.note() instead or bzrlib.trace.  Note that
-        ui_factory.note takes just one string as the argument, not a format
-        string and arguments.
-        """
-        if args:
-            self.ui_factory.note(fmt_string % args)
-        else:
-            self.ui_factory.note(fmt_string)
-
     def clear(self):
         # TODO: deprecate this method; the model object shouldn't be concerned
         # with whether it's shown or not.  Most callers use this because they
@@ -220,12 +207,6 @@
         self.clear()
         self._stack.return_pb(self)
 
-    def note(self, fmt_string, *args, **kwargs):
-        """Record a note without disrupting the progress bar."""
-        self.clear()
-        self.to_messages_file.write(fmt_string % args)
-        self.to_messages_file.write('\n')
-
 
 class DummyProgress(object):
     """Progress-bar standin that does nothing.
@@ -248,9 +229,6 @@
     def clear(self):
         pass
 
-    def note(self, fmt_string, *args, **kwargs):
-        """See _BaseProgressBar.note()."""
-
     def child_progress(self, **kwargs):
         return DummyProgress(**kwargs)
 

=== modified file 'bzrlib/tests/test_progress.py'
--- a/bzrlib/tests/test_progress.py	2010-02-17 17:11:16 +0000
+++ b/bzrlib/tests/test_progress.py	2010-07-09 15:47:01 +0000
@@ -58,7 +58,7 @@
     def make_view(self):
         out = StringIO()
         view = TextProgressView(out)
-        view._width = 80
+        view._avail_width = lambda: 79
         return out, view
     
     def make_task(self, parent_task, view, msg, curr, total):
@@ -100,13 +100,13 @@
         # so we're in the first half of the main task, and half way through
         # that
         self.assertEqual(
-r'[####-               ] reticulating splines:stage2 1/2'
+'[####-               ] reticulating splines:stage2 1/2                         '
             , view._render_line())
         # if the nested task is complete, then we're all the way through the
         # first half of the overall work
         task2.update('stage2', 2, 2)
         self.assertEqual(
-r'[#########\          ] reticulating splines:stage2 2/2'
+'[#########\          ] reticulating splines:stage2 2/2                         '
             , view._render_line())
 
     def test_render_progress_sub_nested(self):
@@ -123,5 +123,38 @@
         # progress indication, just a label; and the bottom one is half done,
         # so the overall fraction is 1/4
         self.assertEqual(
-            r'[####|               ] a:b:c 1/2'
+'[####|               ] a:b:c 1/2                                               '
             , view._render_line())
+
+    def test_render_truncated(self):
+        # when the bar is too long for the terminal, we prefer not to truncate
+        # the counters because they might be interesting, and because
+        # truncating the numbers might be misleading
+        out, view = self.make_view()
+        task_a = ProgressTask(None, progress_view=view)
+        task_a.update('start_' + 'a' * 200 + '_end', 2000, 5000)
+        line = view._render_line()
+        self.assertEqual(
+'- start_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.. 2000/5000',
+           line) 
+        self.assertEqual(len(line), 79)
+
+
+    def test_render_with_activity(self):
+        # if the progress view has activity, it's shown before the spinner
+        out, view = self.make_view()
+        task_a = ProgressTask(None, progress_view=view)
+        view._last_transport_msg = '   123kB   100kB/s '
+        line = view._render_line()
+        self.assertEqual(
+'   123kB   100kB/s /                                                           ',
+           line) 
+        self.assertEqual(len(line), 79)
+
+        task_a.update('start_' + 'a' * 200 + '_end', 2000, 5000)
+        view._last_transport_msg = '   123kB   100kB/s '
+        line = view._render_line()
+        self.assertEqual(
+'   123kB   100kB/s \\ start_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.. 2000/5000',
+           line) 
+        self.assertEqual(len(line), 79)

=== modified file 'bzrlib/tests/test_ui.py'
--- a/bzrlib/tests/test_ui.py	2010-06-21 22:29:38 +0000
+++ b/bzrlib/tests/test_ui.py	2010-07-17 16:46:44 +0000
@@ -100,51 +100,6 @@
         finally:
             pb.finished()
 
-    def test_progress_note(self):
-        stderr = tests.StringIOWrapper()
-        stdout = tests.StringIOWrapper()
-        ui_factory = _mod_ui_text.TextUIFactory(stdin=tests.StringIOWrapper(''),
-                                                stderr=stderr,
-                                                stdout=stdout)
-        pb = ui_factory.nested_progress_bar()
-        try:
-            result = self.applyDeprecated(deprecated_in((2, 1, 0)),
-                pb.note,
-                't')
-            self.assertEqual(None, result)
-            self.assertEqual("t\n", stdout.getvalue())
-            # Since there was no update() call, there should be no clear() call
-            self.failIf(re.search(r'^\r {10,}\r$',
-                                  stderr.getvalue()) is not None,
-                        'We cleared the stderr without anything to put there')
-        finally:
-            pb.finished()
-
-    def test_progress_note_clears(self):
-        stderr = test_progress._TTYStringIO()
-        stdout = test_progress._TTYStringIO()
-        # so that we get a TextProgressBar
-        os.environ['TERM'] = 'xterm'
-        ui_factory = _mod_ui_text.TextUIFactory(
-            stdin=tests.StringIOWrapper(''),
-            stdout=stdout, stderr=stderr)
-        self.assertIsInstance(ui_factory._progress_view,
-                              _mod_ui_text.TextProgressView)
-        pb = ui_factory.nested_progress_bar()
-        try:
-            # Create a progress update that isn't throttled
-            pb.update('x', 1, 1)
-            result = self.applyDeprecated(deprecated_in((2, 1, 0)),
-                pb.note, 't')
-            self.assertEqual(None, result)
-            self.assertEqual("t\n", stdout.getvalue())
-            # the exact contents will depend on the terminal width and we don't
-            # care about that right now - but you're probably running it on at
-            # least a 10-character wide terminal :)
-            self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
-        finally:
-            pb.finished()
-
     def test_text_ui_get_boolean(self):
         stdin = tests.StringIOWrapper("y\n" # True
                                       "n\n" # False
@@ -193,6 +148,7 @@
         factory = _mod_ui_text.TextUIFactory(
             stdin=tests.StringIOWrapper("yada\ny\n"),
             stdout=out, stderr=out)
+        factory._avail_width = lambda: 79
         pb = factory.nested_progress_bar()
         pb.show_bar = False
         pb.show_spinner = False
@@ -204,9 +160,9 @@
                                                factory.get_boolean,
                                                "what do you want"))
         output = out.getvalue()
-        self.assertContainsRe(factory.stdout.getvalue(),
-            "foo *\r\r  *\r*")
-        self.assertContainsRe(factory.stdout.getvalue(),
+        self.assertContainsRe(output,
+            "| foo *\r\r  *\r*")
+        self.assertContainsRe(output,
             r"what do you want\? \[y/n\]: what do you want\? \[y/n\]: ")
         # stdin should have been totally consumed
         self.assertEqual('', factory.stdin.readline())

=== modified file 'bzrlib/ui/text.py'
--- a/bzrlib/ui/text.py	2010-06-21 01:30:45 +0000
+++ b/bzrlib/ui/text.py	2010-07-09 15:47:01 +0000
@@ -300,13 +300,15 @@
         # correspond reliably to overall command progress
         self.enable_bar = False
 
+    def _avail_width(self):
+        # we need one extra space for terminals that wrap on last char
+        w = osutils.terminal_width() 
+        if w is None:
+            return w
+        else:
+            return w - 1
+
     def _show_line(self, s):
-        # sys.stderr.write("progress %r\n" % s)
-        width = osutils.terminal_width()
-        if width is not None:
-            # we need one extra space for terminals that wrap on last char
-            width = width - 1
-            s = '%-*.*s' % (width, width, s)
         self._term_file.write('\r' + s + '\r')
 
     def clear(self):
@@ -348,6 +350,10 @@
             return ''
 
     def _format_task(self, task):
+        """Format task-specific parts of progress bar.
+
+        :returns: (text_part, counter_part) both unicode strings.
+        """
         if not task.show_count:
             s = ''
         elif task.current_cnt is not None and task.total_cnt is not None:
@@ -363,21 +369,39 @@
             t = t._parent_task
             if t.msg:
                 m = t.msg + ':' + m
-        return m + s
+        return m, s
 
     def _render_line(self):
         bar_string = self._render_bar()
         if self._last_task:
-            task_msg = self._format_task(self._last_task)
+            task_part, counter_part = self._format_task(self._last_task)
         else:
-            task_msg = ''
+            task_part = counter_part = ''
         if self._last_task and not self._last_task.show_transport_activity:
             trans = ''
         else:
             trans = self._last_transport_msg
-            if trans:
-                trans += ' | '
-        return (bar_string + trans + task_msg)
+        # the bar separates the transport activity from the message, so even
+        # if there's no bar or spinner, we must show something if both those
+        # fields are present
+        if (task_part or trans) and not bar_string:
+            bar_string = '| '
+        # preferentially truncate the task message if we don't have enough
+        # space
+        avail_width = self._avail_width()
+        if avail_width is not None:
+            # if terminal avail_width is unknown, don't truncate
+            current_len = len(bar_string) + len(trans) + len(task_part) + len(counter_part)
+            gap = current_len - avail_width
+            if gap > 0:
+                task_part = task_part[:-gap-2] + '..'
+        s = trans + bar_string + task_part + counter_part
+        if avail_width is not None:
+            if len(s) < avail_width:
+                s = s.ljust(avail_width)
+            elif len(s) > avail_width:
+                s = s[:avail_width]
+        return s
 
     def _repaint(self):
         s = self._render_line()
@@ -439,7 +463,7 @@
             rate = (self._bytes_since_update
                     / (now - self._transport_update_time))
             # using base-10 units (see HACKING.txt).
-            msg = ("%6dkB %5dkB/s" %
+            msg = ("%6dkB %5dkB/s " %
                     (self._total_byte_count / 1000, int(rate) / 1000,))
             self._transport_update_time = now
             self._last_repaint = now




More information about the bazaar-commits mailing list