Bug 1273634 - [mozlint] Add a treeherder formatter, r?jgraham
This is a really simple and ugly formatter that is compatible with
treeherder's error highlighting mechanism. It is designed to be identical
to the current eslint output on treeherder:
https://dxr.mozilla.org/mozilla-central/rev/4d63dde701b47b8661ab7990f197b6b60e543839/tools/lint/eslint-formatter.js
Eventually eslint will also use this and we can remove that file. Once
bug 1276486 is fixed, we can make this look a little nicer. But for now
it gets the job done.
MozReview-Commit-ID: CwfWPcwWFxF
--- a/python/mozlint/mozlint/formatters/__init__.py
+++ b/python/mozlint/mozlint/formatters/__init__.py
@@ -1,23 +1,25 @@
# 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 .stylish import StylishFormatter
+from .treeherder import TreeherderFormatter
class JSONFormatter(object):
def __call__(self, results):
return json.dumps(results, cls=ResultEncoder)
all_formatters = {
'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/treeherder.py
@@ -0,0 +1,30 @@
+# 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 TreeherderFormatter(object):
+ """Formatter for treeherder friendly output.
+
+ This formatter looks ugly, but prints output such that
+ treeherder is able to highlight the errors and warnings.
+ This is a stop-gap until bug 1276486 is fixed.
+ """
+ fmt = "TEST-UNEXPECTED-{level} | {path}:{lineno}:{column} | {message} ({rule})"
+
+ def __call__(self, result):
+ message = []
+ for path, errors in sorted(result.iteritems()):
+ for err in errors:
+ assert isinstance(err, ResultContainer)
+
+ d = {s: getattr(err, s) for s in err.__slots__}
+ d['level'] = d['level'].upper()
+ d['rule'] = d['rule'] or d['linter']
+ message.append(self.fmt.format(**d))
+
+ return "\n".join(message)
--- a/python/mozlint/mozlint/roller.py
+++ b/python/mozlint/mozlint/roller.py
@@ -37,17 +37,17 @@ def _run_linters(queue, paths, **lintarg
# Ideally we would pass the entire LINTER definition as an argument
# to the worker instead of re-parsing it. But passing a function from
# a dynamically created module (with imp) does not seem to be possible
# with multiprocessing on Windows.
linter = parse(linter_path)
func = supported_types[linter['type']]
res = func(paths, linter, **lintargs) or []
- if isinstance(res, basestring):
+ if not isinstance(res, (list, tuple)):
continue
for r in res:
results[r.path].append(r)
def _run_worker(*args, **lintargs):
try:
--- a/python/mozlint/test/test_formatters.py
+++ b/python/mozlint/test/test_formatters.py
@@ -63,16 +63,26 @@ 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)
self.assertEqual(expected, fmt(self.results))
+ def test_treeherder_formatter(self):
+ expected = """
+TEST-UNEXPECTED-ERROR | a/b/c.txt:1:1 | oh no foo (foo)
+TEST-UNEXPECTED-ERROR | a/b/c.txt:4:1 | oh no baz (baz)
+TEST-UNEXPECTED-WARNING | d/e/f.txt:4:2 | oh no bar (bar-not-allowed)
+""".strip()
+
+ fmt = formatters.get('treeherder')
+ self.assertEqual(expected, fmt(self.results))
+
def test_json_formatter(self):
fmt = formatters.get('json')
formatted = json.loads(fmt(self.results))
self.assertEqual(set(formatted.keys()), set(self.results.keys()))
slots = ResultContainer.__slots__
for errors in formatted.values():
--- a/tools/lint/flake8.lint
+++ b/tools/lint/flake8.lint
@@ -69,17 +69,17 @@ def lint(files, **lintargs):
if not binary:
try:
binary = which.which('flake8')
except which.WhichError:
pass
if not binary:
print(FLAKE8_NOT_FOUND)
- return 1
+ return []
cmdargs = [
binary,
'--format', '{"path":"%(path)s","lineno":%(row)s,'
'"column":%(col)s,"rule":"%(code)s","message":"%(text)s"}',
]
exclude = lintargs.get('exclude')