pre-commit-ex 4.5.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. pre_commit/__init__.py +0 -0
  2. pre_commit/__main__.py +7 -0
  3. pre_commit/all_languages.py +50 -0
  4. pre_commit/clientlib.py +551 -0
  5. pre_commit/color.py +109 -0
  6. pre_commit/commands/__init__.py +0 -0
  7. pre_commit/commands/autoupdate.py +215 -0
  8. pre_commit/commands/clean.py +16 -0
  9. pre_commit/commands/gc.py +98 -0
  10. pre_commit/commands/hazmat.py +95 -0
  11. pre_commit/commands/hook_impl.py +272 -0
  12. pre_commit/commands/init_templatedir.py +39 -0
  13. pre_commit/commands/install_uninstall.py +167 -0
  14. pre_commit/commands/migrate_config.py +135 -0
  15. pre_commit/commands/run.py +474 -0
  16. pre_commit/commands/sample_config.py +18 -0
  17. pre_commit/commands/try_repo.py +77 -0
  18. pre_commit/commands/validate_config.py +18 -0
  19. pre_commit/commands/validate_manifest.py +18 -0
  20. pre_commit/constants.py +13 -0
  21. pre_commit/envcontext.py +62 -0
  22. pre_commit/error_handler.py +81 -0
  23. pre_commit/errors.py +5 -0
  24. pre_commit/file_lock.py +75 -0
  25. pre_commit/git.py +245 -0
  26. pre_commit/hook.py +60 -0
  27. pre_commit/lang_base.py +196 -0
  28. pre_commit/languages/__init__.py +0 -0
  29. pre_commit/languages/conda.py +77 -0
  30. pre_commit/languages/coursier.py +76 -0
  31. pre_commit/languages/dart.py +97 -0
  32. pre_commit/languages/docker.py +181 -0
  33. pre_commit/languages/docker_image.py +32 -0
  34. pre_commit/languages/dotnet.py +111 -0
  35. pre_commit/languages/fail.py +27 -0
  36. pre_commit/languages/golang.py +161 -0
  37. pre_commit/languages/haskell.py +56 -0
  38. pre_commit/languages/julia.py +133 -0
  39. pre_commit/languages/lua.py +75 -0
  40. pre_commit/languages/node.py +110 -0
  41. pre_commit/languages/perl.py +50 -0
  42. pre_commit/languages/pygrep.py +133 -0
  43. pre_commit/languages/python.py +228 -0
  44. pre_commit/languages/r.py +278 -0
  45. pre_commit/languages/ruby.py +145 -0
  46. pre_commit/languages/rust.py +160 -0
  47. pre_commit/languages/swift.py +50 -0
  48. pre_commit/languages/unsupported.py +10 -0
  49. pre_commit/languages/unsupported_script.py +32 -0
  50. pre_commit/logging_handler.py +42 -0
  51. pre_commit/main.py +472 -0
  52. pre_commit/meta_hooks/__init__.py +0 -0
  53. pre_commit/meta_hooks/check_hooks_apply.py +43 -0
  54. pre_commit/meta_hooks/check_useless_excludes.py +83 -0
  55. pre_commit/meta_hooks/identity.py +17 -0
  56. pre_commit/output.py +33 -0
  57. pre_commit/parse_shebang.py +85 -0
  58. pre_commit/prefix.py +18 -0
  59. pre_commit/repository.py +237 -0
  60. pre_commit/resources/__init__.py +0 -0
  61. pre_commit/resources/empty_template_.npmignore +1 -0
  62. pre_commit/resources/empty_template_Cargo.toml +7 -0
  63. pre_commit/resources/empty_template_LICENSE.renv +7 -0
  64. pre_commit/resources/empty_template_Makefile.PL +6 -0
  65. pre_commit/resources/empty_template_activate.R +440 -0
  66. pre_commit/resources/empty_template_environment.yml +9 -0
  67. pre_commit/resources/empty_template_go.mod +1 -0
  68. pre_commit/resources/empty_template_main.go +3 -0
  69. pre_commit/resources/empty_template_main.rs +1 -0
  70. pre_commit/resources/empty_template_package.json +4 -0
  71. pre_commit/resources/empty_template_pre-commit-package-dev-1.rockspec +12 -0
  72. pre_commit/resources/empty_template_pre_commit_placeholder_package.gemspec +6 -0
  73. pre_commit/resources/empty_template_pubspec.yaml +4 -0
  74. pre_commit/resources/empty_template_renv.lock +20 -0
  75. pre_commit/resources/empty_template_setup.py +4 -0
  76. pre_commit/resources/hook-tmpl +20 -0
  77. pre_commit/resources/rbenv.tar.gz +0 -0
  78. pre_commit/resources/ruby-build.tar.gz +0 -0
  79. pre_commit/resources/ruby-download.tar.gz +0 -0
  80. pre_commit/staged_files_only.py +113 -0
  81. pre_commit/store.py +235 -0
  82. pre_commit/util.py +239 -0
  83. pre_commit/xargs.py +184 -0
  84. pre_commit/yaml.py +19 -0
  85. pre_commit/yaml_rewrite.py +52 -0
  86. pre_commit_ex-4.5.1.dist-info/METADATA +63 -0
  87. pre_commit_ex-4.5.1.dist-info/RECORD +91 -0
  88. pre_commit_ex-4.5.1.dist-info/WHEEL +6 -0
  89. pre_commit_ex-4.5.1.dist-info/entry_points.txt +2 -0
  90. pre_commit_ex-4.5.1.dist-info/licenses/LICENSE +19 -0
  91. pre_commit_ex-4.5.1.dist-info/top_level.txt +1 -0
pre_commit/__init__.py ADDED
File without changes
pre_commit/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from pre_commit.main import main
4
+
5
+
6
+ if __name__ == '__main__':
7
+ raise SystemExit(main())
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from pre_commit.lang_base import Language
4
+ from pre_commit.languages import conda
5
+ from pre_commit.languages import coursier
6
+ from pre_commit.languages import dart
7
+ from pre_commit.languages import docker
8
+ from pre_commit.languages import docker_image
9
+ from pre_commit.languages import dotnet
10
+ from pre_commit.languages import fail
11
+ from pre_commit.languages import golang
12
+ from pre_commit.languages import haskell
13
+ from pre_commit.languages import julia
14
+ from pre_commit.languages import lua
15
+ from pre_commit.languages import node
16
+ from pre_commit.languages import perl
17
+ from pre_commit.languages import pygrep
18
+ from pre_commit.languages import python
19
+ from pre_commit.languages import r
20
+ from pre_commit.languages import ruby
21
+ from pre_commit.languages import rust
22
+ from pre_commit.languages import swift
23
+ from pre_commit.languages import unsupported
24
+ from pre_commit.languages import unsupported_script
25
+
26
+
27
+ languages: dict[str, Language] = {
28
+ 'conda': conda,
29
+ 'coursier': coursier,
30
+ 'dart': dart,
31
+ 'docker': docker,
32
+ 'docker_image': docker_image,
33
+ 'dotnet': dotnet,
34
+ 'fail': fail,
35
+ 'golang': golang,
36
+ 'haskell': haskell,
37
+ 'julia': julia,
38
+ 'lua': lua,
39
+ 'node': node,
40
+ 'perl': perl,
41
+ 'pygrep': pygrep,
42
+ 'python': python,
43
+ 'r': r,
44
+ 'ruby': ruby,
45
+ 'rust': rust,
46
+ 'swift': swift,
47
+ 'unsupported': unsupported,
48
+ 'unsupported_script': unsupported_script,
49
+ }
50
+ language_names = sorted(languages)
@@ -0,0 +1,551 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+ import logging
5
+ import os.path
6
+ import re
7
+ import shlex
8
+ import sys
9
+ from collections.abc import Callable
10
+ from collections.abc import Sequence
11
+ from typing import Any
12
+ from typing import NamedTuple
13
+
14
+ import cfgv
15
+ from identify.identify import ALL_TAGS
16
+
17
+ import pre_commit.constants as C
18
+ from pre_commit.all_languages import language_names
19
+ from pre_commit.errors import FatalError
20
+ from pre_commit.yaml import yaml_load
21
+
22
+ logger = logging.getLogger('pre_commit')
23
+
24
+ check_string_regex = cfgv.check_and(cfgv.check_string, cfgv.check_regex)
25
+
26
+ HOOK_TYPES = (
27
+ 'commit-msg',
28
+ 'post-checkout',
29
+ 'post-commit',
30
+ 'post-merge',
31
+ 'post-rewrite',
32
+ 'pre-commit',
33
+ 'pre-merge-commit',
34
+ 'pre-push',
35
+ 'pre-rebase',
36
+ 'prepare-commit-msg',
37
+ )
38
+ # `manual` is not invoked by any installed git hook. See #719
39
+ STAGES = (*HOOK_TYPES, 'manual')
40
+
41
+
42
+ def check_type_tag(tag: str) -> None:
43
+ if tag not in ALL_TAGS:
44
+ raise cfgv.ValidationError(
45
+ f'Type tag {tag!r} is not recognized. '
46
+ f'Try upgrading identify and pre-commit?',
47
+ )
48
+
49
+
50
+ def parse_version(s: str) -> tuple[int, ...]:
51
+ """poor man's version comparison"""
52
+ return tuple(int(p) for p in s.split('.'))
53
+
54
+
55
+ def check_min_version(version: str) -> None:
56
+ if parse_version(version) > parse_version(C.VERSION):
57
+ raise cfgv.ValidationError(
58
+ f'pre-commit version {version} is required but version '
59
+ f'{C.VERSION} is installed. '
60
+ f'Perhaps run `pip install --upgrade pre-commit`.',
61
+ )
62
+
63
+
64
+ _STAGES = {
65
+ 'commit': 'pre-commit',
66
+ 'merge-commit': 'pre-merge-commit',
67
+ 'push': 'pre-push',
68
+ }
69
+
70
+
71
+ def transform_stage(stage: str) -> str:
72
+ return _STAGES.get(stage, stage)
73
+
74
+
75
+ MINIMAL_MANIFEST_SCHEMA = cfgv.Array(
76
+ cfgv.Map(
77
+ 'Hook', 'id',
78
+ cfgv.Required('id', cfgv.check_string),
79
+ cfgv.Optional('stages', cfgv.check_array(cfgv.check_string), []),
80
+ ),
81
+ )
82
+
83
+
84
+ def warn_for_stages_on_repo_init(repo: str, directory: str) -> None:
85
+ try:
86
+ manifest = cfgv.load_from_filename(
87
+ os.path.join(directory, C.MANIFEST_FILE),
88
+ schema=MINIMAL_MANIFEST_SCHEMA,
89
+ load_strategy=yaml_load,
90
+ exc_tp=InvalidManifestError,
91
+ )
92
+ except InvalidManifestError:
93
+ return # they'll get a better error message when it actually loads!
94
+
95
+ legacy_stages = {} # sorted set
96
+ for hook in manifest:
97
+ for stage in hook.get('stages', ()):
98
+ if stage in _STAGES:
99
+ legacy_stages[stage] = True
100
+
101
+ if legacy_stages:
102
+ logger.warning(
103
+ f'repo `{repo}` uses deprecated stage names '
104
+ f'({", ".join(legacy_stages)}) which will be removed in a '
105
+ f'future version. '
106
+ f'Hint: often `pre-commit autoupdate --repo {shlex.quote(repo)}` '
107
+ f'will fix this. '
108
+ f'if it does not -- consider reporting an issue to that repo.',
109
+ )
110
+
111
+
112
+ class StagesMigrationNoDefault(NamedTuple):
113
+ key: str
114
+ default: Sequence[str]
115
+
116
+ def check(self, dct: dict[str, Any]) -> None:
117
+ if self.key not in dct:
118
+ return
119
+
120
+ with cfgv.validate_context(f'At key: {self.key}'):
121
+ val = dct[self.key]
122
+ cfgv.check_array(cfgv.check_any)(val)
123
+
124
+ val = [transform_stage(v) for v in val]
125
+ cfgv.check_array(cfgv.check_one_of(STAGES))(val)
126
+
127
+ def apply_default(self, dct: dict[str, Any]) -> None:
128
+ if self.key not in dct:
129
+ return
130
+ dct[self.key] = [transform_stage(v) for v in dct[self.key]]
131
+
132
+ def remove_default(self, dct: dict[str, Any]) -> None:
133
+ raise NotImplementedError
134
+
135
+
136
+ class StagesMigration(StagesMigrationNoDefault):
137
+ def apply_default(self, dct: dict[str, Any]) -> None:
138
+ dct.setdefault(self.key, self.default)
139
+ super().apply_default(dct)
140
+
141
+
142
+ class DeprecatedStagesWarning(NamedTuple):
143
+ key: str
144
+
145
+ def check(self, dct: dict[str, Any]) -> None:
146
+ if self.key not in dct:
147
+ return
148
+
149
+ val = dct[self.key]
150
+ cfgv.check_array(cfgv.check_any)(val)
151
+
152
+ legacy_stages = [stage for stage in val if stage in _STAGES]
153
+ if legacy_stages:
154
+ logger.warning(
155
+ f'hook id `{dct["id"]}` uses deprecated stage names '
156
+ f'({", ".join(legacy_stages)}) which will be removed in a '
157
+ f'future version. '
158
+ f'run: `pre-commit migrate-config` to automatically fix this.',
159
+ )
160
+
161
+ def apply_default(self, dct: dict[str, Any]) -> None:
162
+ pass
163
+
164
+ def remove_default(self, dct: dict[str, Any]) -> None:
165
+ raise NotImplementedError
166
+
167
+
168
+ class DeprecatedDefaultStagesWarning(NamedTuple):
169
+ key: str
170
+
171
+ def check(self, dct: dict[str, Any]) -> None:
172
+ if self.key not in dct:
173
+ return
174
+
175
+ val = dct[self.key]
176
+ cfgv.check_array(cfgv.check_any)(val)
177
+
178
+ legacy_stages = [stage for stage in val if stage in _STAGES]
179
+ if legacy_stages:
180
+ logger.warning(
181
+ f'top-level `default_stages` uses deprecated stage names '
182
+ f'({", ".join(legacy_stages)}) which will be removed in a '
183
+ f'future version. '
184
+ f'run: `pre-commit migrate-config` to automatically fix this.',
185
+ )
186
+
187
+ def apply_default(self, dct: dict[str, Any]) -> None:
188
+ pass
189
+
190
+ def remove_default(self, dct: dict[str, Any]) -> None:
191
+ raise NotImplementedError
192
+
193
+
194
+ def _translate_language(name: str) -> str:
195
+ return {
196
+ 'system': 'unsupported',
197
+ 'script': 'unsupported_script',
198
+ }.get(name, name)
199
+
200
+
201
+ class LanguageMigration(NamedTuple): # remove
202
+ key: str
203
+ check_fn: Callable[[object], None]
204
+
205
+ def check(self, dct: dict[str, Any]) -> None:
206
+ if self.key not in dct:
207
+ return
208
+
209
+ with cfgv.validate_context(f'At key: {self.key}'):
210
+ self.check_fn(_translate_language(dct[self.key]))
211
+
212
+ def apply_default(self, dct: dict[str, Any]) -> None:
213
+ if self.key not in dct:
214
+ return
215
+
216
+ dct[self.key] = _translate_language(dct[self.key])
217
+
218
+ def remove_default(self, dct: dict[str, Any]) -> None:
219
+ raise NotImplementedError
220
+
221
+
222
+ class LanguageMigrationRequired(LanguageMigration): # replace with Required
223
+ def check(self, dct: dict[str, Any]) -> None:
224
+ if self.key not in dct:
225
+ raise cfgv.ValidationError(f'Missing required key: {self.key}')
226
+
227
+ super().check(dct)
228
+
229
+
230
+ MANIFEST_HOOK_DICT = cfgv.Map(
231
+ 'Hook', 'id',
232
+
233
+ # check first in case it uses some newer, incompatible feature
234
+ cfgv.Optional(
235
+ 'minimum_pre_commit_version',
236
+ cfgv.check_and(cfgv.check_string, check_min_version),
237
+ '0',
238
+ ),
239
+
240
+ cfgv.Required('id', cfgv.check_string),
241
+ cfgv.Required('name', cfgv.check_string),
242
+ cfgv.Required('entry', cfgv.check_string),
243
+ LanguageMigrationRequired('language', cfgv.check_one_of(language_names)),
244
+ cfgv.Optional('alias', cfgv.check_string, ''),
245
+
246
+ cfgv.Optional('files', check_string_regex, ''),
247
+ cfgv.Optional('exclude', check_string_regex, '^$'),
248
+ cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']),
249
+ cfgv.Optional('types_or', cfgv.check_array(check_type_tag), []),
250
+ cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []),
251
+
252
+ cfgv.Optional(
253
+ 'additional_dependencies', cfgv.check_array(cfgv.check_string), [],
254
+ ),
255
+ cfgv.Optional('args', cfgv.check_array(cfgv.check_string), []),
256
+ cfgv.Optional('always_run', cfgv.check_bool, False),
257
+ cfgv.Optional('fail_fast', cfgv.check_bool, False),
258
+ cfgv.Optional('pass_filenames', cfgv.check_bool, True),
259
+ cfgv.Optional('description', cfgv.check_string, ''),
260
+ cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
261
+ cfgv.Optional('log_file', cfgv.check_string, ''),
262
+ cfgv.Optional('require_serial', cfgv.check_bool, False),
263
+ StagesMigration('stages', []),
264
+ cfgv.Optional('verbose', cfgv.check_bool, False),
265
+ )
266
+ MANIFEST_SCHEMA = cfgv.Array(MANIFEST_HOOK_DICT)
267
+
268
+
269
+ class InvalidManifestError(FatalError):
270
+ pass
271
+
272
+
273
+ def _load_manifest_forward_compat(contents: str) -> object:
274
+ obj = yaml_load(contents)
275
+ if isinstance(obj, dict):
276
+ check_min_version('5')
277
+ raise AssertionError('unreachable')
278
+ else:
279
+ return obj
280
+
281
+
282
+ load_manifest = functools.partial(
283
+ cfgv.load_from_filename,
284
+ schema=MANIFEST_SCHEMA,
285
+ load_strategy=_load_manifest_forward_compat,
286
+ exc_tp=InvalidManifestError,
287
+ )
288
+
289
+
290
+ LOCAL = 'local'
291
+ META = 'meta'
292
+
293
+
294
+ class WarnMutableRev(cfgv.Conditional):
295
+ def check(self, dct: dict[str, Any]) -> None:
296
+ super().check(dct)
297
+
298
+ if self.key in dct:
299
+ rev = dct[self.key]
300
+
301
+ if '.' not in rev and not re.match(r'^[a-fA-F0-9]+$', rev):
302
+ logger.warning(
303
+ f'The {self.key!r} field of repo {dct["repo"]!r} '
304
+ f'appears to be a mutable reference '
305
+ f'(moving tag / branch). Mutable references are never '
306
+ f'updated after first install and are not supported. '
307
+ f'See https://pre-commit.com/#using-the-latest-version-for-a-repository ' # noqa: E501
308
+ f'for more details. '
309
+ f'Hint: `pre-commit autoupdate` often fixes this.',
310
+ )
311
+
312
+
313
+ class OptionalSensibleRegexAtHook(cfgv.OptionalNoDefault):
314
+ def check(self, dct: dict[str, Any]) -> None:
315
+ super().check(dct)
316
+
317
+ if '/*' in dct.get(self.key, ''):
318
+ logger.warning(
319
+ f'The {self.key!r} field in hook {dct.get("id")!r} is a '
320
+ f"regex, not a glob -- matching '/*' probably isn't what you "
321
+ f'want here',
322
+ )
323
+ for fwd_slash_re in (r'[\\/]', r'[\/]', r'[/\\]'):
324
+ if fwd_slash_re in dct.get(self.key, ''):
325
+ logger.warning(
326
+ fr'pre-commit normalizes slashes in the {self.key!r} '
327
+ fr'field in hook {dct.get("id")!r} to forward slashes, '
328
+ fr'so you can use / instead of {fwd_slash_re}',
329
+ )
330
+
331
+
332
+ class OptionalSensibleRegexAtTop(cfgv.OptionalNoDefault):
333
+ def check(self, dct: dict[str, Any]) -> None:
334
+ super().check(dct)
335
+
336
+ if '/*' in dct.get(self.key, ''):
337
+ logger.warning(
338
+ f'The top-level {self.key!r} field is a regex, not a glob -- '
339
+ f"matching '/*' probably isn't what you want here",
340
+ )
341
+ for fwd_slash_re in (r'[\\/]', r'[\/]', r'[/\\]'):
342
+ if fwd_slash_re in dct.get(self.key, ''):
343
+ logger.warning(
344
+ fr'pre-commit normalizes the slashes in the top-level '
345
+ fr'{self.key!r} field to forward slashes, so you '
346
+ fr'can use / instead of {fwd_slash_re}',
347
+ )
348
+
349
+
350
+ def _entry(modname: str) -> str:
351
+ """the hook `entry` is passed through `shlex.split()` by the command
352
+ runner, so to prevent issues with spaces and backslashes (on Windows)
353
+ it must be quoted here.
354
+ """
355
+ return f'{shlex.quote(sys.executable)} -m pre_commit.meta_hooks.{modname}'
356
+
357
+
358
+ def warn_unknown_keys_root(
359
+ extra: Sequence[str],
360
+ orig_keys: Sequence[str],
361
+ dct: dict[str, str],
362
+ ) -> None:
363
+ logger.warning(f'Unexpected key(s) present at root: {", ".join(extra)}')
364
+
365
+
366
+ def warn_unknown_keys_repo(
367
+ extra: Sequence[str],
368
+ orig_keys: Sequence[str],
369
+ dct: dict[str, str],
370
+ ) -> None:
371
+ logger.warning(
372
+ f'Unexpected key(s) present on {dct["repo"]}: {", ".join(extra)}',
373
+ )
374
+
375
+
376
+ _meta = (
377
+ (
378
+ 'check-hooks-apply', (
379
+ ('name', 'Check hooks apply to the repository'),
380
+ ('files', f'^{re.escape(C.CONFIG_FILE)}$'),
381
+ ('entry', _entry('check_hooks_apply')),
382
+ ),
383
+ ),
384
+ (
385
+ 'check-useless-excludes', (
386
+ ('name', 'Check for useless excludes'),
387
+ ('files', f'^{re.escape(C.CONFIG_FILE)}$'),
388
+ ('entry', _entry('check_useless_excludes')),
389
+ ),
390
+ ),
391
+ (
392
+ 'identity', (
393
+ ('name', 'identity'),
394
+ ('verbose', True),
395
+ ('entry', _entry('identity')),
396
+ ),
397
+ ),
398
+ )
399
+
400
+
401
+ class NotAllowed(cfgv.OptionalNoDefault):
402
+ def check(self, dct: dict[str, Any]) -> None:
403
+ if self.key in dct:
404
+ raise cfgv.ValidationError(f'{self.key!r} cannot be overridden')
405
+
406
+
407
+ _COMMON_HOOK_WARNINGS = (
408
+ OptionalSensibleRegexAtHook('files', cfgv.check_string),
409
+ OptionalSensibleRegexAtHook('exclude', cfgv.check_string),
410
+ DeprecatedStagesWarning('stages'),
411
+ )
412
+
413
+ META_HOOK_DICT = cfgv.Map(
414
+ 'Hook', 'id',
415
+ cfgv.Required('id', cfgv.check_string),
416
+ cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),
417
+ # language must be `unsupported`
418
+ cfgv.Optional(
419
+ 'language', cfgv.check_one_of({'unsupported'}), 'unsupported',
420
+ ),
421
+ # entry cannot be overridden
422
+ NotAllowed('entry', cfgv.check_any),
423
+ *(
424
+ # default to the hook definition for the meta hooks
425
+ cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id)
426
+ for hook_id, values in _meta
427
+ for key, value in values
428
+ ),
429
+ *(
430
+ # default to the "manifest" parsing
431
+ cfgv.OptionalNoDefault(item.key, item.check_fn)
432
+ # these will always be defaulted above
433
+ if item.key in {'name', 'language', 'entry'} else
434
+ item
435
+ for item in MANIFEST_HOOK_DICT.items
436
+ ),
437
+ *_COMMON_HOOK_WARNINGS,
438
+ )
439
+ CONFIG_HOOK_DICT = cfgv.Map(
440
+ 'Hook', 'id',
441
+
442
+ cfgv.Required('id', cfgv.check_string),
443
+
444
+ # All keys in manifest hook dict are valid in a config hook dict, but
445
+ # are optional.
446
+ # No defaults are provided here as the config is merged on top of the
447
+ # manifest.
448
+ *(
449
+ cfgv.OptionalNoDefault(item.key, item.check_fn)
450
+ for item in MANIFEST_HOOK_DICT.items
451
+ if item.key != 'id'
452
+ if item.key != 'stages'
453
+ if item.key != 'language' # remove
454
+ ),
455
+ StagesMigrationNoDefault('stages', []),
456
+ LanguageMigration('language', cfgv.check_one_of(language_names)), # remove
457
+ *_COMMON_HOOK_WARNINGS,
458
+ )
459
+ LOCAL_HOOK_DICT = cfgv.Map(
460
+ 'Hook', 'id',
461
+
462
+ *MANIFEST_HOOK_DICT.items,
463
+ *_COMMON_HOOK_WARNINGS,
464
+ )
465
+ CONFIG_REPO_DICT = cfgv.Map(
466
+ 'Repository', 'repo',
467
+
468
+ cfgv.Required('repo', cfgv.check_string),
469
+
470
+ cfgv.ConditionalRecurse(
471
+ 'hooks', cfgv.Array(CONFIG_HOOK_DICT),
472
+ 'repo', cfgv.NotIn(LOCAL, META),
473
+ ),
474
+ cfgv.ConditionalRecurse(
475
+ 'hooks', cfgv.Array(LOCAL_HOOK_DICT),
476
+ 'repo', LOCAL,
477
+ ),
478
+ cfgv.ConditionalRecurse(
479
+ 'hooks', cfgv.Array(META_HOOK_DICT),
480
+ 'repo', META,
481
+ ),
482
+
483
+ WarnMutableRev(
484
+ 'rev', cfgv.check_string,
485
+ condition_key='repo',
486
+ condition_value=cfgv.NotIn(LOCAL, META),
487
+ ensure_absent=True,
488
+ ),
489
+ cfgv.WarnAdditionalKeys(('repo', 'rev', 'hooks'), warn_unknown_keys_repo),
490
+ )
491
+ DEFAULT_LANGUAGE_VERSION = cfgv.Map(
492
+ 'DefaultLanguageVersion', None,
493
+ cfgv.NoAdditionalKeys(language_names),
494
+ *(cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in language_names),
495
+ )
496
+ CONFIG_SCHEMA = cfgv.Map(
497
+ 'Config', None,
498
+
499
+ # check first in case it uses some newer, incompatible feature
500
+ cfgv.Optional(
501
+ 'minimum_pre_commit_version',
502
+ cfgv.check_and(cfgv.check_string, check_min_version),
503
+ '0',
504
+ ),
505
+
506
+ cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),
507
+ cfgv.Optional(
508
+ 'default_install_hook_types',
509
+ cfgv.check_array(cfgv.check_one_of(HOOK_TYPES)),
510
+ ['pre-commit'],
511
+ ),
512
+ cfgv.OptionalRecurse(
513
+ 'default_language_version', DEFAULT_LANGUAGE_VERSION, {},
514
+ ),
515
+ StagesMigration('default_stages', STAGES),
516
+ DeprecatedDefaultStagesWarning('default_stages'),
517
+ cfgv.Optional('files', check_string_regex, ''),
518
+ cfgv.Optional('exclude', check_string_regex, '^$'),
519
+ cfgv.Optional('fail_fast', cfgv.check_bool, False),
520
+ cfgv.WarnAdditionalKeys(
521
+ (
522
+ 'repos',
523
+ 'default_install_hook_types',
524
+ 'default_language_version',
525
+ 'default_stages',
526
+ 'files',
527
+ 'exclude',
528
+ 'fail_fast',
529
+ 'minimum_pre_commit_version',
530
+ 'ci',
531
+ ),
532
+ warn_unknown_keys_root,
533
+ ),
534
+ OptionalSensibleRegexAtTop('files', cfgv.check_string),
535
+ OptionalSensibleRegexAtTop('exclude', cfgv.check_string),
536
+
537
+ # do not warn about configuration for pre-commit.ci
538
+ cfgv.OptionalNoDefault('ci', cfgv.check_type(dict)),
539
+ )
540
+
541
+
542
+ class InvalidConfigError(FatalError):
543
+ pass
544
+
545
+
546
+ load_config = functools.partial(
547
+ cfgv.load_from_filename,
548
+ schema=CONFIG_SCHEMA,
549
+ load_strategy=yaml_load,
550
+ exc_tp=InvalidConfigError,
551
+ )