Skip to content

Commit 327ed92

Browse files
committed
Add types to pre-commit
1 parent fa536a8 commit 327ed92

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+911
-411
lines changed

.coveragerc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ exclude_lines =
2525
^\s*return NotImplemented\b
2626
^\s*raise$
2727

28+
# Ignore typing-related things
29+
^if (False|TYPE_CHECKING):
30+
: \.\.\.$
31+
2832
# Don't complain if non-runnable code isn't run:
2933
^if __name__ == ['"]__main__['"]:$
3034

pre_commit/clientlib.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import logging
44
import pipes
55
import sys
6+
from typing import Any
7+
from typing import Dict
8+
from typing import Optional
9+
from typing import Sequence
610

711
import cfgv
812
from aspy.yaml import ordered_load
@@ -18,15 +22,15 @@
1822
check_string_regex = cfgv.check_and(cfgv.check_string, cfgv.check_regex)
1923

2024

21-
def check_type_tag(tag):
25+
def check_type_tag(tag: str) -> None:
2226
if tag not in ALL_TAGS:
2327
raise cfgv.ValidationError(
2428
'Type tag {!r} is not recognized. '
2529
'Try upgrading identify and pre-commit?'.format(tag),
2630
)
2731

2832

29-
def check_min_version(version):
33+
def check_min_version(version: str) -> None:
3034
if parse_version(version) > parse_version(C.VERSION):
3135
raise cfgv.ValidationError(
3236
'pre-commit version {} is required but version {} is installed. '
@@ -36,7 +40,7 @@ def check_min_version(version):
3640
)
3741

3842

39-
def _make_argparser(filenames_help):
43+
def _make_argparser(filenames_help: str) -> argparse.ArgumentParser:
4044
parser = argparse.ArgumentParser()
4145
parser.add_argument('filenames', nargs='*', help=filenames_help)
4246
parser.add_argument('-V', '--version', action='version', version=C.VERSION)
@@ -86,7 +90,7 @@ class InvalidManifestError(FatalError):
8690
)
8791

8892

89-
def validate_manifest_main(argv=None):
93+
def validate_manifest_main(argv: Optional[Sequence[str]] = None) -> int:
9094
parser = _make_argparser('Manifest filenames.')
9195
args = parser.parse_args(argv)
9296
ret = 0
@@ -107,15 +111,15 @@ class MigrateShaToRev:
107111
key = 'rev'
108112

109113
@staticmethod
110-
def _cond(key):
114+
def _cond(key: str) -> cfgv.Conditional:
111115
return cfgv.Conditional(
112116
key, cfgv.check_string,
113117
condition_key='repo',
114118
condition_value=cfgv.NotIn(LOCAL, META),
115119
ensure_absent=True,
116120
)
117121

118-
def check(self, dct):
122+
def check(self, dct: Dict[str, Any]) -> None:
119123
if dct.get('repo') in {LOCAL, META}:
120124
self._cond('rev').check(dct)
121125
self._cond('sha').check(dct)
@@ -126,14 +130,14 @@ def check(self, dct):
126130
else:
127131
self._cond('rev').check(dct)
128132

129-
def apply_default(self, dct):
133+
def apply_default(self, dct: Dict[str, Any]) -> None:
130134
if 'sha' in dct:
131135
dct['rev'] = dct.pop('sha')
132136

133137
remove_default = cfgv.Required.remove_default
134138

135139

136-
def _entry(modname):
140+
def _entry(modname: str) -> str:
137141
"""the hook `entry` is passed through `shlex.split()` by the command
138142
runner, so to prevent issues with spaces and backslashes (on Windows)
139143
it must be quoted here.
@@ -143,13 +147,21 @@ def _entry(modname):
143147
)
144148

145149

146-
def warn_unknown_keys_root(extra, orig_keys, dct):
150+
def warn_unknown_keys_root(
151+
extra: Sequence[str],
152+
orig_keys: Sequence[str],
153+
dct: Dict[str, str],
154+
) -> None:
147155
logger.warning(
148156
'Unexpected key(s) present at root: {}'.format(', '.join(extra)),
149157
)
150158

151159

152-
def warn_unknown_keys_repo(extra, orig_keys, dct):
160+
def warn_unknown_keys_repo(
161+
extra: Sequence[str],
162+
orig_keys: Sequence[str],
163+
dct: Dict[str, str],
164+
) -> None:
153165
logger.warning(
154166
'Unexpected key(s) present on {}: {}'.format(
155167
dct['repo'], ', '.join(extra),
@@ -281,7 +293,7 @@ class InvalidConfigError(FatalError):
281293
pass
282294

283295

284-
def ordered_load_normalize_legacy_config(contents):
296+
def ordered_load_normalize_legacy_config(contents: str) -> Dict[str, Any]:
285297
data = ordered_load(contents)
286298
if isinstance(data, list):
287299
# TODO: Once happy, issue a deprecation warning and instructions
@@ -298,7 +310,7 @@ def ordered_load_normalize_legacy_config(contents):
298310
)
299311

300312

301-
def validate_config_main(argv=None):
313+
def validate_config_main(argv: Optional[Sequence[str]] = None) -> int:
302314
parser = _make_argparser('Config filenames.')
303315
args = parser.parse_args(argv)
304316
ret = 0

pre_commit/color.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class InvalidColorSetting(ValueError):
2121
pass
2222

2323

24-
def format_color(text, color, use_color_setting):
24+
def format_color(text: str, color: str, use_color_setting: bool) -> str:
2525
"""Format text with color.
2626
2727
Args:
@@ -38,7 +38,7 @@ def format_color(text, color, use_color_setting):
3838
COLOR_CHOICES = ('auto', 'always', 'never')
3939

4040

41-
def use_color(setting):
41+
def use_color(setting: str) -> bool:
4242
"""Choose whether to use color based on the command argument.
4343
4444
Args:

pre_commit/commands/autoupdate.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import collections
21
import os.path
32
import re
3+
from typing import Any
4+
from typing import Dict
45
from typing import List
6+
from typing import NamedTuple
57
from typing import Optional
8+
from typing import Sequence
9+
from typing import Tuple
610

711
from aspy.yaml import ordered_dump
812
from aspy.yaml import ordered_load
@@ -16,20 +20,23 @@
1620
from pre_commit.clientlib import LOCAL
1721
from pre_commit.clientlib import META
1822
from pre_commit.commands.migrate_config import migrate_config
23+
from pre_commit.store import Store
1924
from pre_commit.util import CalledProcessError
2025
from pre_commit.util import cmd_output
2126
from pre_commit.util import cmd_output_b
2227
from pre_commit.util import tmpdir
2328

2429

25-
class RevInfo(collections.namedtuple('RevInfo', ('repo', 'rev', 'frozen'))):
26-
__slots__ = ()
30+
class RevInfo(NamedTuple):
31+
repo: str
32+
rev: str
33+
frozen: Optional[str]
2734

2835
@classmethod
29-
def from_config(cls, config):
36+
def from_config(cls, config: Dict[str, Any]) -> 'RevInfo':
3037
return cls(config['repo'], config['rev'], None)
3138

32-
def update(self, tags_only, freeze):
39+
def update(self, tags_only: bool, freeze: bool) -> 'RevInfo':
3340
if tags_only:
3441
tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--abbrev=0')
3542
else:
@@ -57,7 +64,11 @@ class RepositoryCannotBeUpdatedError(RuntimeError):
5764
pass
5865

5966

60-
def _check_hooks_still_exist_at_rev(repo_config, info, store):
67+
def _check_hooks_still_exist_at_rev(
68+
repo_config: Dict[str, Any],
69+
info: RevInfo,
70+
store: Store,
71+
) -> None:
6172
try:
6273
path = store.clone(repo_config['repo'], info.rev)
6374
manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE))
@@ -78,7 +89,11 @@ def _check_hooks_still_exist_at_rev(repo_config, info, store):
7889
REV_LINE_FMT = '{}rev:{}{}{}{}'
7990

8091

81-
def _original_lines(path, rev_infos, retry=False):
92+
def _original_lines(
93+
path: str,
94+
rev_infos: List[Optional[RevInfo]],
95+
retry: bool = False,
96+
) -> Tuple[List[str], List[int]]:
8297
"""detect `rev:` lines or reformat the file"""
8398
with open(path) as f:
8499
original = f.read()
@@ -95,7 +110,7 @@ def _original_lines(path, rev_infos, retry=False):
95110
return _original_lines(path, rev_infos, retry=True)
96111

97112

98-
def _write_new_config(path, rev_infos):
113+
def _write_new_config(path: str, rev_infos: List[Optional[RevInfo]]) -> None:
99114
lines, idxs = _original_lines(path, rev_infos)
100115

101116
for idx, rev_info in zip(idxs, rev_infos):
@@ -119,7 +134,13 @@ def _write_new_config(path, rev_infos):
119134
f.write(''.join(lines))
120135

121136

122-
def autoupdate(config_file, store, tags_only, freeze, repos=()):
137+
def autoupdate(
138+
config_file: str,
139+
store: Store,
140+
tags_only: bool,
141+
freeze: bool,
142+
repos: Sequence[str] = (),
143+
) -> int:
123144
"""Auto-update the pre-commit config to the latest versions of repos."""
124145
migrate_config(config_file, quiet=True)
125146
retv = 0

pre_commit/commands/clean.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import os.path
22

33
from pre_commit import output
4+
from pre_commit.store import Store
45
from pre_commit.util import rmtree
56

67

7-
def clean(store):
8+
def clean(store: Store) -> int:
89
legacy_path = os.path.expanduser('~/.pre-commit')
910
for directory in (store.directory, legacy_path):
1011
if os.path.exists(directory):

pre_commit/commands/gc.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import os.path
2+
from typing import Any
3+
from typing import Dict
4+
from typing import Set
5+
from typing import Tuple
26

37
import pre_commit.constants as C
48
from pre_commit import output
@@ -8,9 +12,15 @@
812
from pre_commit.clientlib import load_manifest
913
from pre_commit.clientlib import LOCAL
1014
from pre_commit.clientlib import META
15+
from pre_commit.store import Store
1116

1217

13-
def _mark_used_repos(store, all_repos, unused_repos, repo):
18+
def _mark_used_repos(
19+
store: Store,
20+
all_repos: Dict[Tuple[str, str], str],
21+
unused_repos: Set[Tuple[str, str]],
22+
repo: Dict[str, Any],
23+
) -> None:
1424
if repo['repo'] == META:
1525
return
1626
elif repo['repo'] == LOCAL:
@@ -47,7 +57,7 @@ def _mark_used_repos(store, all_repos, unused_repos, repo):
4757
))
4858

4959

50-
def _gc_repos(store):
60+
def _gc_repos(store: Store) -> int:
5161
configs = store.select_all_configs()
5262
repos = store.select_all_repos()
5363

@@ -73,7 +83,7 @@ def _gc_repos(store):
7383
return len(unused_repos)
7484

7585

76-
def gc(store):
86+
def gc(store: Store) -> int:
7787
with store.exclusive_lock():
7888
repos_removed = _gc_repos(store)
7989
output.write_line(f'{repos_removed} repo(s) removed.')

pre_commit/commands/init_templatedir.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import logging
22
import os.path
3+
from typing import Sequence
34

45
from pre_commit.commands.install_uninstall import install
6+
from pre_commit.store import Store
57
from pre_commit.util import CalledProcessError
68
from pre_commit.util import cmd_output
79

810
logger = logging.getLogger('pre_commit')
911

1012

11-
def init_templatedir(config_file, store, directory, hook_types):
13+
def init_templatedir(
14+
config_file: str,
15+
store: Store,
16+
directory: str,
17+
hook_types: Sequence[str],
18+
) -> int:
1219
install(
1320
config_file, store, hook_types=hook_types,
1421
overwrite=True, skip_on_missing_config=True, git_dir=directory,
@@ -25,3 +32,4 @@ def init_templatedir(config_file, store, directory, hook_types):
2532
logger.warning(
2633
f'maybe `git config --global init.templateDir {dest}`?',
2734
)
35+
return 0

0 commit comments

Comments
 (0)