diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py index 87679bfa6..0b8582bce 100644 --- a/pre_commit/clientlib.py +++ b/pre_commit/clientlib.py @@ -61,6 +61,7 @@ def _make_argparser(filenames_help: str) -> argparse.ArgumentParser: cfgv.Optional('files', check_string_regex, ''), cfgv.Optional('exclude', check_string_regex, '^$'), cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']), + cfgv.Optional('types_or', cfgv.check_array(check_type_tag), ['file']), cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []), cfgv.Optional( diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 0d335e285..1d6176df7 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -83,20 +83,30 @@ def by_types( self, names: Sequence[str], types: Collection[str], + types_or: Collection[str], exclude_types: Collection[str], ) -> List[str]: - types, exclude_types = frozenset(types), frozenset(exclude_types) + types, types_or, exclude_types = ( + frozenset(types), + frozenset(types_or), + frozenset(exclude_types), + ) ret = [] for filename in names: tags = self._types_for_file(filename) - if tags >= types and not tags & exclude_types: + if tags >= types and not tags & exclude_types and tags & types_or: ret.append(filename) return ret def filenames_for_hook(self, hook: Hook) -> Tuple[str, ...]: names = self.filenames names = filter_by_include_exclude(names, hook.files, hook.exclude) - names = self.by_types(names, hook.types, hook.exclude_types) + names = self.by_types( + names, + hook.types, + hook.types_or, + hook.exclude_types, + ) return tuple(names) @classmethod diff --git a/pre_commit/hook.py b/pre_commit/hook.py index b65ac42b0..ea773942b 100644 --- a/pre_commit/hook.py +++ b/pre_commit/hook.py @@ -22,6 +22,7 @@ class Hook(NamedTuple): files: str exclude: str types: Sequence[str] + types_or: Sequence[str] exclude_types: Sequence[str] additional_dependencies: Sequence[str] args: Sequence[str] diff --git a/pre_commit/meta_hooks/check_useless_excludes.py b/pre_commit/meta_hooks/check_useless_excludes.py index db6865c6c..56d76d674 100644 --- a/pre_commit/meta_hooks/check_useless_excludes.py +++ b/pre_commit/meta_hooks/check_useless_excludes.py @@ -47,8 +47,12 @@ def check_useless_excludes(config_file: str) -> int: # the defaults applied during runtime hook = apply_defaults(hook, MANIFEST_HOOK_DICT) names = classifier.filenames - types, exclude_types = hook['types'], hook['exclude_types'] - names = classifier.by_types(names, types, exclude_types) + types, types_or, exclude_types = ( + hook['types'], + hook['types_or'], + hook['exclude_types'], + ) + names = classifier.by_types(names, types, types_or, exclude_types) include, exclude = hook['files'], hook['exclude'] if not exclude_matches_any(names, include, exclude): print( diff --git a/testing/resources/types_or_repo/.pre-commit-hooks.yaml b/testing/resources/types_or_repo/.pre-commit-hooks.yaml new file mode 100644 index 000000000..a4ea920d6 --- /dev/null +++ b/testing/resources/types_or_repo/.pre-commit-hooks.yaml @@ -0,0 +1,6 @@ +- id: python-cython-files + name: Python and Cython files + entry: bin/hook.sh + language: script + types: [file] + types_or: [python, cython] diff --git a/testing/resources/types_or_repo/bin/hook.sh b/testing/resources/types_or_repo/bin/hook.sh new file mode 100755 index 000000000..bdade5132 --- /dev/null +++ b/testing/resources/types_or_repo/bin/hook.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +echo $@ +exit 1 diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 00b471282..34f3a3753 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -219,6 +219,19 @@ def test_types_hook_repository(cap_out, store, tempdir_factory): assert b'bar.notpy' not in printed +def test_types_or_hook_repository(cap_out, store, tempdir_factory): + git_path = make_consuming_repo(tempdir_factory, 'types_or_repo') + with cwd(git_path): + stage_a_file('bar.notpy') + stage_a_file('bar.pxd') + stage_a_file('bar.py') + ret, printed = _do_run(cap_out, store, git_path, run_opts()) + assert ret == 1 + assert b'bar.notpy' not in printed + assert b'bar.pxd' in printed + assert b'bar.py' in printed + + def test_exclude_types_hook_repository(cap_out, store, tempdir_factory): git_path = make_consuming_repo(tempdir_factory, 'exclude_types_repo') with cwd(git_path): diff --git a/tests/repository_test.py b/tests/repository_test.py index 3d5093df6..8d771458f 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -901,6 +901,7 @@ def test_manifest_hooks(tempdir_factory, store): 'post-commit', 'manual', 'post-checkout', 'push', ), types=['file'], + types_or=['file'], verbose=False, )