Skip to content

Commit 4b860fd

Browse files
bpo-34421: Improve distutils logging for non-ASCII strings. (GH-9126)
Use "backslashreplace" instead of "unicode-escape". It is not implementation depended and escapes only non-encodable characters. Also simplify the code.
1 parent 8fabae3 commit 4b860fd

File tree

2 files changed

+29
-24
lines changed

2 files changed

+29
-24
lines changed

Lib/distutils/log.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,13 @@ def _log(self, level, msg, args):
2727
stream = sys.stderr
2828
else:
2929
stream = sys.stdout
30-
if stream.errors == 'strict':
30+
try:
31+
stream.write('%s\n' % msg)
32+
except UnicodeEncodeError:
3133
# emulate backslashreplace error handler
3234
encoding = stream.encoding
3335
msg = msg.encode(encoding, "backslashreplace").decode(encoding)
34-
try:
3536
stream.write('%s\n' % msg)
36-
except UnicodeEncodeError:
37-
stream.write('%s\n' % msg.encode('unicode-escape').decode('ascii'))
3837
stream.flush()
3938

4039
def log(self, level, msg, *args):

Lib/distutils/tests/test_log.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,39 @@
33
import sys
44
import unittest
55
from tempfile import NamedTemporaryFile
6-
from test.support import run_unittest
6+
from test.support import swap_attr, run_unittest
77

88
from distutils import log
99

1010
class TestLog(unittest.TestCase):
1111
def test_non_ascii(self):
12-
# Issue #8663: test that non-ASCII text is escaped with
13-
# backslashreplace error handler (stream use ASCII encoding and strict
14-
# error handler)
15-
old_stdout = sys.stdout
16-
old_stderr = sys.stderr
17-
old_threshold = log.set_threshold(log.DEBUG)
18-
try:
19-
with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \
20-
NamedTemporaryFile(mode="w+", encoding='ascii') as stderr:
21-
sys.stdout = stdout
22-
sys.stderr = stderr
23-
log.debug("debug:\xe9")
24-
log.fatal("fatal:\xe9")
12+
# Issues #8663, #34421: test that non-encodable text is escaped with
13+
# backslashreplace error handler and encodable non-ASCII text is
14+
# output as is.
15+
for errors in ('strict', 'backslashreplace', 'surrogateescape',
16+
'replace', 'ignore'):
17+
with self.subTest(errors=errors), \
18+
NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stdout, \
19+
NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stderr:
20+
old_threshold = log.set_threshold(log.DEBUG)
21+
try:
22+
with swap_attr(sys, 'stdout', stdout), \
23+
swap_attr(sys, 'stderr', stderr):
24+
log.debug('Dεbug\tMėssãge')
25+
log.fatal('Fαtal\tÈrrōr')
26+
finally:
27+
log.set_threshold(old_threshold)
28+
2529
stdout.seek(0)
26-
self.assertEqual(stdout.read().rstrip(), "debug:\\xe9")
30+
self.assertEqual(stdout.read().rstrip(),
31+
'Dεbug\tM?ss?ge' if errors == 'replace' else
32+
'Dεbug\tMssge' if errors == 'ignore' else
33+
'Dεbug\tM\\u0117ss\\xe3ge')
2734
stderr.seek(0)
28-
self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9")
29-
finally:
30-
log.set_threshold(old_threshold)
31-
sys.stdout = old_stdout
32-
sys.stderr = old_stderr
35+
self.assertEqual(stderr.read().rstrip(),
36+
'Fαtal\t?rr?r' if errors == 'replace' else
37+
'Fαtal\trrr' if errors == 'ignore' else
38+
'Fαtal\t\\xc8rr\\u014dr')
3339

3440
def test_suite():
3541
return unittest.makeSuite(TestLog)

0 commit comments

Comments
 (0)