Bug 1434974 - [mozlint] Create a SummaryFormatter to help triage paths, r?jmaher draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Mon, 29 Jan 2018 16:37:21 -0500
changeset 758061 e340c63f15d5022c27ce474f0d36046007820727
parent 757856 dd6caba141428343fb26f3ec23a3ee1844a4b241
push id99939
push userahalberstadt@mozilla.com
push dateWed, 21 Feb 2018 21:02:56 +0000
reviewersjmaher
bugs1434974
milestone60.0a1
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
python/mozlint/mozlint/formatters/__init__.py
python/mozlint/mozlint/formatters/summary.py
python/mozlint/test/test_formatters.py
--- 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',