From 6a1f945e3105761b1c19eb97168a8b8b8d379898 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 5 Apr 2014 19:12:30 -0700 Subject: [PATCH 1/3] Add color utility. --- pre_commit/color.py | 32 ++++++++++++++++++++++++++++++++ tests/color_test.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 pre_commit/color.py create mode 100644 tests/color_test.py diff --git a/pre_commit/color.py b/pre_commit/color.py new file mode 100644 index 00000000..d3875c39 --- /dev/null +++ b/pre_commit/color.py @@ -0,0 +1,32 @@ + +import sys + +RED = '\033[41m' +GREEN = '\033[42m' +NORMAL = '\033[0m' + + +def format_color(text, color, use_color): + """Format text with color. + + Args: + text - Text to be formatted with color if `use_color` + color - The color start string + use_color - Whether or not to color + """ + if not use_color: + return text + else: + return u'{0}{1}{2}'.format(color, text, NORMAL) + + +def use_color(setting): + """Choose whether to use color based on the command argument. + + Args: + setting - Either `auto`, `always`, or `never` + """ + return ( + setting == 'always' or + (setting == 'auto' and sys.stdout.isatty()) + ) diff --git a/tests/color_test.py b/tests/color_test.py new file mode 100644 index 00000000..930861f9 --- /dev/null +++ b/tests/color_test.py @@ -0,0 +1,35 @@ + +import mock +import pytest +import sys + +from pre_commit.color import format_color +from pre_commit.color import GREEN +from pre_commit.color import use_color + + +@pytest.mark.parametrize(('in_text', 'in_color', 'in_use_color', 'expected'), ( + ('foo', GREEN, True, '{0}foo\033[0m'.format(GREEN)), + ('foo', GREEN, False, 'foo'), +)) +def test_format_color(in_text, in_color, in_use_color, expected): + ret = format_color(in_text, in_color, in_use_color) + assert ret == expected + + +def test_use_color_never(): + assert use_color('never') is False + + +def test_use_color_always(): + assert use_color('always') is True + + +def test_use_color_no_tty(): + with mock.patch.object(sys.stdout, 'isatty', return_value=False): + assert use_color('auto') is False + + +def test_use_color_tty(): + with mock.patch.object(sys.stdout, 'isatty', return_value=True): + assert use_color('auto') is True From 8aa88c7363c53675da4e02f38cfebdbe44c06db6 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 5 Apr 2014 19:21:12 -0700 Subject: [PATCH 2/3] Consolidate arguments for run() --- pre_commit/run.py | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/pre_commit/run.py b/pre_commit/run.py index ac520530..20853e0a 100644 --- a/pre_commit/run.py +++ b/pre_commit/run.py @@ -19,8 +19,8 @@ COLS = int(subprocess.Popen(['tput', 'cols'], stdout=subprocess.PIPE).communicat PASS_FAIL_LENGTH = 6 -def _run_single_hook(runner, repository, hook_id, all_files=False, verbose=False): - if all_files: +def _run_single_hook(runner, repository, hook_id, args): + if args.all_files: get_filenames = git.get_all_files_matching else: get_filenames = git.get_staged_files_matching @@ -56,44 +56,39 @@ def _run_single_hook(runner, repository, hook_id, all_files=False, verbose=False print('{0}{1}{2}'.format(color, pass_fail, NORMAL)) - if output and (retcode or verbose): + if output and (retcode or args.verbose): print('\n' + output) return retcode -def run_hooks(runner, all_files=False, verbose=False): +def run_hooks(runner, args): """Actually run the hooks.""" retval = 0 for repo in runner.repositories: for hook_id in repo.hooks: - retval |= _run_single_hook( - runner, - repo, - hook_id, - all_files=all_files, - verbose=verbose, - ) + retval |= _run_single_hook(runner, repo, hook_id, args) return retval -def run_single_hook(runner, hook_id, all_files=False, verbose=False): +def run_single_hook(runner, hook_id, args): for repo in runner.repositories: if hook_id in repo.hooks: - return _run_single_hook( - runner, - repo, - hook_id, - all_files=all_files, - verbose=verbose, - ) + return _run_single_hook(runner, repo, hook_id, args) else: print('No hook with id `{0}`'.format(hook_id)) return 1 +def _run(runner, args): + if args.hook: + return run_single_hook(runner, args.hook, args) + else: + return run_hooks(runner, args) + + @entry def run(argv): parser = argparse.ArgumentParser() @@ -135,17 +130,7 @@ def run(argv): elif args.command == 'autoupdate': return commands.autoupdate(runner) elif args.command == 'run': - if args.hook: - return run_single_hook( - runner, - args.hook, - all_files=args.all_files, - verbose=args.verbose, - ) - else: - return run_hooks( - runner, all_files=args.all_files, verbose=args.verbose, - ) + return _run(runner, args) elif args.command == 'help': if args.help_cmd: parser.parse_args([args.help_cmd, '--help']) From 73e0111e383945abfa4251e84549a6cc11651a7e Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 5 Apr 2014 19:31:31 -0700 Subject: [PATCH 3/3] Wire in color for pre-commit. Closes #63. --- pre_commit/color.py | 6 ++++++ pre_commit/run.py | 14 ++++++++------ tests/color_test.py | 6 ++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pre_commit/color.py b/pre_commit/color.py index d3875c39..641e719e 100644 --- a/pre_commit/color.py +++ b/pre_commit/color.py @@ -6,6 +6,9 @@ GREEN = '\033[42m' NORMAL = '\033[0m' +class InvalidColorSetting(ValueError): pass + + def format_color(text, color, use_color): """Format text with color. @@ -26,6 +29,9 @@ def use_color(setting): Args: setting - Either `auto`, `always`, or `never` """ + if setting not in ('auto', 'always', 'never'): + raise InvalidColorSetting(setting) + return ( setting == 'always' or (setting == 'auto' and sys.stdout.isatty()) diff --git a/pre_commit/run.py b/pre_commit/run.py index 20853e0a..7f1cb6b5 100644 --- a/pre_commit/run.py +++ b/pre_commit/run.py @@ -5,15 +5,13 @@ import argparse import subprocess import sys +from pre_commit import color from pre_commit import commands from pre_commit import git from pre_commit.runner import Runner from pre_commit.util import entry -RED = '\033[41m' -GREEN = '\033[42m' -NORMAL = '\033[0m' COLS = int(subprocess.Popen(['tput', 'cols'], stdout=subprocess.PIPE).communicate()[0]) PASS_FAIL_LENGTH = 6 @@ -46,15 +44,15 @@ def _run_single_hook(runner, repository, hook_id, args): output = '\n'.join([stdout, stderr]).strip() if retcode != repository.hooks[hook_id]['expected_return_value']: retcode = 1 - color = RED + print_color = color.RED pass_fail = 'Failed' else: retcode = 0 - color = GREEN + print_color = color.GREEN pass_fail = 'Passed' - print('{0}{1}{2}'.format(color, pass_fail, NORMAL)) + print(color.format_color(pass_fail, print_color, args.color)) if output and (retcode or args.verbose): print('\n' + output) @@ -110,6 +108,10 @@ def run(argv): help='Run on all the files in the repo.', ) run.add_argument('--verbose', '-v', action='store_true', default=False) + run.add_argument( + '--color', default='auto', type=color.use_color, + help='Whether to use color in output. Defaults to `auto`', + ) help = subparsers.add_parser('help', help='Show help for a specific command.') help.add_argument('help_cmd', nargs='?', help='Command to show help for.') diff --git a/tests/color_test.py b/tests/color_test.py index 930861f9..44d9b170 100644 --- a/tests/color_test.py +++ b/tests/color_test.py @@ -5,6 +5,7 @@ import sys from pre_commit.color import format_color from pre_commit.color import GREEN +from pre_commit.color import InvalidColorSetting from pre_commit.color import use_color @@ -33,3 +34,8 @@ def test_use_color_no_tty(): def test_use_color_tty(): with mock.patch.object(sys.stdout, 'isatty', return_value=True): assert use_color('auto') is True + + +def test_use_color_raises_if_given_shenanigans(): + with pytest.raises(InvalidColorSetting): + use_color('herpaderp')