[Python-checkins] bpo-34421: Improve distutils logging for non-ASCII strings. (GH-9126)

Serhiy Storchaka webhook-mailer at python.org
Sun Sep 23 02:13:03 EDT 2018


https://github.com/python/cpython/commit/4b860fd777e983f5d2a6bd1288e2b53099c6a803
commit: 4b860fd777e983f5d2a6bd1288e2b53099c6a803
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-09-23T09:12:59+03:00
summary:

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.

files:
M Lib/distutils/log.py
M Lib/distutils/tests/test_log.py

diff --git a/Lib/distutils/log.py b/Lib/distutils/log.py
index 3a6602bc8b8e..8ef6b28ea2ec 100644
--- a/Lib/distutils/log.py
+++ b/Lib/distutils/log.py
@@ -27,14 +27,13 @@ def _log(self, level, msg, args):
                 stream = sys.stderr
             else:
                 stream = sys.stdout
-            if stream.errors == 'strict':
+            try:
+                stream.write('%s\n' % msg)
+            except UnicodeEncodeError:
                 # emulate backslashreplace error handler
                 encoding = stream.encoding
                 msg = msg.encode(encoding, "backslashreplace").decode(encoding)
-            try:
                 stream.write('%s\n' % msg)
-            except UnicodeEncodeError:
-                stream.write('%s\n' % msg.encode('unicode-escape').decode('ascii'))
             stream.flush()
 
     def log(self, level, msg, *args):
diff --git a/Lib/distutils/tests/test_log.py b/Lib/distutils/tests/test_log.py
index 0c2ad7a4268d..22c26246ca9d 100644
--- a/Lib/distutils/tests/test_log.py
+++ b/Lib/distutils/tests/test_log.py
@@ -3,33 +3,39 @@
 import sys
 import unittest
 from tempfile import NamedTemporaryFile
-from test.support import run_unittest
+from test.support import swap_attr, run_unittest
 
 from distutils import log
 
 class TestLog(unittest.TestCase):
     def test_non_ascii(self):
-        # Issue #8663: test that non-ASCII text is escaped with
-        # backslashreplace error handler (stream use ASCII encoding and strict
-        # error handler)
-        old_stdout = sys.stdout
-        old_stderr = sys.stderr
-        old_threshold = log.set_threshold(log.DEBUG)
-        try:
-            with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \
-                 NamedTemporaryFile(mode="w+", encoding='ascii') as stderr:
-                sys.stdout = stdout
-                sys.stderr = stderr
-                log.debug("debug:\xe9")
-                log.fatal("fatal:\xe9")
+        # Issues #8663, #34421: test that non-encodable text is escaped with
+        # backslashreplace error handler and encodable non-ASCII text is
+        # output as is.
+        for errors in ('strict', 'backslashreplace', 'surrogateescape',
+                       'replace', 'ignore'):
+            with self.subTest(errors=errors), \
+                 NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stdout, \
+                 NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stderr:
+                old_threshold = log.set_threshold(log.DEBUG)
+                try:
+                    with swap_attr(sys, 'stdout', stdout), \
+                         swap_attr(sys, 'stderr', stderr):
+                        log.debug('Dεbug\tMėssãge')
+                        log.fatal('Fαtal\tÈrrōr')
+                finally:
+                    log.set_threshold(old_threshold)
+
                 stdout.seek(0)
-                self.assertEqual(stdout.read().rstrip(), "debug:\\xe9")
+                self.assertEqual(stdout.read().rstrip(),
+                        'Dεbug\tM?ss?ge' if errors == 'replace' else
+                        'Dεbug\tMssge' if errors == 'ignore' else
+                        'Dεbug\tM\\u0117ss\\xe3ge')
                 stderr.seek(0)
-                self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9")
-        finally:
-            log.set_threshold(old_threshold)
-            sys.stdout = old_stdout
-            sys.stderr = old_stderr
+                self.assertEqual(stderr.read().rstrip(),
+                        'Fαtal\t?rr?r' if errors == 'replace' else
+                        'Fαtal\trrr' if errors == 'ignore' else
+                        'Fαtal\t\\xc8rr\\u014dr')
 
 def test_suite():
     return unittest.makeSuite(TestLog)



More information about the Python-checkins mailing list