1717from pre_commit .repository import install_hook_envs
1818from pre_commit .staged_files_only import staged_files_only
1919from pre_commit .util import cmd_output
20- from pre_commit .util import memoize_by_cwd
2120from pre_commit .util import noop_context
2221
2322
2423logger = logging .getLogger ('pre_commit' )
2524
2625
27- tags_from_path = memoize_by_cwd (tags_from_path )
26+ def filter_by_include_exclude (names , include , exclude ):
27+ include_re , exclude_re = re .compile (include ), re .compile (exclude )
28+ return [
29+ filename for filename in names
30+ if include_re .search (filename )
31+ if not exclude_re .search (filename )
32+ ]
33+
34+
35+ class Classifier (object ):
36+ def __init__ (self , filenames ):
37+ self .filenames = [f for f in filenames if os .path .lexists (f )]
38+ self ._types_cache = {}
39+
40+ def _types_for_file (self , filename ):
41+ try :
42+ return self ._types_cache [filename ]
43+ except KeyError :
44+ ret = self ._types_cache [filename ] = tags_from_path (filename )
45+ return ret
46+
47+ def by_types (self , names , types , exclude_types ):
48+ types , exclude_types = frozenset (types ), frozenset (exclude_types )
49+ ret = []
50+ for filename in names :
51+ tags = self ._types_for_file (filename )
52+ if tags >= types and not tags & exclude_types :
53+ ret .append (filename )
54+ return ret
55+
56+ def filenames_for_hook (self , hook ):
57+ names = self .filenames
58+ names = filter_by_include_exclude (names , hook .files , hook .exclude )
59+ names = self .by_types (names , hook .types , hook .exclude_types )
60+ return names
2861
2962
3063def _get_skips (environ ):
@@ -36,37 +69,12 @@ def _hook_msg_start(hook, verbose):
3669 return '{}{}' .format ('[{}] ' .format (hook .id ) if verbose else '' , hook .name )
3770
3871
39- def _filter_by_include_exclude (filenames , include , exclude ):
40- include_re , exclude_re = re .compile (include ), re .compile (exclude )
41- return [
42- filename for filename in filenames
43- if (
44- include_re .search (filename ) and
45- not exclude_re .search (filename ) and
46- os .path .lexists (filename )
47- )
48- ]
49-
50-
51- def _filter_by_types (filenames , types , exclude_types ):
52- types , exclude_types = frozenset (types ), frozenset (exclude_types )
53- ret = []
54- for filename in filenames :
55- tags = tags_from_path (filename )
56- if tags >= types and not tags & exclude_types :
57- ret .append (filename )
58- return tuple (ret )
59-
60-
6172SKIPPED = 'Skipped'
6273NO_FILES = '(no files to check)'
6374
6475
65- def _run_single_hook (filenames , hook , args , skips , cols ):
66- include , exclude = hook .files , hook .exclude
67- filenames = _filter_by_include_exclude (filenames , include , exclude )
68- types , exclude_types = hook .types , hook .exclude_types
69- filenames = _filter_by_types (filenames , types , exclude_types )
76+ def _run_single_hook (classifier , hook , args , skips , cols ):
77+ filenames = classifier .filenames_for_hook (hook )
7078
7179 if hook .language == 'pcre' :
7280 logger .warning (
@@ -193,10 +201,11 @@ def _run_hooks(config, hooks, args, environ):
193201 skips = _get_skips (environ )
194202 cols = _compute_cols (hooks , args .verbose )
195203 filenames = _all_filenames (args )
196- filenames = _filter_by_include_exclude (filenames , '' , config ['exclude' ])
204+ filenames = filter_by_include_exclude (filenames , '' , config ['exclude' ])
205+ classifier = Classifier (filenames )
197206 retval = 0
198207 for hook in hooks :
199- retval |= _run_single_hook (filenames , hook , args , skips , cols )
208+ retval |= _run_single_hook (classifier , hook , args , skips , cols )
200209 if retval and config ['fail_fast' ]:
201210 break
202211 if retval and args .show_diff_on_failure and git .has_diff ():
0 commit comments