From 4466b9cd66757ab7de68b5ffd11a8b3d490a342c Mon Sep 17 00:00:00 2001 From: Paul Hooijenga Date: Fri, 20 Oct 2017 15:18:22 +0200 Subject: [PATCH] Warn when exclude pattern is not matched --- pre_commit/commands/run.py | 32 +++++++++++++++------- tests/commands/run_test.py | 54 ++++++++++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 74bff891..816dd5d5 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -38,14 +38,15 @@ def _hook_msg_start(hook, verbose): def _filter_by_include_exclude(filenames, include, exclude): include_re, exclude_re = re.compile(include), re.compile(exclude) - return { - filename for filename in filenames - if ( - include_re.search(filename) and - not exclude_re.search(filename) and - os.path.lexists(filename) - ) - } + filtered = set() + exclude_matched = False + for filename in filenames: + if include_re.search(filename): + if exclude_re.search(filename): + exclude_matched = True + elif os.path.lexists(filename): + filtered.add(filename) + return filtered, exclude_matched def _filter_by_types(filenames, types, exclude_types): @@ -64,7 +65,13 @@ NO_FILES = '(no files to check)' def _run_single_hook(filenames, hook, repo, args, skips, cols): include, exclude = hook['files'], hook['exclude'] - filenames = _filter_by_include_exclude(filenames, include, exclude) + filenames, excluded = _filter_by_include_exclude( + filenames, include, exclude, + ) + if args.all_files and exclude != '^$' and not excluded: + logger.warning('The exclude pattern for {} did not match any files' + .format(_hook_msg_start(hook, args.verbose))) + types, exclude_types = hook['types'], hook['exclude_types'] filenames = _filter_by_types(filenames, types, exclude_types) if hook['id'] in skips: @@ -181,7 +188,12 @@ def _run_hooks(config, repo_hooks, args, environ): skips = _get_skips(environ) cols = _compute_cols([hook for _, hook in repo_hooks], args.verbose) filenames = _all_filenames(args) - filenames = _filter_by_include_exclude(filenames, '', config['exclude']) + filenames, excluded = _filter_by_include_exclude( + filenames, '', config['exclude'], + ) + if args.all_files and config['exclude'] != '^$' and not excluded: + logger.warning('The global exclude pattern did not match any files') + retval = 0 for repo, hook in repo_hooks: retval |= _run_single_hook(filenames, hook, repo, args, skips, cols) diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index d6812ae5..299270e4 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -170,6 +170,34 @@ def test_global_exclude(cap_out, tempdir_factory, mock_out_store_directory): assert printed.endswith(expected) +def test_global_exclude_no_match( + cap_out, tempdir_factory, mock_out_store_directory, +): + git_path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') + with cwd(git_path): + with modify_config() as config: + config['exclude'] = '^test$' + ret, printed = _do_run(cap_out, git_path, run_opts(all_files=True)) + assert ret == 0 + expected = b'The global exclude pattern did not match any files\n' + assert expected in printed + + +def test_hook_exclude_no_match( + cap_out, tempdir_factory, mock_out_store_directory, +): + git_path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') + with cwd(git_path): + with modify_config() as config: + config['repos'][0]['hooks'][0]['exclude'] = '^test$' + print(config['repos'][0]['hooks']) + ret, printed = _do_run(cap_out, git_path, run_opts(all_files=True)) + assert ret == 0 + expected = b'The exclude pattern for Bash hook ' \ + b'did not match any files\n' + assert expected in printed + + def test_show_diff_on_failure( capfd, cap_out, tempdir_factory, mock_out_store_directory, ): @@ -746,7 +774,7 @@ def some_filenames(): def test_include_exclude_base_case(some_filenames): - ret = _filter_by_include_exclude(some_filenames, '', '^$') + ret, _ = _filter_by_include_exclude(some_filenames, '', '^$') assert ret == { '.pre-commit-hooks.yaml', 'pre_commit/main.py', @@ -758,20 +786,36 @@ def test_include_exclude_base_case(some_filenames): def test_matches_broken_symlink(tmpdir): # pramga: no cover (non-windows) with tmpdir.as_cwd(): os.symlink('does-not-exist', 'link') - ret = _filter_by_include_exclude({'link'}, '', '^$') + ret, _ = _filter_by_include_exclude({'link'}, '', '^$') assert ret == {'link'} def test_include_exclude_total_match(some_filenames): - ret = _filter_by_include_exclude(some_filenames, r'^.*\.py$', '^$') + ret, _ = _filter_by_include_exclude(some_filenames, r'^.*\.py$', '^$') assert ret == {'pre_commit/main.py', 'pre_commit/git.py'} def test_include_exclude_does_search_instead_of_match(some_filenames): - ret = _filter_by_include_exclude(some_filenames, r'\.yaml$', '^$') + ret, _ = _filter_by_include_exclude(some_filenames, r'\.yaml$', '^$') assert ret == {'.pre-commit-hooks.yaml'} def test_include_exclude_exclude_removes_files(some_filenames): - ret = _filter_by_include_exclude(some_filenames, '', r'\.py$') + ret, _ = _filter_by_include_exclude(some_filenames, '', r'\.py$') assert ret == {'.pre-commit-hooks.yaml'} + + +def test_include_exclude_exclude_matches(some_filenames): + ret, matched = _filter_by_include_exclude(some_filenames, '', r'\.py$') + assert ret == {'.pre-commit-hooks.yaml'} + assert matched is True + + +def test_include_exclude_exclude_no_matches(some_filenames): + ret, matched = _filter_by_include_exclude(some_filenames, '', r'^$') + assert ret == { + '.pre-commit-hooks.yaml', + 'pre_commit/main.py', + 'pre_commit/git.py', + } + assert matched is False