Bug 1405304 - Add Unix formatter for mozlint. r?ahal
This patch introduces a new report formatter for the mozlint
framework used by "./mach lint" that respects Unix output conventions,
popularised by grep(1), compilers, and preprocessors.
The output format looks like this:
testing/marionette/driver.js:1153:48: no-unused-vars error: 'resp' is defined but never used.
Many editors (ed, Emacs, vi, Acme) recognise this format, allowing
users to interact with the output like a hyperlink to jump to the
specified location in a file.
MozReview-Commit-ID: 3IyiFmNbtMY
--- a/python/mozlint/mozlint/formatters/__init__.py
+++ b/python/mozlint/mozlint/formatters/__init__.py
@@ -5,25 +5,27 @@
from __future__ import absolute_import
import json
from ..result import ResultEncoder
from .compact import CompactFormatter
from .stylish import StylishFormatter
from .treeherder import TreeherderFormatter
+from .unix import UnixFormatter
class JSONFormatter(object):
def __call__(self, results, **kwargs):
return json.dumps(results, cls=ResultEncoder)
all_formatters = {
'compact': CompactFormatter,
'json': JSONFormatter,
'stylish': StylishFormatter,
'treeherder': TreeherderFormatter,
+ 'unix': UnixFormatter,
}
def get(name, **fmtargs):
return all_formatters[name](**fmtargs)
new file mode 100644
--- /dev/null
+++ b/python/mozlint/mozlint/formatters/unix.py
@@ -0,0 +1,34 @@
+# 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 absolute_import, unicode_literals
+
+import os
+
+from ..result import ResultContainer
+
+
+class UnixFormatter(object):
+ """Formatter that respects Unix output conventions frequently
+ employed by preprocessors and compilers. The format is
+ `<FILENAME>:<LINE>[:<COL>]: <RULE> <LEVEL>: <MESSAGE>`.
+
+ """
+ fmt = "{path}:{lineno}:{column} {rule} {level}: {message}"
+
+ def __call__(self, result, **kwargs):
+ msg = []
+
+ for path, errors in sorted(result.iteritems()):
+ for err in errors:
+ assert isinstance(err, ResultContainer)
+
+ slots = {s: getattr(err, s) for s in err.__slots__}
+ slots["path"] = os.path.relpath(slots["path"])
+ slots["column"] = "%d:" % slots["column"] if slots["column"] else ""
+ slots["rule"] = slots["rule"] or slots["linter"]
+
+ msg.append(self.fmt.format(**slots))
+
+ return "\n".join(msg)
--- a/python/mozlint/test/test_formatters.py
+++ b/python/mozlint/test/test_formatters.py
@@ -1,23 +1,28 @@
# 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 absolute_import, unicode_literals
import json
+import os
from collections import defaultdict
import mozunit
import pytest
from mozlint import ResultContainer
from mozlint import formatters
+NORMALISED_PATHS = {
+ 'abc': os.path.normpath('a/b/c.txt'),
+ 'def': os.path.normpath('d/e/f.txt'),
+}
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)
@@ -43,16 +48,24 @@ d/e/f.txt
'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(),
},
+ 'unix': {
+ 'kwargs': {},
+ 'format': """
+{abc}:1: foo error: oh no foo
+{abc}:4: baz error: oh no baz
+{def}:4:2: bar-not-allowed warning: oh no bar
+""".format(**NORMALISED_PATHS).strip(),
+ },
}
@pytest.fixture
def results(scope='module'):
containers = (
ResultContainer(
linter='foo',