Rev 6497: (gz) Encode progress task messages when written to a terminal (Martin in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/2.5/

Patch Queue Manager pqm at pqm.ubuntu.com
Mon Apr 30 11:12:00 UTC 2012


At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/2.5/

------------------------------------------------------------
revno: 6497 [merge]
revision-id: pqm at pqm.ubuntu.com-20120430111200-3zw0af8s9hhx877t
parent: pqm at pqm.ubuntu.com-20120430092759-upfwbgheb2udt89w
parent: martin.packman at canonical.com-20120430104404-0tqn17kcmiv793fh
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: 2.5
timestamp: Mon 2012-04-30 11:12:00 +0000
message:
  (gz) Encode progress task messages when written to a terminal (Martin
   Packman)
modified:
  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
  doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/progress.py'
--- a/bzrlib/progress.py	2011-12-19 13:23:58 +0000
+++ b/bzrlib/progress.py	2012-04-30 09:14:35 +0000
@@ -58,7 +58,9 @@
     Code updating the task may also set fields as hints about how to display
     it: show_pct, show_spinner, show_eta, show_count, show_bar.  UIs
     will not necessarily respect all these fields.
-    
+
+    The message given when updating a task must be unicode, not bytes.
+
     :ivar update_latency: The interval (in seconds) at which the PB should be
         updated.  Setting this to zero suggests every update should be shown
         synchronously.
@@ -106,6 +108,10 @@
             self.msg)
 
     def update(self, msg, current_cnt=None, total_cnt=None):
+        """Report updated task message and if relevent progress counters
+
+        The message given must be unicode, not a byte string.
+        """
         self.msg = msg
         self.current_cnt = current_cnt
         if total_cnt:

=== modified file 'bzrlib/tests/test_progress.py'
--- a/bzrlib/tests/test_progress.py	2011-01-12 01:01:53 +0000
+++ b/bzrlib/tests/test_progress.py	2012-04-30 10:44:04 +0000
@@ -15,32 +15,20 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 
-from StringIO import StringIO
+from cStringIO import StringIO
 
+from bzrlib import (
+    tests,
+    )
 from bzrlib.progress import (
     ProgressTask,
     )
-from bzrlib.tests import TestCase
 from bzrlib.ui.text import (
     TextProgressView,
     )
 
 
-class _TTYStringIO(StringIO):
-    """A helper class which makes a StringIO look like a terminal"""
-
-    def isatty(self):
-        return True
-
-
-class _NonTTYStringIO(StringIO):
-    """Helper that implements isatty() but returns False"""
-
-    def isatty(self):
-        return False
-
-
-class TestTextProgressView(TestCase):
+class TestTextProgressView(tests.TestCase):
     """Tests for text display of progress bars.
 
     These try to exercise the progressview independently of its construction,
@@ -49,13 +37,16 @@
     # The ProgressTask now connects directly to the ProgressView, so we can
     # check them independently of the factory or of the determination of what
     # view to use.
-    
+
+    def make_view_only(self, out, width=79):
+        view = TextProgressView(out)
+        view._avail_width = lambda: width
+        return view
+
     def make_view(self):
         out = StringIO()
-        view = TextProgressView(out)
-        view._avail_width = lambda: 79
-        return out, view
-    
+        return out, self.make_view_only(out)
+
     def make_task(self, parent_task, view, msg, curr, total):
         # would normally be done by UIFactory; is done here so that we don't
         # have to have one.
@@ -168,3 +159,30 @@
 '   123kB   100kB/s \\ start_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.. 2000/5000',
            line) 
         self.assertEqual(len(line), 79)
+
+    def test_render_progress_unicode_enc_utf8(self):
+        out = tests.StringIOWrapper()
+        out.encoding = "utf-8"
+        view = self.make_view_only(out, 20)
+        task = self.make_task(None, view, u"\xa7", 0, 1)
+        view.show_progress(task)
+        self.assertEqual('\r/ \xc2\xa7 0/1            \r',
+            out.getvalue())
+
+    def test_render_progress_unicode_enc_missing(self):
+        out = StringIO()
+        self.assertRaises(AttributeError, getattr, out, "encoding")
+        view = self.make_view_only(out, 20)
+        task = self.make_task(None, view, u"\xa7", 0, 1)
+        view.show_progress(task)
+        self.assertEqual('\r/ ? 0/1             \r',
+            out.getvalue())
+
+    def test_render_progress_unicode_enc_none(self):
+        out = tests.StringIOWrapper()
+        out.encoding = None
+        view = self.make_view_only(out, 20)
+        task = self.make_task(None, view, u"\xa7", 0, 1)
+        view.show_progress(task)
+        self.assertEqual('\r/ ? 0/1             \r',
+            out.getvalue())

=== modified file 'bzrlib/tests/test_ui.py'
--- a/bzrlib/tests/test_ui.py	2011-10-08 19:01:59 +0000
+++ b/bzrlib/tests/test_ui.py	2012-04-30 08:59:57 +0000
@@ -31,7 +31,6 @@
     )
 from bzrlib.tests import (
     fixtures,
-    test_progress,
     )
 from bzrlib.ui import text as _mod_ui_text
 from bzrlib.tests.testui import (
@@ -39,6 +38,20 @@
     )
 
 
+class TTYStringIO(StringIO):
+    """A helper class which makes a StringIO look like a terminal"""
+
+    def isatty(self):
+        return True
+
+
+class NonTTYStringIO(StringIO):
+    """Helper that implements isatty() but returns False"""
+
+    def isatty(self):
+        return False
+
+
 class TestUIConfiguration(tests.TestCaseWithTransport):
 
     def test_output_encoding_configuration(self):
@@ -221,7 +234,7 @@
 
     def test_text_factory_prompts_and_clears(self):
         # a get_boolean call should clear the pb before prompting
-        out = test_progress._TTYStringIO()
+        out = TTYStringIO()
         self.overrideEnv('TERM', 'xterm')
         factory = _mod_ui_text.TextUIFactory(
             stdin=tests.StringIOWrapper("yada\ny\n"),
@@ -292,8 +305,8 @@
     def test_quietness(self):
         self.overrideEnv('BZR_PROGRESS_BAR', 'text')
         ui_factory = _mod_ui_text.TextUIFactory(None,
-            test_progress._TTYStringIO(),
-            test_progress._TTYStringIO())
+            TTYStringIO(),
+            TTYStringIO())
         self.assertIsInstance(ui_factory._progress_view,
             _mod_ui_text.TextProgressView)
         ui_factory.be_quiet(True)
@@ -358,7 +371,6 @@
     def test_progress_construction(self):
         """TextUIFactory constructs the right progress view.
         """
-        TTYStringIO = test_progress._TTYStringIO
         FileStringIO = tests.StringIOWrapper
         for (file_class, term, pb, expected_pb_class) in (
             # on an xterm, either use them or not as the user requests,
@@ -391,9 +403,9 @@
 
     def test_text_ui_non_terminal(self):
         """Even on non-ttys, make_ui_for_terminal gives a text ui."""
-        stdin = test_progress._NonTTYStringIO('')
-        stderr = test_progress._NonTTYStringIO()
-        stdout = test_progress._NonTTYStringIO()
+        stdin = NonTTYStringIO('')
+        stderr = NonTTYStringIO()
+        stdout = NonTTYStringIO()
         for term_type in ['dumb', None, 'xterm']:
             self.overrideEnv('TERM', term_type)
             uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)

=== modified file 'bzrlib/ui/text.py'
--- a/bzrlib/ui/text.py	2011-12-18 12:46:49 +0000
+++ b/bzrlib/ui/text.py	2012-04-30 10:44:04 +0000
@@ -404,8 +404,13 @@
     this only prints the stack from the nominated current task up to the root.
     """
 
-    def __init__(self, term_file):
+    def __init__(self, term_file, encoding=None, errors="replace"):
         self._term_file = term_file
+        if encoding is None:
+            self._encoding = getattr(term_file, "encoding", None) or "ascii"
+        else:
+            self._encoding = encoding
+        self._encoding_errors = errors
         # true when there's output on the screen we may need to clear
         self._have_output = False
         self._last_transport_msg = ''
@@ -432,10 +437,12 @@
         else:
             return w - 1
 
-    def _show_line(self, s):
-        # sys.stderr.write("progress %r\n" % s)
+    def _show_line(self, u):
+        s = u.encode(self._encoding, self._encoding_errors)
         width = self._avail_width()
         if width is not None:
+            # GZ 2012-03-28: Counting bytes is wrong for calculating width of
+            #                text but better than counting codepoints.
             s = '%-*.*s' % (width, width, s)
         self._term_file.write('\r' + s + '\r')
 

=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt	2012-03-29 12:53:01 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt	2012-04-30 11:12:00 +0000
@@ -36,6 +36,9 @@
   destination rather than the proxy when checking certificates.
   (Martin Packman, #944696)
 
+* Fix UnicodeEncodeError when translated progress task messages contain
+  non-ascii text. (Martin Packman, #966934)
+
 * Fixed merge tool availability checking and invocation to search the
   Windows App Path registry in addition to the PATH. (Gordon Tyler, #939605)
 




More information about the bazaar-commits mailing list