Adding a check for readability.

This commit is contained in:
Elf M. Sternberg 2016-09-29 18:36:27 -07:00
parent efd187e955
commit 1db5feac16
4 changed files with 110 additions and 13 deletions

View File

@ -12,17 +12,21 @@ checkers against changed files in your current working directory or
staging area. It can be configured to work with any `lint` command. staging area. It can be configured to work with any `lint` command.
Some commands may require shell wrappers. Some commands may require shell wrappers.
While it may be possible to create a custom lint command in your npm,
grunt, Make, CMake, or whatever, the fact is we all use a VCS, and
most of us use git. Having a centralized repository for what we want
checked and how we want it checked, associated with git (and by
extension, as a pre-commit hook), that can be run at any time for any
reason.
Features Features
-------- --------
* Highly configurable - configuration files, both for git-lint and * Highly configurable - configuration files, both for git-lint and
individual linters, can be global or per-project. individual linters, can be global or per-project.
* Only checks what has been changed, but this can be overriden from the * Only checks files that have been changed, but this can be overriden
command line. from the command line.
* For some linters, can lint both full-files and or only the parts that
have changed.
* Can be used directly as a pre-commit hook, to ensure you personally * Can be used directly as a pre-commit hook, to ensure you personally
don't check in anything broken. don't check in anything broken.

68
bin/git-lint-style.py Normal file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env python
from __future__ import print_function
from functools import reduce
import subprocess
import os.path
import getopt
import sys
import re
import os
import gettext
_ = gettext.gettext
def fullshell(cmd):
process = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
(stdout, stderr) = process.communicate()
return (stdout, stderr, process.returncode)
def path_which(scriptname):
def is_executable(path):
return os.path.exists(path) and os.access(path, os.X_OK)
possibles = [path for path in
[os.path.join(path, scriptname)
for path in os.environ.get('PATH').split(':')]
if is_executable(path)]
return (len(possibles) and possibles.pop(0)) or False
def main(*args):
(opts, filenames) = getopt.getopt(args[1:], '', ['min=', 'max='])
options = {re.sub('^\-+', '', i[0]): i[1] for i in opts}
style = path_which('style')
if not style:
sys.exit(_('Could not find the style program'))
if len(filenames) == 0:
sys.exit(_('No files specifed to scan'))
(values, error, returncode) = fullshell([style, filenames[0]])
if returncode != 0:
print(error)
return returncode
kincaid = re.search(r'Kincaid:\s*([\d\.]+)', values, re.MULTILINE)
val = float(kincaid.group(1))
msg = ""
if options.get('min', None):
if val < int(options.get('min')):
msg = _('Readability too low')
if options.get('max', None):
if val > int(options.get('max')):
msg = _('Readability too high')
print(_("{}: {} {}").format(filenames[0], val, msg))
return (msg != "" and 1) or 0
if __name__ == '__main__':
import sys
sys.exit(main(*sys.argv))

View File

@ -1,17 +1,38 @@
#!/usr/bin/env python #!/usr/bin/env python
from git_lint import load_config, run_linters, git_base from git_lint import load_config, run_linters, git_base
import gettext
_ = gettext.gettext
def main(*args): def main(*args):
if git_base is None: if git_base is None:
sys.exit(_('A git repository was not found.')) sys.exit(_('A git repository was not found.'))
pre_commit_config = { pre_commit_options = {
'staging': True, 'staging': True,
'base': True 'base': True
} }
config = load_config(pre_commit_config, git_base) config = load_config(pre_commit_options, git_base)
return run_linters(pre_commit_config, config, [])
(results,
unlintable_filenames,
cant_lint_filenames,
broken_linter_names,
unfindable_filenames) = run_linters(options, config)
print_report(results,
unlintable_filenames,
cant_lint_filenames,
broken_linter_names,
unfindable_filenames,
options)
if not len(results):
return 0
return max([i[2] for i in results if len(i)])
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -188,6 +188,7 @@ def executable_exists(script, label):
if scriptname.startswith('/'): if scriptname.startswith('/'):
return (is_executable(scriptname) and scriptname) or None return (is_executable(scriptname) and scriptname) or None
# shutil.which() doesn't appear until Python 3, darnit.
possibles = [path for path in possibles = [path for path in
[os.path.join(path, scriptname) [os.path.join(path, scriptname)
for path in os.environ.get('PATH').split(':')] for path in os.environ.get('PATH').split(':')]
@ -253,6 +254,9 @@ def get_filelist(options, extras):
stream = [entry for entry in get_git_response(cmd).split(u'\x00') stream = [entry for entry in get_git_response(cmd).split(u'\x00')
if len(entry) > 0] if len(entry) > 0]
# Yeah, baby, recursion, the way this is meant to be handled.
# If you have more than 999 files that need linting, you have
# a bigger problem...
def parse_stream(acc, stream): def parse_stream(acc, stream):
"""Parse the list of files. T """Parse the list of files. T
@ -420,7 +424,7 @@ class Linters:
return reduce(operator.add, [dryrunonce(linter, self.filenames) for linter in self.linters], []) return reduce(operator.add, [dryrunonce(linter, self.filenames) for linter in self.linters], [])
def run_linters(options, config, extras): def run_linters(options, config, extras=[]):
def build_config_subset(keys): def build_config_subset(keys):
""" Returns a subset of the configuration, with only those linters mentioned in keys """ """ Returns a subset of the configuration, with only those linters mentioned in keys """