fix: add ability to provide a file with excludes

This commit is contained in:
Cesar Sanchez 2023-11-08 17:08:08 -05:00
parent 14169eb31d
commit 4d485507f3
No known key found for this signature in database
4 changed files with 35 additions and 2 deletions

View file

@ -110,6 +110,7 @@ MANIFEST_HOOK_DICT = cfgv.Map(
cfgv.Optional('files', check_string_regex, ''), cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'), cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('exclude_file_path', check_string_regex, '^$'),
cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']), cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']),
cfgv.Optional('types_or', cfgv.check_array(check_type_tag), []), cfgv.Optional('types_or', cfgv.check_array(check_type_tag), []),
cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []), cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []),
@ -357,6 +358,7 @@ CONFIG_SCHEMA = cfgv.Map(
StagesMigration('default_stages', STAGES), StagesMigration('default_stages', STAGES),
cfgv.Optional('files', check_string_regex, ''), cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'), cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('exclude_file_path', check_string_regex, '^$'),
cfgv.Optional('fail_fast', cfgv.check_bool, False), cfgv.Optional('fail_fast', cfgv.check_bool, False),
cfgv.Optional( cfgv.Optional(
'minimum_pre_commit_version', 'minimum_pre_commit_version',

View file

@ -63,6 +63,7 @@ def filter_by_include_exclude(
exclude: str, exclude: str,
) -> Generator[str, None, None]: ) -> Generator[str, None, None]:
include_re, exclude_re = re.compile(include), re.compile(exclude) include_re, exclude_re = re.compile(include), re.compile(exclude)
return ( return (
filename for filename in names filename for filename in names
if include_re.search(filename) if include_re.search(filename)
@ -98,11 +99,23 @@ class Classifier:
yield filename yield filename
def filenames_for_hook(self, hook: Hook) -> Generator[str, None, None]: def filenames_for_hook(self, hook: Hook) -> Generator[str, None, None]:
exclude = hook.exclude or '$^'
file_exclude = ''
if hook.exclude_file_path is not None:
if os.path.isfile(hook.exclude_file_path):
with open(hook.exclude_file_path) as exclude_file:
file_exclude = '|'.join(
line.strip() for line in exclude_file.readlines()
) or '$^'
if file_exclude:
exclude = f'{exclude}|{file_exclude}'
return self.by_types( return self.by_types(
filter_by_include_exclude( filter_by_include_exclude(
self.filenames, self.filenames,
hook.files, hook.files,
hook.exclude, exclude,
), ),
hook.types, hook.types,
hook.types_or, hook.types_or,
@ -115,6 +128,7 @@ class Classifier:
filenames: Iterable[str], filenames: Iterable[str],
include: str, include: str,
exclude: str, exclude: str,
exclude_file_path: str,
) -> Classifier: ) -> Classifier:
# on windows we normalize all filenames to use forward slashes # on windows we normalize all filenames to use forward slashes
# this makes it easier to filter using the `files:` regex # this makes it easier to filter using the `files:` regex
@ -122,6 +136,15 @@ class Classifier:
# see #1173 # see #1173
if os.altsep == '/' and os.sep == '\\': if os.altsep == '/' and os.sep == '\\':
filenames = (f.replace(os.sep, os.altsep) for f in filenames) filenames = (f.replace(os.sep, os.altsep) for f in filenames)
file_exclude = ''
if exclude_file_path is not None:
if os.path.isfile(exclude_file_path):
with open(exclude_file_path) as exclude_file:
file_exclude = '|'.join(
line.strip() for line in exclude_file.readlines()
) or '$^'
if file_exclude:
exclude = f'{exclude}|{file_exclude}'
filenames = filter_by_include_exclude(filenames, include, exclude) filenames = filter_by_include_exclude(filenames, include, exclude)
return Classifier(filenames) return Classifier(filenames)
@ -288,7 +311,7 @@ def _run_hooks(
"""Actually run the hooks.""" """Actually run the hooks."""
cols = _compute_cols(hooks) cols = _compute_cols(hooks)
classifier = Classifier.from_config( classifier = Classifier.from_config(
_all_filenames(args), config['files'], config['exclude'], _all_filenames(args), config['files'], config['exclude'], config['exclude_file_path'],
) )
retval = 0 retval = 0
prior_diff = _get_diff() prior_diff = _get_diff()

View file

@ -20,6 +20,7 @@ class Hook(NamedTuple):
alias: str alias: str
files: str files: str
exclude: str exclude: str
exclude_file_path: str
types: Sequence[str] types: Sequence[str]
types_or: Sequence[str] types_or: Sequence[str]
exclude_types: Sequence[str] exclude_types: Sequence[str]

View file

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import os
from collections.abc import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
@ -13,6 +14,12 @@ from pre_commit.store import Store
def check_all_hooks_match_files(config_file: str) -> int: def check_all_hooks_match_files(config_file: str) -> int:
config = load_config(config_file) config = load_config(config_file)
exclude_file_path = config['exclude_file_path']
if exclude_file_path is not None:
if os.path.isfile(exclude_file_path):
with open(exclude_file_path, 'r') as f:
config['exclude'] = f.read().splitlines()
classifier = Classifier.from_config( classifier = Classifier.from_config(
git.get_all_files(), config['files'], config['exclude'], git.get_all_files(), config['files'], config['exclude'],
) )