From 898a3ea1bb7dc2a3a0a65f8b2019408691c557ff Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 8 Sep 2017 13:19:00 -0700 Subject: [PATCH] Implement `fail_fast`. --- pre_commit/clientlib.py | 1 + pre_commit/commands/run.py | 6 ++++-- pre_commit/runner.py | 6 +++++- tests/commands/run_test.py | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py index e69359b0..7fb49d78 100644 --- a/pre_commit/clientlib.py +++ b/pre_commit/clientlib.py @@ -130,6 +130,7 @@ CONFIG_SCHEMA = schema.Map( 'Config', None, schema.RequiredRecurse('repos', schema.Array(CONFIG_REPO_DICT)), + schema.Optional('fail_fast', schema.check_bool, False), ) diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 99232585..505bb54d 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -169,13 +169,15 @@ def _compute_cols(hooks, verbose): return max(cols, 80) -def _run_hooks(repo_hooks, args, environ): +def _run_hooks(config, repo_hooks, args, environ): """Actually run the hooks.""" skips = _get_skips(environ) cols = _compute_cols([hook for _, hook in repo_hooks], args.verbose) retval = 0 for repo, hook in repo_hooks: retval |= _run_single_hook(hook, repo, args, skips, cols) + if retval and config['fail_fast']: + break if ( retval and args.show_diff_on_failure and @@ -251,4 +253,4 @@ def run(runner, args, environ=os.environ): if not hook['stages'] or args.hook_stage in hook['stages'] ] - return _run_hooks(repo_hooks, args, environ) + return _run_hooks(runner.config, repo_hooks, args, environ) diff --git a/pre_commit/runner.py b/pre_commit/runner.py index 346d6021..d853868a 100644 --- a/pre_commit/runner.py +++ b/pre_commit/runner.py @@ -37,10 +37,14 @@ class Runner(object): def config_file_path(self): return os.path.join(self.git_root, self.config_file) + @cached_property + def config(self): + return load_config(self.config_file_path) + @cached_property def repositories(self): """Returns a tuple of the configured repositories.""" - repos = load_config(self.config_file_path)['repos'] + repos = self.config['repos'] repos = tuple(Repository.create(x, self.store) for x in repos) for repo in repos: repo.require_installed() diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 39d3ac0b..53e098b0 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -729,3 +729,18 @@ def test_pass_filenames( ) assert expected_out + b'\nHello World' in printed assert (b'foo.py' in printed) == pass_filenames + + +def test_fail_fast( + cap_out, repo_with_failing_hook, mock_out_store_directory, +): + with cwd(repo_with_failing_hook): + with modify_config() as config: + # More than one hook + config['fail_fast'] = True + config['repos'][0]['hooks'] *= 2 + stage_a_file() + + ret, printed = _do_run(cap_out, repo_with_failing_hook, _get_opts()) + # it should have only run one hook + assert printed.count(b'Failing hook') == 1