Bug 1585686 - [mozlint] Avoid UnicodeEncodeError for users who have an 'ascii' locale r=gbrown
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Thu, 03 Oct 2019 13:17:08 +0000
changeset 496169 b995dbfa4d87ea871205e4f41f8eca8460d2042f
parent 496168 6050b9e662edf1099201136cb1044bdc3cad3ade
child 496170 f8f091f34f2c6b5f483dac45a0b0ae4a0c328a95
push id36646
push usernerli@mozilla.com
push dateThu, 03 Oct 2019 21:48:01 +0000
treeherdermozilla-central@2e1bfb7458de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgbrown
bugs1585686
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1585686 - [mozlint] Avoid UnicodeEncodeError for users who have an 'ascii' locale r=gbrown Differential Revision: https://phabricator.services.mozilla.com/D47941
python/mozlint/mozlint/cli.py
python/mozlint/test/test_cli.py
--- a/python/mozlint/mozlint/cli.py
+++ b/python/mozlint/mozlint/cli.py
@@ -207,18 +207,30 @@ def run(paths, linters, formats, outgoin
         edit_issues(result)
         result = lint.roll(result.issues.keys())
 
     for formatter_name, path in formats:
         formatter = formatters.get(formatter_name)
 
         out = formatter(result)
         if out:
-            output_file = open(path, 'w') if path else sys.stdout
-            print(out, file=output_file)
+            fh = open(path, 'w') if path else sys.stdout
+
+            if not path and fh.encoding == 'ascii':
+                # If sys.stdout.encoding is ascii, printing output will fail
+                # due to the stylish formatter's use of unicode characters.
+                # Ideally the user should fix their environment by setting
+                # `LC_ALL=C.UTF-8` or similar. But this is a common enough
+                # problem that we help them out a little here by manually
+                # encoding and writing to the stdout buffer directly.
+                out += '\n'
+                fh.buffer.write(out.encode('utf-8', errors='replace'))
+                fh.buffer.flush()
+            else:
+                print(out, file=fh)
 
     return result.returncode
 
 
 if __name__ == '__main__':
     parser = MozlintParser()
     args = vars(parser.parse_args())
     sys.exit(run(**args))
--- a/python/mozlint/test/test_cli.py
+++ b/python/mozlint/test/test_cli.py
@@ -1,13 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
+import subprocess
+import sys
 from distutils.spawn import find_executable
 
 import mozunit
 import pytest
 
 from mozlint import cli
 
 here = os.path.abspath(os.path.dirname(__file__))
@@ -27,16 +29,27 @@ def run(parser, lintdir, files):
         args = args or []
         args.extend(files)
         lintargs = vars(parser.parse_args(args))
         lintargs['root'] = here
         return cli.run(**lintargs)
     return inner
 
 
+def test_cli_with_ascii_encoding(run, monkeypatch, capfd):
+    cmd = [sys.executable, 'runcli.py', '-l=string', '-f=stylish']
+    env = os.environ.copy()
+    env['PYTHONPATH'] = os.pathsep.join(sys.path)
+    env['PYTHONIOENCODING'] = 'ascii'
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+                            cwd=here, env=env, universal_newlines=True)
+    out = proc.communicate()[0]
+    assert 'Traceback' not in out
+
+
 def test_cli_run_with_fix(run, capfd):
     ret = run(['-f', 'json', '--fix', '--linter', 'external'])
     out, err = capfd.readouterr()
     assert ret == 0
     assert out.endswith('{}\n')
 
 
 @pytest.mark.skipif(not find_executable("echo"), reason="No `echo` executable found.")