Merge lp:~hid-iwata/bzr/fix-mojibake into lp:bzr

Proposed by IWATA Hidetaka
Status: Needs review
Proposed branch: lp:~hid-iwata/bzr/fix-mojibake
Merge into: lp:bzr
Diff against target: 68 lines (+31/-2)
2 files modified
bzrlib/commands.py (+9/-2)
bzrlib/trace.py (+22/-0)
To merge this branch: bzr merge lp:~hid-iwata/bzr/fix-mojibake
Reviewer Review Type Date Requested Status
bzr-core Pending
Review via email: mp+119051@code.launchpad.net

Description of the change

Workaround for #777996 and #909152

Unreadable error message can be a huge blocker for newbie users.
This patch may not be perfect, but I think this is better than now.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

This is close to the right fix for this (longstanding and very annoying) issue.

I really think we want to do the decoding inside trace.report_exception rather than wrapping the stream passed to allow it to be stupid, what do you think?

Revision history for this message
IWATA Hidetaka (hid-iwata) wrote :

All stderr output must be encoded to terminal-encoding, even if stderr.encoding is None. (qbzr requires such behavior)
inside trace.report_exception, err_file can be any other file-like-object and I don't know which encoding to choose.
This is the reason why I use wrapped stream.

Unmerged revisions

6545. By IWATA Hidetaka

Consider decoding error.

6544. By IWATA Hidetaka

Fix mojibake of localized error message.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/commands.py'
2--- bzrlib/commands.py 2012-03-13 17:25:29 +0000
3+++ bzrlib/commands.py 2012-08-09 21:38:46 +0000
4@@ -918,6 +918,13 @@
5 os.close(pffileno)
6 os.remove(pfname)
7
8+def _err_file():
9+ """
10+ Get file like object which exception is written to.
11+ """
12+ return trace.EncodedOutputStream(sys.stderr,
13+ osutils.get_terminal_encoding(),
14+ 'replace')
15
16 def exception_to_return_code(the_callable, *args, **kwargs):
17 """UI level helper for profiling and coverage.
18@@ -932,7 +939,7 @@
19 # used to handle AssertionError and KeyboardInterrupt
20 # specially here, but hopefully they're handled ok by the logger now
21 exc_info = sys.exc_info()
22- exitcode = trace.report_exception(exc_info, sys.stderr)
23+ exitcode = trace.report_exception(exc_info, _err_file())
24 if os.environ.get('BZR_PDB'):
25 print '**** entering debugger'
26 tb = exc_info[2]
27@@ -1252,7 +1259,7 @@
28 except Exception, e:
29 if (isinstance(e, (OSError, IOError))
30 or not getattr(e, 'internal_error', True)):
31- trace.report_exception(sys.exc_info(), sys.stderr)
32+ trace.report_exception(sys.exc_info(), _err_file())
33 return 3
34 else:
35 raise
36
37=== modified file 'bzrlib/trace.py'
38--- bzrlib/trace.py 2012-02-01 13:24:42 +0000
39+++ bzrlib/trace.py 2012-08-09 21:38:46 +0000
40@@ -610,6 +610,28 @@
41 # to the file so there's no danger of getting into a loop here.
42 mutter("Logging record unformattable: %s %% %s", msg, args)
43
44+class EncodedOutputStream(object):
45+ def __init__(self, stream, encoding=None, errors='strict'):
46+ self.stream = stream
47+ if encoding is None:
48+ encoding = getattr(stream, "encoding", "ascii")
49+ self.encoding = encoding
50+ self.errors = errors
51+
52+ def write(self, text):
53+ if isinstance(text, unicode):
54+ self.stream.write(text.encode(self.encoding, self.errors))
55+ else:
56+ try:
57+ text = text.decode("utf-8")
58+ self.stream.write(text.encode(self.encoding, self.errors))
59+ except UnicodeError:
60+ self.stream.write(text)
61+
62+ def __getattr__(self, name):
63+ """Redirect to self.stream"""
64+ return getattr(self.stream, name)
65+
66
67 class Config(object):
68 """Configuration of message tracing in bzrlib.