We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
warn on unknown keys at the top level
We're now warning on unknown keys at the hook level, this should also happen at the top level

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pre_commit/clientlib.py`
Content:
```
1 from __future__ import absolute_import
2 from __future__ import unicode_literals
3 
4 import argparse
5 import functools
6 import pipes
7 import sys
8 
9 import cfgv
10 from aspy.yaml import ordered_load
11 from identify.identify import ALL_TAGS
12 
13 import pre_commit.constants as C
14 from pre_commit.error_handler import FatalError
15 from pre_commit.languages.all import all_languages
16 from pre_commit.util import parse_version
17 
18 
19 def check_type_tag(tag):
20     if tag not in ALL_TAGS:
21         raise cfgv.ValidationError(
22             'Type tag {!r} is not recognized.  '
23             'Try upgrading identify and pre-commit?'.format(tag),
24         )
25 
26 
27 def check_min_version(version):
28     if parse_version(version) > parse_version(C.VERSION):
29         raise cfgv.ValidationError(
30             'pre-commit version {} is required but version {} is installed.  '
31             'Perhaps run `pip install --upgrade pre-commit`.'.format(
32                 version, C.VERSION,
33             ),
34         )
35 
36 
37 def _make_argparser(filenames_help):
38     parser = argparse.ArgumentParser()
39     parser.add_argument('filenames', nargs='*', help=filenames_help)
40     parser.add_argument('-V', '--version', action='version', version=C.VERSION)
41     return parser
42 
43 
44 MANIFEST_HOOK_DICT = cfgv.Map(
45     'Hook', 'id',
46 
47     cfgv.Required('id', cfgv.check_string),
48     cfgv.Required('name', cfgv.check_string),
49     cfgv.Required('entry', cfgv.check_string),
50     cfgv.Required('language', cfgv.check_one_of(all_languages)),
51     cfgv.Optional('alias', cfgv.check_string, ''),
52 
53     cfgv.Optional(
54         'files', cfgv.check_and(cfgv.check_string, cfgv.check_regex), '',
55     ),
56     cfgv.Optional(
57         'exclude', cfgv.check_and(cfgv.check_string, cfgv.check_regex), '^$',
58     ),
59     cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']),
60     cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []),
61 
62     cfgv.Optional(
63         'additional_dependencies', cfgv.check_array(cfgv.check_string), [],
64     ),
65     cfgv.Optional('args', cfgv.check_array(cfgv.check_string), []),
66     cfgv.Optional('always_run', cfgv.check_bool, False),
67     cfgv.Optional('pass_filenames', cfgv.check_bool, True),
68     cfgv.Optional('description', cfgv.check_string, ''),
69     cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
70     cfgv.Optional('log_file', cfgv.check_string, ''),
71     cfgv.Optional('minimum_pre_commit_version', cfgv.check_string, '0'),
72     cfgv.Optional('require_serial', cfgv.check_bool, False),
73     cfgv.Optional('stages', cfgv.check_array(cfgv.check_one_of(C.STAGES)), []),
74     cfgv.Optional('verbose', cfgv.check_bool, False),
75 )
76 MANIFEST_SCHEMA = cfgv.Array(MANIFEST_HOOK_DICT)
77 
78 
79 class InvalidManifestError(FatalError):
80     pass
81 
82 
83 load_manifest = functools.partial(
84     cfgv.load_from_filename,
85     schema=MANIFEST_SCHEMA,
86     load_strategy=ordered_load,
87     exc_tp=InvalidManifestError,
88 )
89 
90 
91 def validate_manifest_main(argv=None):
92     parser = _make_argparser('Manifest filenames.')
93     args = parser.parse_args(argv)
94     ret = 0
95     for filename in args.filenames:
96         try:
97             load_manifest(filename)
98         except InvalidManifestError as e:
99             print(e)
100             ret = 1
101     return ret
102 
103 
104 LOCAL = 'local'
105 META = 'meta'
106 
107 
108 class MigrateShaToRev(object):
109     @staticmethod
110     def _cond(key):
111         return cfgv.Conditional(
112             key, cfgv.check_string,
113             condition_key='repo',
114             condition_value=cfgv.NotIn(LOCAL, META),
115             ensure_absent=True,
116         )
117 
118     def check(self, dct):
119         if dct.get('repo') in {LOCAL, META}:
120             self._cond('rev').check(dct)
121             self._cond('sha').check(dct)
122         elif 'sha' in dct and 'rev' in dct:
123             raise cfgv.ValidationError('Cannot specify both sha and rev')
124         elif 'sha' in dct:
125             self._cond('sha').check(dct)
126         else:
127             self._cond('rev').check(dct)
128 
129     def apply_default(self, dct):
130         if 'sha' in dct:
131             dct['rev'] = dct.pop('sha')
132 
133     def remove_default(self, dct):
134         pass
135 
136 
137 def _entry(modname):
138     """the hook `entry` is passed through `shlex.split()` by the command
139     runner, so to prevent issues with spaces and backslashes (on Windows)
140     it must be quoted here.
141     """
142     return '{} -m pre_commit.meta_hooks.{}'.format(
143         pipes.quote(sys.executable), modname,
144     )
145 
146 
147 _meta = (
148     (
149         'check-hooks-apply', (
150             ('name', 'Check hooks apply to the repository'),
151             ('files', C.CONFIG_FILE),
152             ('entry', _entry('check_hooks_apply')),
153         ),
154     ),
155     (
156         'check-useless-excludes', (
157             ('name', 'Check for useless excludes'),
158             ('files', C.CONFIG_FILE),
159             ('entry', _entry('check_useless_excludes')),
160         ),
161     ),
162     (
163         'identity', (
164             ('name', 'identity'),
165             ('verbose', True),
166             ('entry', _entry('identity')),
167         ),
168     ),
169 )
170 
171 META_HOOK_DICT = cfgv.Map(
172     'Hook', 'id',
173     cfgv.Required('id', cfgv.check_string),
174     cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),
175     # language must be system
176     cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'),
177     *([
178         # default to the hook definition for the meta hooks
179         cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id)
180         for hook_id, values in _meta
181         for key, value in values
182     ] + [
183         # default to the "manifest" parsing
184         cfgv.OptionalNoDefault(item.key, item.check_fn)
185         # these will always be defaulted above
186         if item.key in {'name', 'language', 'entry'} else
187         item
188         for item in MANIFEST_HOOK_DICT.items
189     ])
190 )
191 CONFIG_HOOK_DICT = cfgv.Map(
192     'Hook', 'id',
193 
194     cfgv.Required('id', cfgv.check_string),
195 
196     # All keys in manifest hook dict are valid in a config hook dict, but
197     # are optional.
198     # No defaults are provided here as the config is merged on top of the
199     # manifest.
200     *[
201         cfgv.OptionalNoDefault(item.key, item.check_fn)
202         for item in MANIFEST_HOOK_DICT.items
203         if item.key != 'id'
204     ]
205 )
206 CONFIG_REPO_DICT = cfgv.Map(
207     'Repository', 'repo',
208 
209     cfgv.Required('repo', cfgv.check_string),
210 
211     cfgv.ConditionalRecurse(
212         'hooks', cfgv.Array(CONFIG_HOOK_DICT),
213         'repo', cfgv.NotIn(LOCAL, META),
214     ),
215     cfgv.ConditionalRecurse(
216         'hooks', cfgv.Array(MANIFEST_HOOK_DICT),
217         'repo', LOCAL,
218     ),
219     cfgv.ConditionalRecurse(
220         'hooks', cfgv.Array(META_HOOK_DICT),
221         'repo', META,
222     ),
223 
224     MigrateShaToRev(),
225 )
226 DEFAULT_LANGUAGE_VERSION = cfgv.Map(
227     'DefaultLanguageVersion', None,
228     cfgv.NoAdditionalKeys(all_languages),
229     *[cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages]
230 )
231 CONFIG_SCHEMA = cfgv.Map(
232     'Config', None,
233 
234     cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),
235     cfgv.OptionalRecurse(
236         'default_language_version', DEFAULT_LANGUAGE_VERSION, {},
237     ),
238     cfgv.Optional(
239         'default_stages',
240         cfgv.check_array(cfgv.check_one_of(C.STAGES)),
241         C.STAGES,
242     ),
243     cfgv.Optional('exclude', cfgv.check_regex, '^$'),
244     cfgv.Optional('fail_fast', cfgv.check_bool, False),
245     cfgv.Optional(
246         'minimum_pre_commit_version',
247         cfgv.check_and(cfgv.check_string, check_min_version),
248         '0',
249     ),
250 )
251 
252 
253 class InvalidConfigError(FatalError):
254     pass
255 
256 
257 def ordered_load_normalize_legacy_config(contents):
258     data = ordered_load(contents)
259     if isinstance(data, list):
260         # TODO: Once happy, issue a deprecation warning and instructions
261         return {'repos': data}
262     else:
263         return data
264 
265 
266 load_config = functools.partial(
267     cfgv.load_from_filename,
268     schema=CONFIG_SCHEMA,
269     load_strategy=ordered_load_normalize_legacy_config,
270     exc_tp=InvalidConfigError,
271 )
272 
273 
274 def validate_config_main(argv=None):
275     parser = _make_argparser('Config filenames.')
276     args = parser.parse_args(argv)
277     ret = 0
278     for filename in args.filenames:
279         try:
280             load_config(filename)
281         except InvalidConfigError as e:
282             print(e)
283             ret = 1
284     return ret
285
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -3,6 +3,7 @@
 
 import argparse
 import functools
+import logging
 import pipes
 import sys
 
@@ -15,6 +16,8 @@
 from pre_commit.languages.all import all_languages
 from pre_commit.util import parse_version
 
+logger = logging.getLogger('pre_commit')
+
 
 def check_type_tag(tag):
     if tag not in ALL_TAGS:
@@ -106,6 +109,8 @@
 
 
 class MigrateShaToRev(object):
+    key = 'rev'
+
     @staticmethod
     def _cond(key):
         return cfgv.Conditional(
@@ -144,6 +149,14 @@
     )
 
 
+def warn_unknown_keys(extra, orig_keys):
+    logger.warning(
+        'Unexpected config key(s): {}'.format(
+            ', '.join(sorted(extra)),
+        ),
+    )
+
+
 _meta = (
     (
         'check-hooks-apply', (
@@ -222,6 +235,10 @@
     ),
 
     MigrateShaToRev(),
+    cfgv.WarnAdditionalKeys(
+        ('repo', 'rev', 'hooks'),
+        warn_unknown_keys,
+    ),
 )
 DEFAULT_LANGUAGE_VERSION = cfgv.Map(
     'DefaultLanguageVersion', None,
@@ -247,6 +264,17 @@
         cfgv.check_and(cfgv.check_string, check_min_version),
         '0',
     ),
+    cfgv.WarnAdditionalKeys(
+        (
+            'repos',
+            'default_language_version',
+            'default_stages',
+            'exclude',
+            'fail_fast',
+            'minimum_pre_commit_version',
+        ),
+        warn_unknown_keys,
+    ),
 )

{"golden_diff": "diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py\n--- a/pre_commit/clientlib.py\n+++ b/pre_commit/clientlib.py\n@@ -3,6 +3,7 @@\n \n import argparse\n import functools\n+import logging\n import pipes\n import sys\n \n@@ -15,6 +16,8 @@\n from pre_commit.languages.all import all_languages\n from pre_commit.util import parse_version\n \n+logger = logging.getLogger('pre_commit')\n+\n \n def check_type_tag(tag):\n     if tag not in ALL_TAGS:\n@@ -106,6 +109,8 @@\n \n \n class MigrateShaToRev(object):\n+    key = 'rev'\n+\n     @staticmethod\n     def _cond(key):\n         return cfgv.Conditional(\n@@ -144,6 +149,14 @@\n     )\n \n \n+def warn_unknown_keys(extra, orig_keys):\n+    logger.warning(\n+        'Unexpected config key(s): {}'.format(\n+            ', '.join(sorted(extra)),\n+        ),\n+    )\n+\n+\n _meta = (\n     (\n         'check-hooks-apply', (\n@@ -222,6 +235,10 @@\n     ),\n \n     MigrateShaToRev(),\n+    cfgv.WarnAdditionalKeys(\n+        ('repo', 'rev', 'hooks'),\n+        warn_unknown_keys,\n+    ),\n )\n DEFAULT_LANGUAGE_VERSION = cfgv.Map(\n     'DefaultLanguageVersion', None,\n@@ -247,6 +264,17 @@\n         cfgv.check_and(cfgv.check_string, check_min_version),\n         '0',\n     ),\n+    cfgv.WarnAdditionalKeys(\n+        (\n+            'repos',\n+            'default_language_version',\n+            'default_stages',\n+            'exclude',\n+            'fail_fast',\n+            'minimum_pre_commit_version',\n+        ),\n+        warn_unknown_keys,\n+    ),\n )\n", "issue": "warn on unknown keys at the top level\nWe're now warning on unknown keys at the hook level, this should also happen at the top level\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import unicode_literals\n\nimport argparse\nimport functools\nimport pipes\nimport sys\n\nimport cfgv\nfrom aspy.yaml import ordered_load\nfrom identify.identify import ALL_TAGS\n\nimport pre_commit.constants as C\nfrom pre_commit.error_handler import FatalError\nfrom pre_commit.languages.all import all_languages\nfrom pre_commit.util import parse_version\n\n\ndef check_type_tag(tag):\n    if tag not in ALL_TAGS:\n        raise cfgv.ValidationError(\n            'Type tag {!r} is not recognized.  '\n            'Try upgrading identify and pre-commit?'.format(tag),\n        )\n\n\ndef check_min_version(version):\n    if parse_version(version) > parse_version(C.VERSION):\n        raise cfgv.ValidationError(\n            'pre-commit version {} is required but version {} is installed.  '\n            'Perhaps run `pip install --upgrade pre-commit`.'.format(\n                version, C.VERSION,\n            ),\n        )\n\n\ndef _make_argparser(filenames_help):\n    parser = argparse.ArgumentParser()\n    parser.add_argument('filenames', nargs='*', help=filenames_help)\n    parser.add_argument('-V', '--version', action='version', version=C.VERSION)\n    return parser\n\n\nMANIFEST_HOOK_DICT = cfgv.Map(\n    'Hook', 'id',\n\n    cfgv.Required('id', cfgv.check_string),\n    cfgv.Required('name', cfgv.check_string),\n    cfgv.Required('entry', cfgv.check_string),\n    cfgv.Required('language', cfgv.check_one_of(all_languages)),\n    cfgv.Optional('alias', cfgv.check_string, ''),\n\n    cfgv.Optional(\n        'files', cfgv.check_and(cfgv.check_string, cfgv.check_regex), '',\n    ),\n    cfgv.Optional(\n        'exclude', cfgv.check_and(cfgv.check_string, cfgv.check_regex), '^$',\n    ),\n    cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']),\n    cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []),\n\n    cfgv.Optional(\n        'additional_dependencies', cfgv.check_array(cfgv.check_string), [],\n    ),\n    cfgv.Optional('args', cfgv.check_array(cfgv.check_string), []),\n    cfgv.Optional('always_run', cfgv.check_bool, False),\n    cfgv.Optional('pass_filenames', cfgv.check_bool, True),\n    cfgv.Optional('description', cfgv.check_string, ''),\n    cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),\n    cfgv.Optional('log_file', cfgv.check_string, ''),\n    cfgv.Optional('minimum_pre_commit_version', cfgv.check_string, '0'),\n    cfgv.Optional('require_serial', cfgv.check_bool, False),\n    cfgv.Optional('stages', cfgv.check_array(cfgv.check_one_of(C.STAGES)), []),\n    cfgv.Optional('verbose', cfgv.check_bool, False),\n)\nMANIFEST_SCHEMA = cfgv.Array(MANIFEST_HOOK_DICT)\n\n\nclass InvalidManifestError(FatalError):\n    pass\n\n\nload_manifest = functools.partial(\n    cfgv.load_from_filename,\n    schema=MANIFEST_SCHEMA,\n    load_strategy=ordered_load,\n    exc_tp=InvalidManifestError,\n)\n\n\ndef validate_manifest_main(argv=None):\n    parser = _make_argparser('Manifest filenames.')\n    args = parser.parse_args(argv)\n    ret = 0\n    for filename in args.filenames:\n        try:\n            load_manifest(filename)\n        except InvalidManifestError as e:\n            print(e)\n            ret = 1\n    return ret\n\n\nLOCAL = 'local'\nMETA = 'meta'\n\n\nclass MigrateShaToRev(object):\n    @staticmethod\n    def _cond(key):\n        return cfgv.Conditional(\n            key, cfgv.check_string,\n            condition_key='repo',\n            condition_value=cfgv.NotIn(LOCAL, META),\n            ensure_absent=True,\n        )\n\n    def check(self, dct):\n        if dct.get('repo') in {LOCAL, META}:\n            self._cond('rev').check(dct)\n            self._cond('sha').check(dct)\n        elif 'sha' in dct and 'rev' in dct:\n            raise cfgv.ValidationError('Cannot specify both sha and rev')\n        elif 'sha' in dct:\n            self._cond('sha').check(dct)\n        else:\n            self._cond('rev').check(dct)\n\n    def apply_default(self, dct):\n        if 'sha' in dct:\n            dct['rev'] = dct.pop('sha')\n\n    def remove_default(self, dct):\n        pass\n\n\ndef _entry(modname):\n    \"\"\"the hook `entry` is passed through `shlex.split()` by the command\n    runner, so to prevent issues with spaces and backslashes (on Windows)\n    it must be quoted here.\n    \"\"\"\n    return '{} -m pre_commit.meta_hooks.{}'.format(\n        pipes.quote(sys.executable), modname,\n    )\n\n\n_meta = (\n    (\n        'check-hooks-apply', (\n            ('name', 'Check hooks apply to the repository'),\n            ('files', C.CONFIG_FILE),\n            ('entry', _entry('check_hooks_apply')),\n        ),\n    ),\n    (\n        'check-useless-excludes', (\n            ('name', 'Check for useless excludes'),\n            ('files', C.CONFIG_FILE),\n            ('entry', _entry('check_useless_excludes')),\n        ),\n    ),\n    (\n        'identity', (\n            ('name', 'identity'),\n            ('verbose', True),\n            ('entry', _entry('identity')),\n        ),\n    ),\n)\n\nMETA_HOOK_DICT = cfgv.Map(\n    'Hook', 'id',\n    cfgv.Required('id', cfgv.check_string),\n    cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),\n    # language must be system\n    cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'),\n    *([\n        # default to the hook definition for the meta hooks\n        cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id)\n        for hook_id, values in _meta\n        for key, value in values\n    ] + [\n        # default to the \"manifest\" parsing\n        cfgv.OptionalNoDefault(item.key, item.check_fn)\n        # these will always be defaulted above\n        if item.key in {'name', 'language', 'entry'} else\n        item\n        for item in MANIFEST_HOOK_DICT.items\n    ])\n)\nCONFIG_HOOK_DICT = cfgv.Map(\n    'Hook', 'id',\n\n    cfgv.Required('id', cfgv.check_string),\n\n    # All keys in manifest hook dict are valid in a config hook dict, but\n    # are optional.\n    # No defaults are provided here as the config is merged on top of the\n    # manifest.\n    *[\n        cfgv.OptionalNoDefault(item.key, item.check_fn)\n        for item in MANIFEST_HOOK_DICT.items\n        if item.key != 'id'\n    ]\n)\nCONFIG_REPO_DICT = cfgv.Map(\n    'Repository', 'repo',\n\n    cfgv.Required('repo', cfgv.check_string),\n\n    cfgv.ConditionalRecurse(\n        'hooks', cfgv.Array(CONFIG_HOOK_DICT),\n        'repo', cfgv.NotIn(LOCAL, META),\n    ),\n    cfgv.ConditionalRecurse(\n        'hooks', cfgv.Array(MANIFEST_HOOK_DICT),\n        'repo', LOCAL,\n    ),\n    cfgv.ConditionalRecurse(\n        'hooks', cfgv.Array(META_HOOK_DICT),\n        'repo', META,\n    ),\n\n    MigrateShaToRev(),\n)\nDEFAULT_LANGUAGE_VERSION = cfgv.Map(\n    'DefaultLanguageVersion', None,\n    cfgv.NoAdditionalKeys(all_languages),\n    *[cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages]\n)\nCONFIG_SCHEMA = cfgv.Map(\n    'Config', None,\n\n    cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),\n    cfgv.OptionalRecurse(\n        'default_language_version', DEFAULT_LANGUAGE_VERSION, {},\n    ),\n    cfgv.Optional(\n        'default_stages',\n        cfgv.check_array(cfgv.check_one_of(C.STAGES)),\n        C.STAGES,\n    ),\n    cfgv.Optional('exclude', cfgv.check_regex, '^$'),\n    cfgv.Optional('fail_fast', cfgv.check_bool, False),\n    cfgv.Optional(\n        'minimum_pre_commit_version',\n        cfgv.check_and(cfgv.check_string, check_min_version),\n        '0',\n    ),\n)\n\n\nclass InvalidConfigError(FatalError):\n    pass\n\n\ndef ordered_load_normalize_legacy_config(contents):\n    data = ordered_load(contents)\n    if isinstance(data, list):\n        # TODO: Once happy, issue a deprecation warning and instructions\n        return {'repos': data}\n    else:\n        return data\n\n\nload_config = functools.partial(\n    cfgv.load_from_filename,\n    schema=CONFIG_SCHEMA,\n    load_strategy=ordered_load_normalize_legacy_config,\n    exc_tp=InvalidConfigError,\n)\n\n\ndef validate_config_main(argv=None):\n    parser = _make_argparser('Config filenames.')\n    args = parser.parse_args(argv)\n    ret = 0\n    for filename in args.filenames:\n        try:\n            load_config(filename)\n        except InvalidConfigError as e:\n            print(e)\n            ret = 1\n    return ret\n", "path": "pre_commit/clientlib.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import unicode_literals\n\nimport argparse\nimport functools\nimport logging\nimport pipes\nimport sys\n\nimport cfgv\nfrom aspy.yaml import ordered_load\nfrom identify.identify import ALL_TAGS\n\nimport pre_commit.constants as C\nfrom pre_commit.error_handler import FatalError\nfrom pre_commit.languages.all import all_languages\nfrom pre_commit.util import parse_version\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef check_type_tag(tag):\n    if tag not in ALL_TAGS:\n        raise cfgv.ValidationError(\n            'Type tag {!r} is not recognized.  '\n            'Try upgrading identify and pre-commit?'.format(tag),\n        )\n\n\ndef check_min_version(version):\n    if parse_version(version) > parse_version(C.VERSION):\n        raise cfgv.ValidationError(\n            'pre-commit version {} is required but version {} is installed.  '\n            'Perhaps run `pip install --upgrade pre-commit`.'.format(\n                version, C.VERSION,\n            ),\n        )\n\n\ndef _make_argparser(filenames_help):\n    parser = argparse.ArgumentParser()\n    parser.add_argument('filenames', nargs='*', help=filenames_help)\n    parser.add_argument('-V', '--version', action='version', version=C.VERSION)\n    return parser\n\n\nMANIFEST_HOOK_DICT = cfgv.Map(\n    'Hook', 'id',\n\n    cfgv.Required('id', cfgv.check_string),\n    cfgv.Required('name', cfgv.check_string),\n    cfgv.Required('entry', cfgv.check_string),\n    cfgv.Required('language', cfgv.check_one_of(all_languages)),\n    cfgv.Optional('alias', cfgv.check_string, ''),\n\n    cfgv.Optional(\n        'files', cfgv.check_and(cfgv.check_string, cfgv.check_regex), '',\n    ),\n    cfgv.Optional(\n        'exclude', cfgv.check_and(cfgv.check_string, cfgv.check_regex), '^$',\n    ),\n    cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']),\n    cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []),\n\n    cfgv.Optional(\n        'additional_dependencies', cfgv.check_array(cfgv.check_string), [],\n    ),\n    cfgv.Optional('args', cfgv.check_array(cfgv.check_string), []),\n    cfgv.Optional('always_run', cfgv.check_bool, False),\n    cfgv.Optional('pass_filenames', cfgv.check_bool, True),\n    cfgv.Optional('description', cfgv.check_string, ''),\n    cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),\n    cfgv.Optional('log_file', cfgv.check_string, ''),\n    cfgv.Optional('minimum_pre_commit_version', cfgv.check_string, '0'),\n    cfgv.Optional('require_serial', cfgv.check_bool, False),\n    cfgv.Optional('stages', cfgv.check_array(cfgv.check_one_of(C.STAGES)), []),\n    cfgv.Optional('verbose', cfgv.check_bool, False),\n)\nMANIFEST_SCHEMA = cfgv.Array(MANIFEST_HOOK_DICT)\n\n\nclass InvalidManifestError(FatalError):\n    pass\n\n\nload_manifest = functools.partial(\n    cfgv.load_from_filename,\n    schema=MANIFEST_SCHEMA,\n    load_strategy=ordered_load,\n    exc_tp=InvalidManifestError,\n)\n\n\ndef validate_manifest_main(argv=None):\n    parser = _make_argparser('Manifest filenames.')\n    args = parser.parse_args(argv)\n    ret = 0\n    for filename in args.filenames:\n        try:\n            load_manifest(filename)\n        except InvalidManifestError as e:\n            print(e)\n            ret = 1\n    return ret\n\n\nLOCAL = 'local'\nMETA = 'meta'\n\n\nclass MigrateShaToRev(object):\n    key = 'rev'\n\n    @staticmethod\n    def _cond(key):\n        return cfgv.Conditional(\n            key, cfgv.check_string,\n            condition_key='repo',\n            condition_value=cfgv.NotIn(LOCAL, META),\n            ensure_absent=True,\n        )\n\n    def check(self, dct):\n        if dct.get('repo') in {LOCAL, META}:\n            self._cond('rev').check(dct)\n            self._cond('sha').check(dct)\n        elif 'sha' in dct and 'rev' in dct:\n            raise cfgv.ValidationError('Cannot specify both sha and rev')\n        elif 'sha' in dct:\n            self._cond('sha').check(dct)\n        else:\n            self._cond('rev').check(dct)\n\n    def apply_default(self, dct):\n        if 'sha' in dct:\n            dct['rev'] = dct.pop('sha')\n\n    def remove_default(self, dct):\n        pass\n\n\ndef _entry(modname):\n    \"\"\"the hook `entry` is passed through `shlex.split()` by the command\n    runner, so to prevent issues with spaces and backslashes (on Windows)\n    it must be quoted here.\n    \"\"\"\n    return '{} -m pre_commit.meta_hooks.{}'.format(\n        pipes.quote(sys.executable), modname,\n    )\n\n\ndef warn_unknown_keys(extra, orig_keys):\n    logger.warning(\n        'Unexpected config key(s): {}'.format(\n            ', '.join(sorted(extra)),\n        ),\n    )\n\n\n_meta = (\n    (\n        'check-hooks-apply', (\n            ('name', 'Check hooks apply to the repository'),\n            ('files', C.CONFIG_FILE),\n            ('entry', _entry('check_hooks_apply')),\n        ),\n    ),\n    (\n        'check-useless-excludes', (\n            ('name', 'Check for useless excludes'),\n            ('files', C.CONFIG_FILE),\n            ('entry', _entry('check_useless_excludes')),\n        ),\n    ),\n    (\n        'identity', (\n            ('name', 'identity'),\n            ('verbose', True),\n            ('entry', _entry('identity')),\n        ),\n    ),\n)\n\nMETA_HOOK_DICT = cfgv.Map(\n    'Hook', 'id',\n    cfgv.Required('id', cfgv.check_string),\n    cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),\n    # language must be system\n    cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'),\n    *([\n        # default to the hook definition for the meta hooks\n        cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id)\n        for hook_id, values in _meta\n        for key, value in values\n    ] + [\n        # default to the \"manifest\" parsing\n        cfgv.OptionalNoDefault(item.key, item.check_fn)\n        # these will always be defaulted above\n        if item.key in {'name', 'language', 'entry'} else\n        item\n        for item in MANIFEST_HOOK_DICT.items\n    ])\n)\nCONFIG_HOOK_DICT = cfgv.Map(\n    'Hook', 'id',\n\n    cfgv.Required('id', cfgv.check_string),\n\n    # All keys in manifest hook dict are valid in a config hook dict, but\n    # are optional.\n    # No defaults are provided here as the config is merged on top of the\n    # manifest.\n    *[\n        cfgv.OptionalNoDefault(item.key, item.check_fn)\n        for item in MANIFEST_HOOK_DICT.items\n        if item.key != 'id'\n    ]\n)\nCONFIG_REPO_DICT = cfgv.Map(\n    'Repository', 'repo',\n\n    cfgv.Required('repo', cfgv.check_string),\n\n    cfgv.ConditionalRecurse(\n        'hooks', cfgv.Array(CONFIG_HOOK_DICT),\n        'repo', cfgv.NotIn(LOCAL, META),\n    ),\n    cfgv.ConditionalRecurse(\n        'hooks', cfgv.Array(MANIFEST_HOOK_DICT),\n        'repo', LOCAL,\n    ),\n    cfgv.ConditionalRecurse(\n        'hooks', cfgv.Array(META_HOOK_DICT),\n        'repo', META,\n    ),\n\n    MigrateShaToRev(),\n    cfgv.WarnAdditionalKeys(\n        ('repo', 'rev', 'hooks'),\n        warn_unknown_keys,\n    ),\n)\nDEFAULT_LANGUAGE_VERSION = cfgv.Map(\n    'DefaultLanguageVersion', None,\n    cfgv.NoAdditionalKeys(all_languages),\n    *[cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages]\n)\nCONFIG_SCHEMA = cfgv.Map(\n    'Config', None,\n\n    cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),\n    cfgv.OptionalRecurse(\n        'default_language_version', DEFAULT_LANGUAGE_VERSION, {},\n    ),\n    cfgv.Optional(\n        'default_stages',\n        cfgv.check_array(cfgv.check_one_of(C.STAGES)),\n        C.STAGES,\n    ),\n    cfgv.Optional('exclude', cfgv.check_regex, '^$'),\n    cfgv.Optional('fail_fast', cfgv.check_bool, False),\n    cfgv.Optional(\n        'minimum_pre_commit_version',\n        cfgv.check_and(cfgv.check_string, check_min_version),\n        '0',\n    ),\n    cfgv.WarnAdditionalKeys(\n        (\n            'repos',\n            'default_language_version',\n            'default_stages',\n            'exclude',\n            'fail_fast',\n            'minimum_pre_commit_version',\n        ),\n        warn_unknown_keys,\n    ),\n)\n\n\nclass InvalidConfigError(FatalError):\n    pass\n\n\ndef ordered_load_normalize_legacy_config(contents):\n    data = ordered_load(contents)\n    if isinstance(data, list):\n        # TODO: Once happy, issue a deprecation warning and instructions\n        return {'repos': data}\n    else:\n        return data\n\n\nload_config = functools.partial(\n    cfgv.load_from_filename,\n    schema=CONFIG_SCHEMA,\n    load_strategy=ordered_load_normalize_legacy_config,\n    exc_tp=InvalidConfigError,\n)\n\n\ndef validate_config_main(argv=None):\n    parser = _make_argparser('Config filenames.')\n    args = parser.parse_args(argv)\n    ret = 0\n    for filename in args.filenames:\n        try:\n            load_config(filename)\n        except InvalidConfigError as e:\n            print(e)\n            ret = 1\n    return ret\n", "path": "pre_commit/clientlib.py"}]}