Bug 1306122 - [mozlint] Create a compact formatter that mimics the eslint 'compact' format, r?armenzg
MozReview-Commit-ID: 5JJJhMIrMIB
--- a/python/mozlint/mozlint/cli.py
+++ b/python/mozlint/mozlint/cli.py
@@ -114,17 +114,19 @@ def run(paths, linters, fmt, outgoing, w
lint.read(find_linters(linters))
# run all linters
results = lint.roll(paths, outgoing=outgoing, workdir=workdir)
formatter = formatters.get(fmt)
# Encode output with 'replace' to avoid UnicodeEncodeErrors on
# environments that aren't using utf-8.
- print(formatter(results, failed=lint.failed).encode(
- sys.stdout.encoding or 'ascii', 'replace'))
+ out = formatter(results, failed=lint.failed).encode(
+ sys.stdout.encoding or 'ascii', 'replace')
+ if out:
+ print(out)
return 1 if results or lint.failed else 0
if __name__ == '__main__':
parser = MozlintParser()
args = vars(parser.parse_args())
sys.exit(run(**args))
--- a/python/mozlint/mozlint/formatters/__init__.py
+++ b/python/mozlint/mozlint/formatters/__init__.py
@@ -1,25 +1,27 @@
# 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 json
from ..result import ResultEncoder
+from .compact import CompactFormatter
from .stylish import StylishFormatter
from .treeherder import TreeherderFormatter
class JSONFormatter(object):
def __call__(self, results, **kwargs):
return json.dumps(results, cls=ResultEncoder)
all_formatters = {
+ 'compact': CompactFormatter,
'json': JSONFormatter,
'stylish': StylishFormatter,
'treeherder': TreeherderFormatter,
}
def get(name, **fmtargs):
return all_formatters[name](**fmtargs)
new file mode 100644
--- /dev/null
+++ b/python/mozlint/mozlint/formatters/compact.py
@@ -0,0 +1,37 @@
+# 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/.
+
+from __future__ import unicode_literals
+
+from ..result import ResultContainer
+
+
+class CompactFormatter(object):
+ """Formatter for compact output.
+
+ This formatter prints one error per line, mimicking the
+ eslint 'compact' formatter.
+ """
+ fmt = "{path}: line {lineno}{column}, {level} - {message} ({rule})"
+
+ def __init__(self, summary=True):
+ self.summary = summary
+
+ def __call__(self, result, **kwargs):
+ message = []
+ num_problems = 0
+ for path, errors in sorted(result.iteritems()):
+ num_problems += len(errors)
+ for err in errors:
+ assert isinstance(err, ResultContainer)
+
+ d = {s: getattr(err, s) for s in err.__slots__}
+ d["column"] = ", col %s" % d["column"] if d["column"] else ""
+ d['level'] = d['level'].capitalize()
+ d['rule'] = d['rule'] or d['linter']
+ message.append(self.fmt.format(**d))
+
+ if self.summary and num_problems:
+ message.append("\n{} problem{}".format(num_problems, '' if num_problems == 1 else 's'))
+ return "\n".join(message)
--- a/python/mozlint/test/test_formatters.py
+++ b/python/mozlint/test/test_formatters.py
@@ -9,16 +9,53 @@ import sys
from collections import defaultdict
import pytest
from mozlint import ResultContainer
from mozlint import formatters
+EXPECTED = {
+ 'compact': {
+ 'kwargs': {},
+ 'format': """
+a/b/c.txt: line 1, Error - oh no foo (foo)
+a/b/c.txt: line 4, Error - oh no baz (baz)
+d/e/f.txt: line 4, col 2, Warning - oh no bar (bar-not-allowed)
+
+3 problems
+""".strip(),
+ },
+ 'stylish': {
+ 'kwargs': {
+ 'disable_colors': True,
+ },
+ 'format': """
+a/b/c.txt
+ 1 error oh no foo (foo)
+ 4 error oh no baz (baz)
+
+d/e/f.txt
+ 4:2 warning oh no bar bar-not-allowed (bar)
+
+\u2716 3 problems (2 errors, 1 warning)
+""".strip(),
+ },
+ 'treeherder': {
+ 'kwargs': {},
+ 'format': """
+TEST-UNEXPECTED-ERROR | a/b/c.txt:1 | oh no foo (foo)
+TEST-UNEXPECTED-ERROR | a/b/c.txt:4 | oh no baz (baz)
+TEST-UNEXPECTED-WARNING | d/e/f.txt:4:2 | oh no bar (bar-not-allowed)
+""".strip(),
+ },
+}
+
+
@pytest.fixture
def results(scope='module'):
containers = (
ResultContainer(
linter='foo',
path='a/b/c.txt',
message="oh no foo",
lineno=1,
@@ -42,41 +79,21 @@ def results(scope='module'):
),
)
results = defaultdict(list)
for c in containers:
results[c.path].append(c)
return results
-def test_stylish_formatter(results):
- expected = """
-a/b/c.txt
- 1 error oh no foo (foo)
- 4 error oh no baz (baz)
-
-d/e/f.txt
- 4:2 warning oh no bar bar-not-allowed (bar)
-
-\u2716 3 problems (2 errors, 1 warning)
-""".strip()
-
- fmt = formatters.get('stylish', disable_colors=True)
- assert expected == fmt(results)
-
-
-def test_treeherder_formatter(results):
- expected = """
-TEST-UNEXPECTED-ERROR | a/b/c.txt:1 | oh no foo (foo)
-TEST-UNEXPECTED-ERROR | a/b/c.txt:4 | oh no baz (baz)
-TEST-UNEXPECTED-WARNING | d/e/f.txt:4:2 | oh no bar (bar-not-allowed)
-""".strip()
-
- fmt = formatters.get('treeherder')
- assert expected == fmt(results)
+@pytest.mark.parametrize("name", EXPECTED.keys())
+def test_formatters(results, name):
+ opts = EXPECTED[name]
+ fmt = formatters.get(name, **opts['kwargs'])
+ assert fmt(results) == opts['format']
def test_json_formatter(results):
fmt = formatters.get('json')
formatted = json.loads(fmt(results))
assert set(formatted.keys()) == set(results.keys())