Refactor to use namedtuples because I like names. Changed some function names to better reflect what they do.
This commit is contained in:
parent
5cc8770ac3
commit
e11655e3b9
|
@ -9,11 +9,18 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import configparser
|
import configparser
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
import ConfigParser as configparser
|
import ConfigParser as configparser
|
||||||
|
|
||||||
|
try: # noqa: F401
|
||||||
|
from typing import Dict, List, Text, Any, Optional, Union, Callable, Tuple # noqa: F401
|
||||||
|
except: # noqa: F401
|
||||||
|
pass # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
|
||||||
|
@ -167,25 +174,23 @@ class MatchFilter:
|
||||||
# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/
|
# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/
|
||||||
#
|
#
|
||||||
|
|
||||||
def executable_exists(script, label):
|
def linter_exists(linter, label):
|
||||||
if not len(script):
|
if not len(linter):
|
||||||
sys.exit(
|
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
||||||
_('Syntax error in command configuration for {} ').format(label))
|
|
||||||
|
|
||||||
scriptname = script.split(' ').pop(0)
|
lintername = linter.split(' ').pop(0)
|
||||||
if not len(scriptname):
|
if not len(lintername):
|
||||||
sys.exit(
|
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
||||||
_('Syntax error in command configuration for {} ').format(label))
|
|
||||||
|
|
||||||
def is_executable(path):
|
def is_executable(path):
|
||||||
return os.path.exists(path) and os.access(path, os.X_OK)
|
return os.path.exists(path) and os.access(path, os.X_OK)
|
||||||
|
|
||||||
if scriptname.startswith('/'):
|
if lintername.startswith('/'):
|
||||||
return (is_executable(scriptname) and scriptname) or None
|
return (is_executable(lintername) and lintername) or None
|
||||||
|
|
||||||
# shutil.which() doesn't appear until Python 3, darnit.
|
# 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, lintername)
|
||||||
for path in os.environ.get('PATH').split(':')]
|
for path in os.environ.get('PATH').split(':')]
|
||||||
if is_executable(path)]
|
if is_executable(path)]
|
||||||
|
|
||||||
|
@ -193,10 +198,9 @@ def executable_exists(script, label):
|
||||||
|
|
||||||
|
|
||||||
def get_linter_status(config):
|
def get_linter_status(config):
|
||||||
|
|
||||||
def get_working_linter_names(config):
|
def get_working_linter_names(config):
|
||||||
return [i.name for i in config
|
return [i.name for i in config
|
||||||
if executable_exists(i.linter['command'], i.name)]
|
if linter_exists(i.linter['command'], i.name)]
|
||||||
|
|
||||||
working_linter_names = get_working_linter_names(config)
|
working_linter_names = get_working_linter_names(config)
|
||||||
broken_linter_names = (set([i.name for i in config]) - set(working_linter_names))
|
broken_linter_names = (set([i.name for i in config]) - set(working_linter_names))
|
||||||
|
@ -440,7 +444,6 @@ def run_linters(options, config, extras=[]):
|
||||||
all_filenames, unfindable_filenames = get_filelist(options, extras)
|
all_filenames, unfindable_filenames = get_filelist(options, extras)
|
||||||
|
|
||||||
is_lintable = MatchFilter(config)
|
is_lintable = MatchFilter(config)
|
||||||
|
|
||||||
lintable_filenames = set([filename for filename in all_filenames
|
lintable_filenames = set([filename for filename in all_filenames
|
||||||
if is_lintable(filename)])
|
if is_lintable(filename)])
|
||||||
|
|
||||||
|
@ -450,7 +453,6 @@ def run_linters(options, config, extras=[]):
|
||||||
|
|
||||||
cant_lint_filter = MatchFilter(build_config_subset(
|
cant_lint_filter = MatchFilter(build_config_subset(
|
||||||
broken_linter_names))
|
broken_linter_names))
|
||||||
|
|
||||||
cant_lint_filenames = [filename for filename in lintable_filenames
|
cant_lint_filenames = [filename for filename in lintable_filenames
|
||||||
if cant_lint_filter(filename)]
|
if cant_lint_filter(filename)]
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,19 @@
|
||||||
# Author: Elf M. Sternberg
|
# Author: Elf M. Sternberg
|
||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from collections import namedtuple
|
||||||
import getopt
|
import getopt
|
||||||
|
|
||||||
|
try: # noqa: F401
|
||||||
|
from typing import Dict, List, Text, Any, Optional, Union, Callable, Tuple # noqa: F401
|
||||||
|
except: # noqa: F401
|
||||||
|
pass # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
|
Option = namedtuple('Option', ['short', 'long', 'takes', 'help', 'conflicts']) # type: str, str, str, str, List[str]
|
||||||
|
|
||||||
|
Arguments = namedtuple('Arguments', ['arguments', 'filenames', 'excluded']) # type: List[str], List[str], List[str]
|
||||||
|
|
||||||
# This was a lot shorter and smarter in Hy...
|
# This was a lot shorter and smarter in Hy...
|
||||||
|
|
||||||
# A lot of what you see here is separated from git_lint itself, since this will not be
|
# A lot of what you see here is separated from git_lint itself, since this will not be
|
||||||
|
@ -17,6 +28,7 @@ import getopt
|
||||||
|
|
||||||
|
|
||||||
def cleanup_options(options, commandline):
|
def cleanup_options(options, commandline):
|
||||||
|
# type: (List[Option], List[str]) -> Arguments
|
||||||
"""Takes a table of options and the commandline, and returns a
|
"""Takes a table of options and the commandline, and returns a
|
||||||
dictionary of those options that appear on the commandline
|
dictionary of those options that appear on the commandline
|
||||||
along with any extra arguments.
|
along with any extra arguments.
|
||||||
|
@ -29,36 +41,39 @@ def cleanup_options(options, commandline):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def make_option_streamliner(options):
|
def make_option_streamliner(options):
|
||||||
|
# type: (List[Option]) -> Callable[[Dict[str, str], Option], Dict[str, str]]
|
||||||
|
|
||||||
"""Takes a list of option tuples, and returns a function that takes
|
"""Takes a list of option tuples, and returns a function that takes
|
||||||
the output of getopt and reduces it to the longopt key and
|
the output of getopt and reduces it to the longopt key and
|
||||||
associated values as a dictionary.
|
associated values as a dictionary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fullset = {}
|
fullset = {} # type: Dict[str, str]
|
||||||
for option in options:
|
for option in options:
|
||||||
if option[1]:
|
if option.long:
|
||||||
fullset['--' + option[1]] = option[1]
|
fullset['--' + option.long] = option.long
|
||||||
if option[0]:
|
if option.short:
|
||||||
fullset['-' + option[0]] = option[1]
|
fullset['-' + option.short] = option.long
|
||||||
|
|
||||||
def streamliner(acc, it):
|
def streamliner(acc, it):
|
||||||
|
# type: (Dict[str, str], Option) -> Dict[str, str]
|
||||||
acc[fullset[it[0]]] = it[1]
|
acc[fullset[it[0]]] = it[1]
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
return streamliner
|
return streamliner
|
||||||
|
|
||||||
def remove_conflicted_options(options, request):
|
def remove_conflicted_options(options, request):
|
||||||
|
# type: (List[Option], Dict[str, str]) -> Tuple[List[str], List[str]]
|
||||||
"""Takes our list of option tuples, and a cleaned copy of what was
|
"""Takes our list of option tuples, and a cleaned copy of what was
|
||||||
requested from getopt, and returns a copy of the request
|
requested from getopt, and returns a copy of the request
|
||||||
without any options that are marked as superseded, along with
|
without any options that are marked as superseded, along with
|
||||||
the list of superseded options
|
the list of superseded options
|
||||||
"""
|
"""
|
||||||
def get_excluded_keys(memo, opt):
|
def get_excluded_keys(memo, option):
|
||||||
return memo + ((len(opt) > 4 and opt[4]) or [])
|
return memo + option.conflicts
|
||||||
|
|
||||||
keys = request.keys()
|
keys = request.keys()
|
||||||
marked = [option for option in options if option[1] in keys]
|
marked = [option for option in options if option.long in keys]
|
||||||
exclude = reduce(get_excluded_keys, marked, [])
|
exclude = reduce(get_excluded_keys, marked, [])
|
||||||
excluded = [key for key in keys if key in exclude]
|
excluded = [key for key in keys if key in exclude]
|
||||||
cleaned = {key: request[key] for key in keys
|
cleaned = {key: request[key] for key in keys
|
||||||
|
@ -66,12 +81,12 @@ def cleanup_options(options, commandline):
|
||||||
return (cleaned, excluded)
|
return (cleaned, excluded)
|
||||||
|
|
||||||
def shortoptstogo(i):
|
def shortoptstogo(i):
|
||||||
return i[0] + ((i[2] and ':') or '')
|
return i.short + ((i.takes and ':') or '')
|
||||||
|
|
||||||
def longoptstogo(i):
|
def longoptstogo(i):
|
||||||
return i[1] + ((i[2] and '=') or '')
|
return i.long + ((i.takes and '=') or '')
|
||||||
|
|
||||||
optstringsshort = ''.join([shortoptstogo(opt) for opt in options if opt[0]])
|
optstringsshort = ''.join([shortoptstogo(opt) for opt in options if opt.short])
|
||||||
optstringslong = [longoptstogo(opt) for opt in options]
|
optstringslong = [longoptstogo(opt) for opt in options]
|
||||||
(chosen_options, filenames) = getopt.getopt(commandline[1:],
|
(chosen_options, filenames) = getopt.getopt(commandline[1:],
|
||||||
optstringsshort,
|
optstringsshort,
|
||||||
|
@ -84,4 +99,4 @@ def cleanup_options(options, commandline):
|
||||||
(ret, excluded) = remove_conflicted_options(
|
(ret, excluded) = remove_conflicted_options(
|
||||||
options, reduce(streamline_options, chosen_options, {}))
|
options, reduce(streamline_options, chosen_options, {}))
|
||||||
|
|
||||||
return (ret, filenames, excluded)
|
return Arguments(ret, filenames, excluded)
|
||||||
|
|
|
@ -1,43 +1,45 @@
|
||||||
import gettext
|
import gettext
|
||||||
|
from option_handler import Option
|
||||||
|
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
OPTIONS = [
|
OPTIONS = [
|
||||||
('o', 'only', True,
|
Option('o', 'only', True,
|
||||||
_('A comma-separated list of only those linters to run'), ['exclude']),
|
_('A comma-separated list of only those linters to run'), ['exclude']),
|
||||||
('x', 'exclude', True,
|
Option('x', 'exclude', True,
|
||||||
_('A comma-separated list of linters to skip'), []),
|
_('A comma-separated list of linters to skip'), []),
|
||||||
('l', 'linters', False,
|
Option('l', 'linters', False,
|
||||||
_('Show the list of configured linters'), []),
|
_('Show the list of configured linters'), []),
|
||||||
('b', 'base', False,
|
Option('b', 'base', False,
|
||||||
_('Check all changed files in the repository, not just those in the current directory.'), []),
|
_('Check all changed files in the repository, not just those in the current directory.'), []),
|
||||||
('a', 'all', False,
|
Option('a', 'all', False,
|
||||||
_('Scan all files in the repository, not just those that have changed.'), ['revision']),
|
_('Scan all files in the repository, not just those that have changed.'), ['revision']),
|
||||||
('r', 'revision', True,
|
Option('r', 'revision', True,
|
||||||
_('Scan all files changed between revisions'), []),
|
_('Scan all files changed between revisions'), []),
|
||||||
(None, 'pr', False,
|
Option(None, 'pr', False,
|
||||||
_('Scan all files changed between head and previous check-in'), ['revision']),
|
_('Scan all files changed between head and previous check-in'), ['revision']),
|
||||||
('e', 'every', False,
|
Option('e', 'every', False,
|
||||||
_('Short for -b -a: scan everything'), []),
|
_('Short for -b -a: scan everything'), []),
|
||||||
('w', 'workspace', False,
|
Option('w', 'workspace', False,
|
||||||
_('Scan the workspace'), ['staging']),
|
_('Scan the workspace'), ['staging']),
|
||||||
('s', 'staging', False,
|
Option('s', 'staging', False,
|
||||||
_('Scan the staging area (useful for pre-commit).'), []),
|
_('Scan the staging area (useful for pre-commit).'), []),
|
||||||
# ('g', 'changes', False,
|
# ('g', 'changes', False,
|
||||||
# _("Report lint failures only for diff'd sections"), ['complete']),
|
# _("Report lint failures only for diff'd sections"), ['complete']),
|
||||||
# ('p', 'complete', False,
|
# ('p', 'complete', False,
|
||||||
# _('Report lint failures for all files'), []),
|
# _('Report lint failures for all files'), []),
|
||||||
('t', 'bylinter', False,
|
Option('t', 'bylinter', False,
|
||||||
_('Group the reports by linter first as they appear in the config file [default]'), []),
|
_('Group the reports by linter first as they appear in the config file [default]'), []),
|
||||||
('f', 'byfile', False,
|
Option('f', 'byfile', False,
|
||||||
_('Group the reports by file first'), []),
|
_('Group the reports by file first'), []),
|
||||||
('d', 'dryrun', False,
|
Option('d', 'dryrun', False,
|
||||||
_('Dry run - report what would be done, but do not run linters'), []),
|
_('Dry run - report what would be done, but do not run linters'), []),
|
||||||
('c', 'config', True,
|
Option('c', 'config', True,
|
||||||
_('Path to config file'), []),
|
_('Path to config file'), []),
|
||||||
('h', 'help', False,
|
Option('h', 'help', False,
|
||||||
_('This help message'), []),
|
_('This help message'), []),
|
||||||
('V', 'verbose', False,
|
Option('V', 'verbose', False,
|
||||||
_('A slightly more verbose output'), []),
|
_('A slightly more verbose output'), []),
|
||||||
('v', 'version', False,
|
Option('v', 'version', False,
|
||||||
_('Version information'), [])
|
_('Version information'), [])
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue