Bug 1434974 - [mozlint] Create a SummaryFormatter to help triage paths, r?jmaher
This formatter is useful for triaging paths when enabling new linters or
expanding existing ones. It works well with the -n/--no-filter option.
For example, if I wanted to look for candidates of new directories to enable
the codespell linter on, I could run:
./mach lint -l codespell -nf summary
This will print something like:
accessible: 429
dom: 142
layout: 15
testing: 53
etc..
If desired, you can also specify a depth by setting MOZLINT_SUMMARY_DEPTH. A
depth of 2 means results will be aggregated further down, e.g:
accesible/build: 129
accesible/ipc: 300
dom/indexedDB: 100
dom/workers: 42
etc..
The depth is always relative to the common path prefix of all results, so
running:
./mach lint -l codespell -nf python/mozbuild
Would expand all the directories under python/mozbuild (not topsrdir).
MozReview-Commit-ID: OiihLTpULA
--- a/python/mozlint/mozlint/formatters/__init__.py
+++ b/python/mozlint/mozlint/formatters/__init__.py
@@ -4,28 +4,30 @@
from __future__ import absolute_import
import json
from ..result import ResultEncoder
from .compact import CompactFormatter
from .stylish import StylishFormatter
+from .summary import SummaryFormatter
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,
+ 'summary': SummaryFormatter,
'treeherder': TreeherderFormatter,
'unix': UnixFormatter,
}
def get(name, **fmtargs):
return all_formatters[name](**fmtargs)
new file mode 100644
--- /dev/null
+++ b/python/mozlint/mozlint/formatters/summary.py
@@ -0,0 +1,35 @@
+# 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 collections import defaultdict
+
+import mozpack.path as mozpath
+
+
+class SummaryFormatter(object):
+
+ def __init__(self, depth=None):
+ self.depth = depth or int(os.environ.get('MOZLINT_SUMMARY_DEPTH', 1))
+
+ def __call__(self, result, **kwargs):
+ commonprefix = mozpath.commonprefix([mozpath.abspath(p) for p in result])
+ commonprefix = commonprefix.rsplit('/', 1)[0] + '/'
+
+ summary = defaultdict(int)
+ for path, errors in result.iteritems():
+ path = mozpath.abspath(path)
+ assert path.startswith(commonprefix)
+
+ if path == commonprefix:
+ summary[path] += len(errors)
+ continue
+
+ parts = mozpath.split(mozpath.relpath(path, commonprefix))[:self.depth]
+ path = mozpath.join(commonprefix, *parts)
+ summary[path] += len(errors)
+
+ return '\n'.join(['{}: {}'.format(k, summary[k]) for k in sorted(summary)])
--- a/python/mozlint/test/test_formatters.py
+++ b/python/mozlint/test/test_formatters.py
@@ -4,24 +4,26 @@
from __future__ import absolute_import, unicode_literals
import json
import os
from collections import defaultdict
import mozunit
+import mozpack.path as mozpath
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'),
+ 'cwd': mozpath.normpath(os.getcwd()),
}
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)
@@ -56,16 +58,23 @@ TEST-UNEXPECTED-WARNING | d/e/f.txt:4:2
'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(),
},
+ 'summary': {
+ 'kwargs': {},
+ 'format': """
+{cwd}/a: 2
+{cwd}/d: 1
+""".format(**NORMALISED_PATHS).strip(),
+ },
}
@pytest.fixture
def results(scope='module'):
containers = (
ResultContainer(
linter='foo',