Merge pull request #151 from pre-commit/dont_dict_pre_commit_config

Allow multiple hooks with same id in .pre-commit-config.yaml
This commit is contained in:
Anthony Sottile 2014-08-01 08:22:23 -07:00
commit ed86307dc4
6 changed files with 43 additions and 23 deletions

View file

@ -7,6 +7,8 @@ omit =
/usr/* /usr/*
*/tmp* */tmp*
setup.py setup.py
# Don't complain if non-runnable code isn't run
*/__main__.py
[report] [report]
exclude_lines = exclude_lines =

View file

@ -43,7 +43,7 @@ def _update_repository(repo_config, runner):
new_repo = Repository.create(new_config, runner.store) new_repo = Repository.create(new_config, runner.store)
# See if any of our hooks were deleted with the new commits # See if any of our hooks were deleted with the new commits
hooks = set(repo.hooks.keys()) hooks = set(hook_id for hook_id, _ in repo.hooks)
hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks.keys())) hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks.keys()))
if hooks_missing: if hooks_missing:
raise RepositoryCannotBeUpdatedError( raise RepositoryCannotBeUpdatedError(

View file

@ -47,7 +47,7 @@ def _print_user_skipped(hook, write, args):
)) ))
def _run_single_hook(runner, repository, hook_id, args, write, skips=set()): def _run_single_hook(runner, repository, hook, args, write, skips=set()):
if args.all_files: if args.all_files:
get_filenames = git.get_all_files_matching get_filenames = git.get_all_files_matching
elif git.is_in_merge_conflict(): elif git.is_in_merge_conflict():
@ -55,10 +55,8 @@ def _run_single_hook(runner, repository, hook_id, args, write, skips=set()):
else: else:
get_filenames = git.get_staged_files_matching get_filenames = git.get_staged_files_matching
hook = repository.hooks[hook_id]
filenames = get_filenames(hook['files'], hook['exclude']) filenames = get_filenames(hook['files'], hook['exclude'])
if hook_id in skips: if hook['id'] in skips:
_print_user_skipped(hook, write, args) _print_user_skipped(hook, write, args)
return 0 return 0
elif not filenames: elif not filenames:
@ -70,9 +68,9 @@ def _run_single_hook(runner, repository, hook_id, args, write, skips=set()):
write(get_hook_message(_hook_msg_start(hook, args.verbose), end_len=6)) write(get_hook_message(_hook_msg_start(hook, args.verbose), end_len=6))
sys.stdout.flush() sys.stdout.flush()
retcode, stdout, stderr = repository.run_hook(hook_id, filenames) retcode, stdout, stderr = repository.run_hook(hook, filenames)
if retcode != repository.hooks[hook_id]['expected_return_value']: if retcode != hook['expected_return_value']:
retcode = 1 retcode = 1
print_color = color.RED print_color = color.RED
pass_fail = 'Failed' pass_fail = 'Failed'
@ -101,9 +99,9 @@ def _run_hooks(runner, args, write, environ):
skips = _get_skips(environ) skips = _get_skips(environ)
for repo in runner.repositories: for repo in runner.repositories:
for hook_id in repo.hooks: for _, hook in repo.hooks:
retval |= _run_single_hook( retval |= _run_single_hook(
runner, repo, hook_id, args, write, skips=skips, runner, repo, hook, args, write, skips=skips,
) )
return retval return retval
@ -112,8 +110,11 @@ def _run_hooks(runner, args, write, environ):
def _run_hook(runner, args, write): def _run_hook(runner, args, write):
hook_id = args.hook hook_id = args.hook
for repo in runner.repositories: for repo in runner.repositories:
if hook_id in repo.hooks: for hook_id_in_repo, hook in repo.hooks:
return _run_single_hook(runner, repo, hook_id, args, write=write) if hook_id == hook_id_in_repo:
return _run_single_hook(
runner, repo, hook, args, write=write,
)
else: else:
write('No hook with id `{0}`\n'.format(hook_id)) write('No hook with id `{0}`\n'.format(hook_id))
return 1 return 1

View file

@ -4,7 +4,6 @@ from cached_property import cached_property
from pre_commit.languages.all import languages from pre_commit.languages.all import languages
from pre_commit.manifest import Manifest from pre_commit.manifest import Manifest
from pre_commit.ordereddict import OrderedDict
from pre_commit.prefixed_command_runner import PrefixedCommandRunner from pre_commit.prefixed_command_runner import PrefixedCommandRunner
@ -33,13 +32,13 @@ class Repository(object):
def languages(self): def languages(self):
return set( return set(
(hook['language'], hook['language_version']) (hook['language'], hook['language_version'])
for hook in self.hooks.values() for _, hook in self.hooks
) )
@cached_property @cached_property
def hooks(self): def hooks(self):
# TODO: merging in manifest dicts is a smell imo # TODO: merging in manifest dicts is a smell imo
return OrderedDict( return tuple(
(hook['id'], dict(self.manifest.hooks[hook['id']], **hook)) (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))
for hook in self.repo_config['hooks'] for hook in self.repo_config['hooks']
) )
@ -71,15 +70,14 @@ class Repository(object):
continue continue
language.install_environment(self.cmd_runner, language_version) language.install_environment(self.cmd_runner, language_version)
def run_hook(self, hook_id, file_args): def run_hook(self, hook, file_args):
"""Run a hook. """Run a hook.
Args: Args:
hook_id - Id of the hook hook - Hook dictionary
file_args - List of files to run file_args - List of files to run
""" """
self.require_installed() self.require_installed()
hook = self.hooks[hook_id]
return languages[hook['language']].run_hook( return languages[hook['language']].run_hook(
self.cmd_runner, hook, file_args, self.cmd_runner, hook, file_args,
) )

View file

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import io
import mock import mock
import os import os
import os.path import os.path
@ -205,7 +206,22 @@ def test_hook_id_not_in_non_verbose_output(
def test_hook_id_in_verbose_output( def test_hook_id_in_verbose_output(
repo_with_passing_hook, mock_out_store_directory repo_with_passing_hook, mock_out_store_directory,
): ):
ret, printed = _do_run(repo_with_passing_hook, _get_opts(verbose=True)) ret, printed = _do_run(repo_with_passing_hook, _get_opts(verbose=True))
assert '[bash_hook] Bash hook' in printed assert '[bash_hook] Bash hook' in printed
def test_multiple_hooks_same_id(
repo_with_passing_hook, mock_out_store_directory,
):
with local.cwd(repo_with_passing_hook):
# Add bash hook on there again
with io.open('.pre-commit-config.yaml', 'a+') as config_file:
config_file.write(' - id: bash_hook\n')
local['git']('add', '.pre-commit-config.yaml')
stage_a_file()
ret, output = _do_run(repo_with_passing_hook, _get_opts())
assert ret == 0
assert output.count('Bash hook') == 2

View file

@ -29,7 +29,10 @@ def _test_hook_repo(
path = make_repo(tmpdir_factory, repo_path) path = make_repo(tmpdir_factory, repo_path)
config = make_config_from_repo(path) config = make_config_from_repo(path)
repo = Repository.create(config, store) repo = Repository.create(config, store)
ret = repo.run_hook(hook_id, args) hook_dict = [
hook for repo_hook_id, hook in repo.hooks if repo_hook_id == hook_id
][0]
ret = repo.run_hook(hook_dict, args)
assert ret[0] == expected_return_code assert ret[0] == expected_return_code
assert ret[1] == expected assert ret[1] == expected
@ -261,11 +264,11 @@ def test_config_overrides_repo_specifics(tmpdir_factory, store):
config = make_config_from_repo(path) config = make_config_from_repo(path)
repo = Repository.create(config, store) repo = Repository.create(config, store)
assert repo.hooks['bash_hook']['files'] == '' assert repo.hooks[0][1]['files'] == ''
# Set the file regex to something else # Set the file regex to something else
config['hooks'][0]['files'] = '\\.sh$' config['hooks'][0]['files'] = '\\.sh$'
repo = Repository.create(config, store) repo = Repository.create(config, store)
assert repo.hooks['bash_hook']['files'] == '\\.sh$' assert repo.hooks[0][1]['files'] == '\\.sh$'
def _create_repo_with_tags(tmpdir_factory, src, tag): def _create_repo_with_tags(tmpdir_factory, src, tag):
@ -286,13 +289,13 @@ def test_tags_on_repositories(in_tmpdir, tmpdir_factory, store):
repo_1 = Repository.create( repo_1 = Repository.create(
make_config_from_repo(git_dir_1, sha=tag), store, make_config_from_repo(git_dir_1, sha=tag), store,
) )
ret = repo_1.run_hook('prints_cwd', ['-L']) ret = repo_1.run_hook(repo_1.hooks[0][1], ['-L'])
assert ret[0] == 0 assert ret[0] == 0
assert ret[1].strip() == in_tmpdir assert ret[1].strip() == in_tmpdir
repo_2 = Repository.create( repo_2 = Repository.create(
make_config_from_repo(git_dir_2, sha=tag), store, make_config_from_repo(git_dir_2, sha=tag), store,
) )
ret = repo_2.run_hook('bash_hook', ['bar']) ret = repo_2.run_hook(repo_2.hooks[0][1], ['bar'])
assert ret[0] == 0 assert ret[0] == 0
assert ret[1] == 'bar\nHello World\n' assert ret[1] == 'bar\nHello World\n'