Bug 1281899 - [mozlint] Create cli module and move logic from tools/lint/mach_commands.py there, r=smacleod
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Fri, 24 Jun 2016 14:06:22 -0400
changeset 303821 eaaff060e865adebe50d6877b670db089ca5caf5
parent 303820 1289141491d126653e3b1f50f4cd907c26ba2fef
child 303822 6984d7cf65bbcf19443ef4c8e09e5c7f4e8ae5c3
push id30403
push usergszorc@mozilla.com
push dateThu, 07 Jul 2016 01:15:37 +0000
treeherdermozilla-central@39ec98bf2580 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmacleod
bugs1281899
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1281899 - [mozlint] Create cli module and move logic from tools/lint/mach_commands.py there, r=smacleod There is currently no built-in user interface to mozlint. The only existing interface is the external cli provided by |mach lint|. However, in the future mozlint may need to be used in a context where mach isn't readily available (i.e version-control-tools). This patch basically just moves the cli logic out of mach_commands.py, and into mozlint core. That way it can be re-used in other places without needing to be re-implemented. The |mach lint setup| subcommand was removed because apparently subcommands don't work with the parser attribute. Nothing was using it yet anyway, so I removed it for now. It may get re-added in some form in the future. MozReview-Commit-ID: aOGp2Yrncs
python/mozlint/mozlint/cli.py
python/mozlint/mozlint/types.py
tools/lint/mach_commands.py
new file mode 100644
--- /dev/null
+++ b/python/mozlint/mozlint/cli.py
@@ -0,0 +1,91 @@
+# 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 print_function, unicode_literals
+
+import os
+import subprocess
+import sys
+from argparse import ArgumentParser
+
+
+SEARCH_PATHS = []
+
+
+class MozlintParser(ArgumentParser):
+    arguments = [
+        [['paths'],
+         {'nargs': '*',
+          'default': None,
+          'help': "Paths to file or directories to lint, like "
+                  "'browser/components/loop' or 'mobile/android'. "
+                  "Defaults to the current directory if not given.",
+          }],
+        [['-l', '--linter'],
+         {'dest': 'linters',
+          'default': [],
+          'action': 'append',
+          'help': "Linters to run, e.g 'eslint'. By default all linters "
+                  "are run for all the appropriate files.",
+          }],
+        [['-f', '--format'],
+         {'dest': 'fmt',
+          'default': 'stylish',
+          'help': "Formatter to use. Defaults to 'stylish'.",
+          }],
+        [['-n', '--no-filter'],
+         {'dest': 'use_filters',
+          'default': True,
+          'action': 'store_false',
+          'help': "Ignore all filtering. This is useful for quickly "
+                  "testing a directory that otherwise wouldn't be run, "
+                  "without needing to modify the config file.",
+          }],
+    ]
+
+    def __init__(self, **kwargs):
+        ArgumentParser.__init__(self, usage=self.__doc__, **kwargs)
+
+        for cli, args in self.arguments:
+            self.add_argument(*cli, **args)
+
+
+def find_linters(self, linters=None):
+    lints = []
+    for search_path in SEARCH_PATHS:
+        if not os.path.isdir(search_path):
+            continue
+
+        files = os.listdir(search_path)
+        for f in files:
+            name, ext = os.path.splitext(f)
+            if ext != '.lint':
+                continue
+
+            if linters and name not in linters:
+                continue
+
+            lints.append(os.path.join(search_path, f))
+    return lints
+
+
+def run(paths, linters, fmt, **lintargs):
+    from mozlint import LintRoller, formatters
+    paths = paths or ['.']
+
+    lint = LintRoller(**lintargs)
+    lint.read(find_linters(linters))
+
+    # run all linters
+    results = lint.roll(paths)
+
+    formatter = formatters.get(fmt)
+    print(formatter(results))
+    return 1 if results else 0
+
+
+if __name__ == '__main__':
+    parser = MozlintParser()
+    args = vars(parser.parse_args())
+    sys.exit(run(**args))
--- a/python/mozlint/mozlint/types.py
+++ b/python/mozlint/mozlint/types.py
@@ -27,17 +27,17 @@ class BaseType(object):
         """
         exclude = lintargs.get('exclude', [])
         exclude.extend(linter.get('exclude', []))
 
         if lintargs.get('use_filters', True):
             paths, exclude = filterpaths(paths, linter.get('include'), exclude)
 
         if not paths:
-            print("{}: No files to lint for specified paths!".format(linter['name']))
+            print("{}: no files to lint in specified paths".format(linter['name']))
             return
 
         lintargs['exclude'] = exclude
         if self.batch:
             return self._lint(paths, linter, **lintargs)
 
         errors = []
         try:
--- a/tools/lint/mach_commands.py
+++ b/tools/lint/mach_commands.py
@@ -18,17 +18,16 @@ from mozbuild.base import (
     MachCommandBase,
 )
 
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
-    SubCommand,
 )
 
 
 here = os.path.abspath(os.path.dirname(__file__))
 
 
 ESLINT_PACKAGES = [
     "eslint@2.9.0",
@@ -58,92 +57,34 @@ NPM_NOT_FOUND_MESSAGE = '''
 Node Package Manager (npm) is either not installed or installed to a
 non-standard path. Please install npm from https://nodejs.org (it comes as an
 option in the node installation) and try again.
 
 Valid installation paths:
 '''.strip()
 
 
+def setup_argument_parser():
+    from mozlint import cli
+    return cli.MozlintParser()
+
+
 @CommandProvider
 class MachCommands(MachCommandBase):
 
     @Command(
         'lint', category='devenv',
-        description='Run linters.')
-    @CommandArgument(
-        'paths', nargs='*', default=None,
-        help="Paths to file or directories to lint, like "
-             "'browser/components/loop' or 'mobile/android'. "
-             "Defaults to the current directory if not given.")
-    @CommandArgument(
-        '-l', '--linter', dest='linters', default=None, action='append',
-        help="Linters to run, e.g 'eslint'. By default all linters are run "
-             "for all the appropriate files.")
-    @CommandArgument(
-        '-f', '--format', dest='fmt', default='stylish',
-        help="Formatter to use. Defaults to 'stylish'.")
-    @CommandArgument(
-        '-n', '--no-filter', dest='use_filters', default=True, action='store_false',
-        help="Ignore all filtering. This is useful for quickly testing a "
-             "directory that otherwise wouldn't be run, without needing to "
-             "modify the config file.")
-    def lint(self, paths, linters=None, fmt='stylish', **lintargs):
+        description='Run linters.',
+        parser=setup_argument_parser)
+    def lint(self, paths, linters, fmt, **lintargs):
         """Run linters."""
-        from mozlint import LintRoller, formatters
-
-        paths = paths or ['.']
-
-        lint_files = self.find_linters(linters)
-
+        from mozlint import cli
         lintargs['exclude'] = ['obj*']
-        lint = LintRoller(**lintargs)
-        lint.read(lint_files)
-
-        # run all linters
-        results = lint.roll(paths)
-
-        status = 0
-        if results:
-            status = 1
-
-        formatter = formatters.get(fmt)
-        print(formatter(results))
-        return status
-
-    @SubCommand('lint', 'setup',
-                "Setup required libraries for specified lints.")
-    @CommandArgument(
-        '-l', '--linter', dest='linters', default=None, action='append',
-        help="Linters to run, e.g 'eslint'. By default all linters are run "
-             "for all the appropriate files.")
-    def lint_setup(self, linters=None, **lintargs):
-        from mozlint import LintRoller
-
-        lint_files = self.find_linters(linters)
-        lint = LintRoller(lintargs=lintargs)
-        lint.read(lint_files)
-
-        for l in lint.linters:
-            if 'setup' in l:
-                l['setup']()
-
-    def find_linters(self, linters=None):
-        lints = []
-        files = os.listdir(here)
-        for f in files:
-            name, ext = os.path.splitext(f)
-            if ext != '.lint':
-                continue
-
-            if linters and name not in linters:
-                continue
-
-            lints.append(os.path.join(here, f))
-        return lints
+        cli.SEARCH_PATHS.append(here)
+        return cli.run(paths, linters, fmt, **lintargs)
 
     @Command('eslint', category='devenv',
              description='Run eslint or help configure eslint for optimal development.')
     @CommandArgument('-s', '--setup', default=False, action='store_true',
                      help='configure eslint for optimal development.')
     @CommandArgument('-e', '--ext', default='[.js,.jsm,.jsx,.xml,.html]',
                      help='Filename extensions to lint, default: "[.js,.jsm,.jsx,.xml,.html]".')
     @CommandArgument('-b', '--binary', default=None,