mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-01-14 21:10:27 -06:00
leverage mypy to check language implementations
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Callable
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pre_commit.languages import conda
|
||||
from pre_commit.languages import docker
|
||||
@@ -15,58 +19,43 @@ from pre_commit.languages import rust
|
||||
from pre_commit.languages import script
|
||||
from pre_commit.languages import swift
|
||||
from pre_commit.languages import system
|
||||
from pre_commit.prefix import Prefix
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pre_commit.repository import Hook
|
||||
|
||||
|
||||
# A language implements the following constant and functions in its module:
|
||||
#
|
||||
# # Use None for no environment
|
||||
# ENVIRONMENT_DIR = 'foo_env'
|
||||
#
|
||||
# def get_default_version():
|
||||
# """Return a value to replace the 'default' value for language_version.
|
||||
#
|
||||
# return 'default' if there is no better option.
|
||||
# """
|
||||
#
|
||||
# def healthy(prefix, language_version):
|
||||
# """Return whether or not the environment is considered functional."""
|
||||
#
|
||||
# def install_environment(prefix, version, additional_dependencies):
|
||||
# """Installs a repository in the given repository. Note that the current
|
||||
# working directory will already be inside the repository.
|
||||
#
|
||||
# Args:
|
||||
# prefix - `Prefix` bound to the repository.
|
||||
# version - A version specified in the hook configuration or 'default'.
|
||||
# """
|
||||
#
|
||||
# def run_hook(hook, file_args, color):
|
||||
# """Runs a hook and returns the returncode and output of running that
|
||||
# hook.
|
||||
#
|
||||
# Args:
|
||||
# hook - `Hook`
|
||||
# file_args - The files to be run
|
||||
# color - whether the hook should be given a pty (when supported)
|
||||
#
|
||||
# Returns:
|
||||
# (returncode, output)
|
||||
# """
|
||||
class Language(NamedTuple):
|
||||
name: str
|
||||
# Use `None` for no installation / environment
|
||||
ENVIRONMENT_DIR: Optional[str]
|
||||
# return a value to replace `'default` for `language_version`
|
||||
get_default_version: Callable[[], str]
|
||||
# return whether the environment is healthy (or should be rebuilt)
|
||||
healthy: Callable[[Prefix, str], bool]
|
||||
# install a repository for the given language and language_version
|
||||
install_environment: Callable[[Prefix, str, Sequence[str]], None]
|
||||
# execute a hook and return the exit code and output
|
||||
run_hook: 'Callable[[Hook, Sequence[str], bool], Tuple[int, bytes]]'
|
||||
|
||||
languages: Dict[str, Any] = {
|
||||
'conda': conda,
|
||||
'docker': docker,
|
||||
'docker_image': docker_image,
|
||||
'fail': fail,
|
||||
'golang': golang,
|
||||
'node': node,
|
||||
'pygrep': pygrep,
|
||||
'python': python,
|
||||
'python_venv': python_venv,
|
||||
'ruby': ruby,
|
||||
'rust': rust,
|
||||
'script': script,
|
||||
'swift': swift,
|
||||
'system': system,
|
||||
|
||||
# TODO: back to modules + Protocol: https://github.com/python/mypy/issues/5018
|
||||
languages = {
|
||||
# BEGIN GENERATED (testing/gen-languages-all)
|
||||
'conda': Language(name='conda', ENVIRONMENT_DIR=conda.ENVIRONMENT_DIR, get_default_version=conda.get_default_version, healthy=conda.healthy, install_environment=conda.install_environment, run_hook=conda.run_hook), # noqa: E501
|
||||
'docker': Language(name='docker', ENVIRONMENT_DIR=docker.ENVIRONMENT_DIR, get_default_version=docker.get_default_version, healthy=docker.healthy, install_environment=docker.install_environment, run_hook=docker.run_hook), # noqa: E501
|
||||
'docker_image': Language(name='docker_image', ENVIRONMENT_DIR=docker_image.ENVIRONMENT_DIR, get_default_version=docker_image.get_default_version, healthy=docker_image.healthy, install_environment=docker_image.install_environment, run_hook=docker_image.run_hook), # noqa: E501
|
||||
'fail': Language(name='fail', ENVIRONMENT_DIR=fail.ENVIRONMENT_DIR, get_default_version=fail.get_default_version, healthy=fail.healthy, install_environment=fail.install_environment, run_hook=fail.run_hook), # noqa: E501
|
||||
'golang': Language(name='golang', ENVIRONMENT_DIR=golang.ENVIRONMENT_DIR, get_default_version=golang.get_default_version, healthy=golang.healthy, install_environment=golang.install_environment, run_hook=golang.run_hook), # noqa: E501
|
||||
'node': Language(name='node', ENVIRONMENT_DIR=node.ENVIRONMENT_DIR, get_default_version=node.get_default_version, healthy=node.healthy, install_environment=node.install_environment, run_hook=node.run_hook), # noqa: E501
|
||||
'pygrep': Language(name='pygrep', ENVIRONMENT_DIR=pygrep.ENVIRONMENT_DIR, get_default_version=pygrep.get_default_version, healthy=pygrep.healthy, install_environment=pygrep.install_environment, run_hook=pygrep.run_hook), # noqa: E501
|
||||
'python': Language(name='python', ENVIRONMENT_DIR=python.ENVIRONMENT_DIR, get_default_version=python.get_default_version, healthy=python.healthy, install_environment=python.install_environment, run_hook=python.run_hook), # noqa: E501
|
||||
'python_venv': Language(name='python_venv', ENVIRONMENT_DIR=python_venv.ENVIRONMENT_DIR, get_default_version=python_venv.get_default_version, healthy=python_venv.healthy, install_environment=python_venv.install_environment, run_hook=python_venv.run_hook), # noqa: E501
|
||||
'ruby': Language(name='ruby', ENVIRONMENT_DIR=ruby.ENVIRONMENT_DIR, get_default_version=ruby.get_default_version, healthy=ruby.healthy, install_environment=ruby.install_environment, run_hook=ruby.run_hook), # noqa: E501
|
||||
'rust': Language(name='rust', ENVIRONMENT_DIR=rust.ENVIRONMENT_DIR, get_default_version=rust.get_default_version, healthy=rust.healthy, install_environment=rust.install_environment, run_hook=rust.run_hook), # noqa: E501
|
||||
'script': Language(name='script', ENVIRONMENT_DIR=script.ENVIRONMENT_DIR, get_default_version=script.get_default_version, healthy=script.healthy, install_environment=script.install_environment, run_hook=script.run_hook), # noqa: E501
|
||||
'swift': Language(name='swift', ENVIRONMENT_DIR=swift.ENVIRONMENT_DIR, get_default_version=swift.get_default_version, healthy=swift.healthy, install_environment=swift.install_environment, run_hook=swift.run_hook), # noqa: E501
|
||||
'system': Language(name='system', ENVIRONMENT_DIR=system.ENVIRONMENT_DIR, get_default_version=system.get_default_version, healthy=system.healthy, install_environment=system.install_environment, run_hook=system.run_hook), # noqa: E501
|
||||
# END GENERATED
|
||||
}
|
||||
all_languages = sorted(languages)
|
||||
|
||||
@@ -113,6 +113,7 @@ class Hook(NamedTuple):
|
||||
logger.info('This may take a few minutes...')
|
||||
|
||||
lang = languages[self.language]
|
||||
assert lang.ENVIRONMENT_DIR is not None
|
||||
venv = environment_dir(lang.ENVIRONMENT_DIR, self.language_version)
|
||||
|
||||
# There's potentially incomplete cleanup from previous runs
|
||||
|
||||
27
testing/gen-languages-all
Executable file
27
testing/gen-languages-all
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
LANGUAGES = [
|
||||
'conda', 'docker', 'docker_image', 'fail', 'golang', 'node', 'pygrep',
|
||||
'python', 'python_venv', 'ruby', 'rust', 'script', 'swift', 'system',
|
||||
]
|
||||
FIELDS = [
|
||||
'ENVIRONMENT_DIR', 'get_default_version', 'healthy', 'install_environment',
|
||||
'run_hook',
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(f' # BEGIN GENERATED ({sys.argv[0]})')
|
||||
for lang in LANGUAGES:
|
||||
parts = [f' {lang!r}: Language(name={lang!r}']
|
||||
for k in FIELDS:
|
||||
parts.append(f', {k}={lang}.{k}')
|
||||
parts.append('), # noqa: E501')
|
||||
print(''.join(parts))
|
||||
print(' # END GENERATED')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
@@ -1,62 +0,0 @@
|
||||
import inspect
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from pre_commit.languages.all import all_languages
|
||||
from pre_commit.languages.all import languages
|
||||
from pre_commit.prefix import Prefix
|
||||
|
||||
|
||||
def _argspec(annotations):
|
||||
args = [k for k in annotations if k != 'return']
|
||||
return inspect.FullArgSpec(
|
||||
args=args, annotations=annotations,
|
||||
varargs=None, varkw=None, defaults=None,
|
||||
kwonlyargs=[], kwonlydefaults=None,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('language', all_languages)
|
||||
def test_install_environment_argspec(language):
|
||||
expected_argspec = _argspec({
|
||||
'return': None,
|
||||
'prefix': Prefix,
|
||||
'version': str,
|
||||
'additional_dependencies': Sequence[str],
|
||||
})
|
||||
argspec = inspect.getfullargspec(languages[language].install_environment)
|
||||
assert argspec == expected_argspec
|
||||
|
||||
|
||||
@pytest.mark.parametrize('language', all_languages)
|
||||
def test_ENVIRONMENT_DIR(language):
|
||||
assert hasattr(languages[language], 'ENVIRONMENT_DIR')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('language', all_languages)
|
||||
def test_run_hook_argspec(language):
|
||||
expected_argspec = _argspec({
|
||||
'return': Tuple[int, bytes],
|
||||
'hook': 'Hook', 'file_args': Sequence[str], 'color': bool,
|
||||
})
|
||||
argspec = inspect.getfullargspec(languages[language].run_hook)
|
||||
assert argspec == expected_argspec
|
||||
|
||||
|
||||
@pytest.mark.parametrize('language', all_languages)
|
||||
def test_get_default_version_argspec(language):
|
||||
expected_argspec = _argspec({'return': str})
|
||||
argspec = inspect.getfullargspec(languages[language].get_default_version)
|
||||
assert argspec == expected_argspec
|
||||
|
||||
|
||||
@pytest.mark.parametrize('language', all_languages)
|
||||
def test_healthy_argspec(language):
|
||||
expected_argspec = _argspec({
|
||||
'return': bool,
|
||||
'prefix': Prefix, 'language_version': str,
|
||||
})
|
||||
argspec = inspect.getfullargspec(languages[language].healthy)
|
||||
assert argspec == expected_argspec
|
||||
Reference in New Issue
Block a user