Compare commits

...

23 Commits

Author SHA1 Message Date
Elf M. Sternberg e86fe1ff07 Adding DESCRIPTION file for local project management. 2017-09-10 09:48:53 -07:00
Ken Sternberg 41d61476e1 Updated Contributor list, of course! 2017-08-02 10:43:48 -07:00
Elf M. Sternberg ef2a5c579b Merge pull request #3 from tino/add-only-and-exclude
Implement --only and --exclude flags
2017-08-01 13:23:14 -07:00
Tino de Bruijn b485697742 Implement --only and --exclude flags 2017-07-28 11:35:20 +02:00
Ken Sternberg 1e26d46d17 Document a little more. 2017-05-04 11:19:03 -07:00
Ken Sternberg e5a8f93630 Lint and format fix. 2017-05-04 10:40:26 -07:00
Ken Sternberg 09dcb1a050 Cleanup old and unnecessary build files. 2017-05-04 10:39:04 -07:00
Ken Sternberg e11655e3b9 Refactor to use namedtuples because I like names. Changed some function names to better reflect what they do. 2017-05-04 10:35:57 -07:00
Elf M. Sternberg 5cc8770ac3 Merge branch 'master' of github.com:elfsternberg/git-linter
* 'master' of github.com:elfsternberg/git-linter:
  Added a command, '--revision', the lets you test files that were changed and committed already.  This is mostly a helpful addition to "Oh, I didn't lint those!"
2017-01-28 14:45:45 -08:00
Elf M. Sternberg 8fec735719 Added some ToDos to the README. 2017-01-28 14:45:33 -08:00
Elf M. Sternberg a8451c83bf Added a command, '--revision', the lets you test files that were changed
and committed already.  This is mostly a helpful addition to "Oh, I didn't
lint those!"

Also, added the basic '--pr', which gives you the list of files
changed in the very last commit, so you don't have to specify which
revision you mean.
2016-10-07 10:35:40 -07:00
Elf M. Sternberg 1a942e6b1c Bug revision version bump. 2016-10-06 09:11:52 -07:00
Elf M. Sternberg 9b40abe4ae Python 3 compatibility bug. 2016-10-06 09:08:18 -07:00
Elf M. Sternberg 6e09df1427 A better Sphinx theme. 2016-10-06 08:31:43 -07:00
Elf M. Sternberg f70aea7ec3 Code re-org for my sanity. 2016-10-05 14:58:07 -07:00
Elf M. Sternberg 3461709ebf Updating docs. 2016-10-05 13:11:49 -07:00
Elf M. Sternberg 74d217d6fe First version commited to pypi. Fixed bug that prevented clean installation
on OSX.  Bumped version to 0.0.6, and tagged.
2016-10-05 11:14:46 -07:00
Elf M. Sternberg f12b746ade Fixed references to installation. 2016-10-05 09:14:59 -07:00
Elf M. Sternberg 4895dde1b6 Fixed a spelling error. 2016-10-04 10:38:53 -07:00
Elf M. Sternberg d046268ad6 RST still makes no sense. 2016-10-04 10:28:23 -07:00
Elf M. Sternberg 1b5b215e3b RST makes no sense. 2016-10-04 10:27:37 -07:00
Elf M. Sternberg 556c56fb43 Fixing links, adding documentatin. 2016-10-04 10:26:44 -07:00
Elf M. Sternberg a1b5ae9eb0 Fixed usage inclusion. 2016-10-04 10:16:17 -07:00
17 changed files with 218 additions and 174 deletions

View File

@ -10,4 +10,4 @@ Development Lead
Contributors Contributors
------------ ------------
None yet. Why not be the first? * Tino de Bruijn <work@tino.io>

1
DESCRIPTION.md Normal file
View File

@ -0,0 +1 @@
GitLint is a git command to automatically run a suite of pre-defined linters.

View File

@ -8,5 +8,6 @@ recursive-include tests *
recursive-exclude * __pycache__ recursive-exclude * __pycache__
recursive-exclude * *.py[co] recursive-exclude * *.py[co]
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif *.1
include docs/_build/man/git-lint.1 include docs/_build/man/git-lint.1
include bin/pre-commit bin/git-lint-style

View File

@ -34,9 +34,18 @@ To lint what's in your staging directory:
`git lint -s` `git lint -s`
Documentation
-------------
Complete documentation for the project is available in the docs directory, or at `Git
Linter Docs <https://elfsternberg.github.io/git-linter/index.html>`_.
Install Install
------- -------
This *ought* to work:
`pip install git-linter` `pip install git-linter`
You will need to copy the .git-lint configuration file to either your You will need to copy the .git-lint configuration file to either your
@ -44,6 +53,13 @@ home directory or the repo`s base directory. Edit the configuration
file as needed. You will also need any linters that you plan on file as needed. You will also need any linters that you plan on
running. running.
As git-linter is still mostly alpha code, it might be better to install
from source:
``
git clone https://github.com/elfsternberg/git-linter
python setup.py install
``
Features Features
-------- --------
@ -66,12 +82,18 @@ Features
Acknowledgements Acknowledgements
---------------- ----------------
`Git lint` started life as a simple pre-commit hook. Most of the
changes since were inspired by Steve Pulec's *`Why You Need a Git
Pre-Commit Hook and Why Most Are Wrong`_ ,* as well as just my own needs as
a software developer.
.. _Why You Need a Git Pre-Commit Hook and Why Most Are Wrong: https://dzone.com/articles/why-your-need-git-pre-commit `Git lint` started life as a simple pre-commit hook. Most of the changes since were
inspired by Steve Pulec's `Why You Need a Git Pre-Commit Hook and Why Most Are Wrong <https://dzone.com/articles/why-your-need-git-pre-commit>`_, as well as just my own needs
as a software developer.
To do
-----
* The '-q' and '--quiet' arguments do not work.
* Ought to be able to silence the "no linter found" message.
* Ought to be able to configure '-q' and '--silence' commands in .git-lint/config file.
* Ought to be able to override config file for above.
Disclaimer Disclaimer

View File

@ -111,7 +111,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = 'default' html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a # Theme options are theme-specific and customize the look and feel of a
# theme further. For a list of options available for each theme, see the # theme further. For a list of options available for each theme, see the

View File

@ -3,8 +3,12 @@
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. contain the root `toctree` directive.
Welcome to Git Lint's documentation! Git Lint
====================================== ========
Git Lint provides a new git command that automatically runs identifiable linters (style
and syntax quality assurance programs) against changed files in your current git
repository or staging area.
Contents: Contents:

View File

@ -12,7 +12,7 @@ To install Git Lint, run this command in your terminal:
.. code-block:: console .. code-block:: console
$ pip install git_linter $ pip install git-linter
If you don't have `pip`_ installed, this `Python installation guide`_ can guide If you don't have `pip`_ installed, this `Python installation guide`_ can guide
you through the process. you through the process.
@ -30,13 +30,13 @@ You can either clone the public repository:
.. code-block:: console .. code-block:: console
$ git clone git://github.com/elfsternberg/git_linter $ git clone git://github.com/elfsternberg/git-linter
Or download the `tarball`_: Or download the `tarball`_:
.. code-block:: console .. code-block:: console
$ curl -OL https://github.com/elfsternberg/git_linter/tarball/master $ curl -OL https://github.com/elfsternberg/git-linter/tarball/master
Once you have a copy of the source, you can install it with: Once you have a copy of the source, you can install it with:
@ -44,9 +44,8 @@ Once you have a copy of the source, you can install it with:
$ python setup.py install $ python setup.py install
.. _Github repo: https://github.com/elfsternberg/git_linter .. _Github repo: https://github.com/elfsternberg/git-linter
.. _tarball: https://github.com/elfsternberg/git_linter/tarball/master .. _tarball: https://github.com/elfsternberg/git-linter/tarball/master
Once installed, please copy the '.git-lint' example file. You may install this either in Once installed, please copy the '.git-lint' example file. You may install this either in
your home directory as ``.git-lint`` or in your project's git directory as your home or repository directory as ``.git-lint``.
``.git/lint/git-lint``

View File

@ -12,25 +12,13 @@ git lint [options] [filenames]
Options Options
------- -------
.. include: arguments.rst .. include:: arguments.rst
As a pre-commit hook: As a pre-commit hook:
--------------------- ---------------------
.. code-block:: python There's a file, pre-commit, in the /bin directory with the project. (Or you can download
it from the github repository.) Install it in you .git/hooks/pre-commit file, and
chmod +x .git/hooks/pre-commit.
#!/usr/bin/env python The pre-commit hook is *experimental*. Please be careful with it.
import git_lint
git_lint.run_precommit(staging = True, timestamps = True)
Install this file in your project's ``.git/hooks/pre-commit``, and set
the file's executable flag to ``true``:
.. code-block:: shell
chmod +x pre-commit
Please see the :ref:`api` for more details on options taken by the
``run_precommit()`` and ``run_gitlint`` commands.
There is an example ``pre-commit`` script shipped with ``git lint``.

View File

@ -2,6 +2,6 @@
__author__ = 'Kenneth M. "Elf" Sternberg' __author__ = 'Kenneth M. "Elf" Sternberg'
__email__ = 'elf.sternberg@gmail.com' __email__ = 'elf.sternberg@gmail.com'
__version__ = '0.0.4' __version__ = '0.0.7'
__all__ = ['git_lint'] __all__ = ['git_lint']

View File

@ -11,7 +11,7 @@ import gettext
_ = gettext.gettext _ = gettext.gettext
NAME = 'git-lint' NAME = 'git-lint'
VERSION = '0.0.4' VERSION = '0.0.7'
def main(): def main():

View File

@ -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
@ -24,7 +31,20 @@ _ = gettext.gettext
# |___/ # |___/
def find_config_file(options, base): # (commandLineDictionary, repositoryLocation) -> (configurationDictionary | exit)
def load_config(options, base):
"""Loads the git-lint configuration file.
Returns the configuration file as a dictionary of dictionaries.
Performs substitutions as specified in the SafeConfigParser
specification; the only one performed currently is the 'repodir'
will be replaced with the base directory of the repository.
Combined with the option to specify the .git-lint configuration as
a directory, this allows users to keep per-project configuration
files for specific linters.
"""
def find_config_file(options, base):
""" Returns the configuration file from a prioritized list of locations. """ Returns the configuration file from a prioritized list of locations.
Locations are prioritized as: Locations are prioritized as:
@ -56,20 +76,6 @@ def find_config_file(options, base):
return matches[0] return matches[0]
# (commandLineDictionary, repositoryLocation) -> (configurationDictionary | exit)
def load_config(options, base):
"""Loads the git-lint configuration file.
Returns the configuration file as a dictionary of dictionaries.
Performs substitutions as specified in the SafeConfigParser
specification; the only one performed currently is the 'repodir'
will be replaced with the base directory of the repository.
Combined with the option to specify the .git-lint configuration as
a directory, this allows users to keep per-project configuration
files for specific linters.
"""
Linter = namedtuple('Linter', ['name', 'linter']) Linter = namedtuple('Linter', ['name', 'linter'])
path = find_config_file(options, base) path = find_config_file(options, base)
configloader = configparser.SafeConfigParser() configloader = configparser.SafeConfigParser()
@ -168,37 +174,34 @@ 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)]
return (len(possibles) and possibles.pop(0)) or False return (len(possibles) and possibles.pop(0)) or False
def get_working_linter_names(config):
return [i.name for i in config
if executable_exists(i.linter['command'], i.name)]
def get_linter_status(config): def get_linter_status(config):
def get_working_linter_names(config):
return [i.name for i in config
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))
return working_linter_names, broken_linter_names return working_linter_names, broken_linter_names
@ -272,6 +275,11 @@ def get_filelist(options, extras):
return check_for_conflicts(parse_stream([], stream)) return check_for_conflicts(parse_stream([], stream))
def revision_list():
cmd = ['diff', '--name-only', '-z', options.get('revision')]
return [entry for entry in get_git_response(cmd).split(u'\x00')
if len(entry) > 0]
def staging_list(): def staging_list():
""" Return the list of files added or modified to the stage """ """ Return the list of files added or modified to the stage """
@ -303,6 +311,9 @@ def get_filelist(options, extras):
working_directory_trans = base_file_filter working_directory_trans = base_file_filter
file_list_generator = working_list file_list_generator = working_list
if 'revision' in options:
file_list_generator = revision_list
working_directory_trans = base_file_filter
if 'all' in options: if 'all' in options:
file_list_generator = all_list file_list_generator = all_list
if 'staging' in options: if 'staging' in options:
@ -421,6 +432,9 @@ class Linters:
def run_linters(options, config, extras=[]): def run_linters(options, config, extras=[]):
if 'pr' in options:
options.pop('pr')
options['revision'] = 'HEAD^..HEAD'
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 """
@ -430,17 +444,25 @@ 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)])
unlintable_filenames = set(all_filenames) - lintable_filenames unlintable_filenames = set(all_filenames) - lintable_filenames
# Filter the linter config down to the selected ones.
if 'only' in options:
config = [linter for linter in config
if linter.name in options['only']]
elif 'exclude' in options:
config = [linter for linter in config
if linter.name not in options['exclude']]
if not len(config):
raise RuntimeError('No linters left to run! Be less strict with --only and --exclude.')
working_linter_names, broken_linter_names = get_linter_status(config) working_linter_names, broken_linter_names = get_linter_status(config)
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)]

View File

@ -2,8 +2,18 @@
# 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: Dict[str, 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 +27,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.
@ -24,41 +35,50 @@ def cleanup_options(options, commandline):
:param List(Tuple (string, string, boolean, string, List(string))) options, :param List(Tuple (string, string, boolean, string, List(string))) options,
The table of options: One-letter option, long option, takes arguments, The table of options: One-letter option, long option, takes arguments,
Help text, list of (long) options superseded by this one. Help text, list of (long) options superseded by this one.
: param List(strings) commandline : param List(strings) commandline
The arguments as received by the start-up process The arguments as received by the start-up process
: returns List(strings), List(strings), List(strings)
The longopt dictionary of arguments and associated values (if any)
The list of filenames left after argument processing
The longopt list of arguments that were excluded by argument precedence
""" """
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 +86,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]) 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 +104,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)

View File

@ -1,39 +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.'), []), _('Scan all files in the repository, not just those that have changed.'), ['revision']),
('e', 'every', False, Option('r', 'revision', True,
_('Scan all files changed between revisions'), []),
Option(None, 'pr', False,
_('Scan all files changed between head and previous check-in'), ['revision']),
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'), [])
] ]

View File

@ -1,16 +1,19 @@
from __future__ import print_function from __future__ import print_function
from functools import reduce
from .git_lint import load_config, run_linters, git_base from .git_lint import load_config, run_linters, git_base
import operator import operator
import gettext import gettext
_ = gettext.gettext _ = gettext.gettext
def base_file_cleaner(files): def print_report(results, unlintable_filenames, cant_lint_filenames,
broken_linter_names, unfindable_filenames, options={'bylinter': True}):
def base_file_cleaner(files):
return [file.replace(git_base + '/', '', 1) for file in files] return [file.replace(git_base + '/', '', 1) for file in files]
# ICK. Mutation, references, and hidden assignment.
# ICK. Mutation, references, and hidden assignment. def group_by(iterable, field_id):
def group_by(iterable, field_id):
results = [] results = []
keys = {} keys = {}
for obj in iterable: for obj in iterable:
@ -22,9 +25,6 @@ def group_by(iterable, field_id):
results.append((key, keys[key])) results.append((key, keys[key]))
return results return results
def print_report(results, unlintable_filenames, cant_lint_filenames,
broken_linter_names, unfindable_filenames, options={'bylinter': True}):
sort_position = 1 sort_position = 1
grouping = _('Linter: {}') grouping = _('Linter: {}')
if 'byfile' in options: if 'byfile' in options:
@ -44,9 +44,9 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
print ('') print ('')
if len(broken_linter_names) and (len(cant_lint_filenames) or ('verbose' in options)): if len(broken_linter_names) and (len(cant_lint_filenames) or ('verbose' in options)):
print(_('These linters could not be run:'), ','.join(broken_linter_names)) print(_('Linters not found:'), ','.join(broken_linter_names))
if len(cant_lint_filenames): if len(cant_lint_filenames):
print(_('Files not linted:')) print(' ' + _('Files not linted:'))
print('\n'.join([' {}'.format(f) for f in cant_lint_filenames])) print('\n'.join([' {}'.format(f) for f in cant_lint_filenames]))
print('') print('')
@ -56,7 +56,7 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
print('') print('')
if len(unfindable_filenames): if len(unfindable_filenames):
print(_('Files not be found:')) print(_('Files not found:'))
print('\n'.join([' {}'.format(f) for f in unfindable_filenames])) print('\n'.join([' {}'.format(f) for f in unfindable_filenames]))
print('') print('')
@ -64,7 +64,7 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
def print_help(options, name): def print_help(options, name):
print(_('Usage: {} [options] [filenames]').format(name)) print(_('Usage: {} [options] [filenames]').format(name))
for item in options: for item in options:
print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3])) print(' {:<2} --{:<12} {}'.format((item[0] and ('-' + item[0])) or '', item[1], item[3]))
def print_version(name, version): def print_version(name, version):

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.0.4 current_version = 0.0.7
commit = True commit = True
tag = True tag = True
@ -16,3 +16,4 @@ universal = 1
[flake8] [flake8]
exclude = docs exclude = docs

View File

@ -39,7 +39,7 @@ test_requirements = [
setup( setup(
name='git_linter', name='git_linter',
version='0.0.4', version='0.0.7',
description="A git command to lint everything in your workspace (or stage) that was changed since the last commit.", description="A git command to lint everything in your workspace (or stage) that was changed since the last commit.",
long_description=readme + '\n\n' + history, long_description=readme + '\n\n' + history,
author='Kenneth M. "Elf" Sternberg', author='Kenneth M. "Elf" Sternberg',

View File

@ -1,20 +0,0 @@
#!/usr/bin/hy ; -*- mode: clojure -*-
(import subprocess os sys os.path)
(defn fmit [formatstring iterable]
(apply .format (+ [formatstring] (list iterable))))
(defmain [&rest args]
(let [[git_dir (subprocess.check_output ["git" "rev-parse" "--show-toplevel"])]]
(.append sys.path (os.path.join git_dir "git_lint"))
(import [git_lint [git_lint]])
(print (.join "\n" (map (fn [i] (if (get i 2)
(+ (fmit "\item[\oOptArg{{-{0}}}{{ names}} " i)
(fmit "\oOptArg{{--{1}}}={{ names}}] " i)
(fmit "{3}" i))
(+ (fmit "\item[\oOptArg{{-{0}}} " i)
(fmit "\oOptArg{{--{1}}}] " i)
(fmit "{3}" i))))
git_lint.OPTIONS_LIST)))))