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,26 +12,30 @@ checkers against changed files in your current working directory or
staging area. It can be configured to work with any `lint` command.
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
--------
* Highly configurable - configuration files, both for git-lint and
individual linters, can be global or per-project.
* Only checks what has been changed, but this can be overriden from the
command line.
* For some linters, can lint both full-files and or only the parts that
have changed.
* Only checks files that have been changed, but this can be overriden
from the command line.
* Can be used directly as a pre-commit hook, to ensure you personally
don't check in anything broken.
* Used this way, it checks the *staging* area, not your workspace.
* Used this way, it checks the *staging* area, not your workspace.
* When using the staging area, it stashes your current work. Upon
restoration of your workspace, it ensures the timestamps are the
same, so as not to confuse your build system or IDE.
* When using the staging area, it stashes your current work. Upon
restoration of your workspace, it ensures the timestamps are the
same, so as not to confuse your build system or IDE.
Credits
-------

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
from git_lint import load_config, run_linters, git_base
import gettext
_ = gettext.gettext
def main(*args):
if git_base is None:
sys.exit(_('A git repository was not found.'))
pre_commit_config = {
pre_commit_options = {
'staging': True,
'base': True
}
config = load_config(pre_commit_config, git_base)
return run_linters(pre_commit_config, config, [])
config = load_config(pre_commit_options, git_base)
(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__':

View File

@ -188,6 +188,7 @@ def executable_exists(script, label):
if scriptname.startswith('/'):
return (is_executable(scriptname) and scriptname) or None
# shutil.which() doesn't appear until Python 3, darnit.
possibles = [path for path in
[os.path.join(path, scriptname)
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')
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):
"""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], [])
def run_linters(options, config, extras):
def run_linters(options, config, extras=[]):
def build_config_subset(keys):
""" Returns a subset of the configuration, with only those linters mentioned in keys """