Add types to pre-commit

This commit is contained in:
Anthony Sottile 2020-01-10 23:32:28 -08:00
parent fa536a8693
commit 327ed924a3
62 changed files with 911 additions and 411 deletions

View file

@ -2,9 +2,14 @@ import json
import logging
import os
import shlex
from typing import Any
from typing import Dict
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
import pre_commit.constants as C
from pre_commit import five
@ -15,6 +20,7 @@ from pre_commit.clientlib import META
from pre_commit.languages.all import languages
from pre_commit.languages.helpers import environment_dir
from pre_commit.prefix import Prefix
from pre_commit.store import Store
from pre_commit.util import parse_version
from pre_commit.util import rmtree
@ -22,15 +28,15 @@ from pre_commit.util import rmtree
logger = logging.getLogger('pre_commit')
def _state(additional_deps):
def _state(additional_deps: Sequence[str]) -> object:
return {'additional_dependencies': sorted(additional_deps)}
def _state_filename(prefix, venv):
def _state_filename(prefix: Prefix, venv: str) -> str:
return prefix.path(venv, '.install_state_v' + C.INSTALLED_STATE_VERSION)
def _read_state(prefix, venv):
def _read_state(prefix: Prefix, venv: str) -> Optional[object]:
filename = _state_filename(prefix, venv)
if not os.path.exists(filename):
return None
@ -39,7 +45,7 @@ def _read_state(prefix, venv):
return json.load(f)
def _write_state(prefix, venv, state):
def _write_state(prefix: Prefix, venv: str, state: object) -> None:
state_filename = _state_filename(prefix, venv)
staging = state_filename + 'staging'
with open(staging, 'w') as state_file:
@ -76,11 +82,11 @@ class Hook(NamedTuple):
verbose: bool
@property
def cmd(self):
def cmd(self) -> Tuple[str, ...]:
return tuple(shlex.split(self.entry)) + tuple(self.args)
@property
def install_key(self):
def install_key(self) -> Tuple[Prefix, str, str, Tuple[str, ...]]:
return (
self.prefix,
self.language,
@ -88,7 +94,7 @@ class Hook(NamedTuple):
tuple(self.additional_dependencies),
)
def installed(self):
def installed(self) -> bool:
lang = languages[self.language]
venv = environment_dir(lang.ENVIRONMENT_DIR, self.language_version)
return (
@ -101,7 +107,7 @@ class Hook(NamedTuple):
)
)
def install(self):
def install(self) -> None:
logger.info(f'Installing environment for {self.src}.')
logger.info('Once installed this environment will be reused.')
logger.info('This may take a few minutes...')
@ -120,12 +126,12 @@ class Hook(NamedTuple):
# Write our state to indicate we're installed
_write_state(self.prefix, venv, _state(self.additional_dependencies))
def run(self, file_args, color):
def run(self, file_args: Sequence[str], color: bool) -> Tuple[int, bytes]:
lang = languages[self.language]
return lang.run_hook(self, file_args, color)
@classmethod
def create(cls, src, prefix, dct):
def create(cls, src: str, prefix: Prefix, dct: Dict[str, Any]) -> 'Hook':
# TODO: have cfgv do this (?)
extra_keys = set(dct) - set(_KEYS)
if extra_keys:
@ -136,9 +142,10 @@ class Hook(NamedTuple):
return cls(src=src, prefix=prefix, **{k: dct[k] for k in _KEYS})
def _hook(*hook_dicts, **kwargs):
root_config = kwargs.pop('root_config')
assert not kwargs, kwargs
def _hook(
*hook_dicts: Dict[str, Any],
root_config: Dict[str, Any],
) -> Dict[str, Any]:
ret, rest = dict(hook_dicts[0]), hook_dicts[1:]
for dct in rest:
ret.update(dct)
@ -166,8 +173,12 @@ def _hook(*hook_dicts, **kwargs):
return ret
def _non_cloned_repository_hooks(repo_config, store, root_config):
def _prefix(language_name, deps):
def _non_cloned_repository_hooks(
repo_config: Dict[str, Any],
store: Store,
root_config: Dict[str, Any],
) -> Tuple[Hook, ...]:
def _prefix(language_name: str, deps: Sequence[str]) -> Prefix:
language = languages[language_name]
# pygrep / script / system / docker_image do not have
# environments so they work out of the current directory
@ -186,7 +197,11 @@ def _non_cloned_repository_hooks(repo_config, store, root_config):
)
def _cloned_repository_hooks(repo_config, store, root_config):
def _cloned_repository_hooks(
repo_config: Dict[str, Any],
store: Store,
root_config: Dict[str, Any],
) -> Tuple[Hook, ...]:
repo, rev = repo_config['repo'], repo_config['rev']
manifest_path = os.path.join(store.clone(repo, rev), C.MANIFEST_FILE)
by_id = {hook['id']: hook for hook in load_manifest(manifest_path)}
@ -215,16 +230,20 @@ def _cloned_repository_hooks(repo_config, store, root_config):
)
def _repository_hooks(repo_config, store, root_config):
def _repository_hooks(
repo_config: Dict[str, Any],
store: Store,
root_config: Dict[str, Any],
) -> Tuple[Hook, ...]:
if repo_config['repo'] in {LOCAL, META}:
return _non_cloned_repository_hooks(repo_config, store, root_config)
else:
return _cloned_repository_hooks(repo_config, store, root_config)
def install_hook_envs(hooks, store):
def _need_installed():
seen: Set[Hook] = set()
def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None:
def _need_installed() -> List[Hook]:
seen: Set[Tuple[Prefix, str, str, Tuple[str, ...]]] = set()
ret = []
for hook in hooks:
if hook.install_key not in seen and not hook.installed():
@ -240,7 +259,7 @@ def install_hook_envs(hooks, store):
hook.install()
def all_hooks(root_config, store):
def all_hooks(root_config: Dict[str, Any], store: Store) -> Tuple[Hook, ...]:
return tuple(
hook
for repo in root_config['repos']