diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7988012498db7..b6df108a3166c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -69,6 +69,7 @@ repos: rev: v1.2.2 hooks: - id: yesqa + additional_dependencies: [flake8==3.9.0] - repo: local hooks: - id: flake8-rst @@ -164,6 +165,13 @@ repos: files: ^(environment.yml|requirements-dev.txt)$ pass_filenames: false additional_dependencies: [pyyaml] + - id: sync-flake8-versions + name: Check flake8 version is synced across flake8, yesqa, and environment.yml + language: python + entry: python scripts/sync_flake8_versions.py + files: ^(\.pre-commit-config\.yaml|environment\.yml)$ + pass_filenames: false + additional_dependencies: [pyyaml] - id: title-capitalization name: Validate correct capitalization among titles in documentation entry: python scripts/validate_rst_title_capitalization.py diff --git a/environment.yml b/environment.yml index b942adb764104..2849297abf3ac 100644 --- a/environment.yml +++ b/environment.yml @@ -20,7 +20,7 @@ dependencies: # code checks - black=20.8b1 - cpplint - - flake8 + - flake8=3.9.0 - flake8-bugbear>=21.3.2 # used by flake8, find likely bugs - flake8-comprehensions>=3.1.0 # used by flake8, linting of unnecessary comprehensions - isort>=5.2.1 # check that imports are in the right order diff --git a/requirements-dev.txt b/requirements-dev.txt index 094456eb85c9a..966cf50d4fbac 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ asv cython>=0.29.21 black==20.8b1 cpplint -flake8 +flake8==3.9.0 flake8-bugbear>=21.3.2 flake8-comprehensions>=3.1.0 isort>=5.2.1 diff --git a/scripts/sync_flake8_versions.py b/scripts/sync_flake8_versions.py new file mode 100644 index 0000000000000..8dd7abcf47f02 --- /dev/null +++ b/scripts/sync_flake8_versions.py @@ -0,0 +1,101 @@ +""" +Check that the flake8 pins are the same in: + +- environment.yml +- .pre-commit-config.yaml, in the flake8 hook +- .pre-commit-config.yaml, in the additional dependencies of the yesqa hook + +The flake8 hook revision in .pre-commit-config.yaml is taken as the reference revision. + +Usage: either + +- ``python scripts/sync_flake8_versions.py``, or +- ``pre-commit run sync-flake8-versions --all-files``. +""" +import sys +from typing import ( + Any, + Mapping, + NamedTuple, + Sequence, + Tuple, + TypeVar, +) + +import yaml + + +class Revisions(NamedTuple): + precommit_rev: str + precommit_yesqa_rev: str + environment_rev: str + + +YamlMapping = Mapping[str, Any] +Repo = TypeVar("Repo", bound=YamlMapping) + + +def _get_repo_hook(repos: Sequence[Repo], hook_name: str) -> Tuple[Repo, YamlMapping]: + for repo in repos: + for hook in repo["hooks"]: + if hook["id"] == hook_name: + return repo, hook + else: + raise RuntimeError(f"Repo with hook {hook_name} not found") + + +def get_revisions(precommit_config: YamlMapping, environment: YamlMapping) -> Revisions: + repos = precommit_config["repos"] + flake8_repo, _ = _get_repo_hook(repos, "flake8") + precommit_rev = flake8_repo["rev"] + + _, yesqa_hook = _get_repo_hook(repos, "yesqa") + additional_dependencies = yesqa_hook.get("additional_dependencies", []) + for dep in additional_dependencies: + if "==" in dep: + pkg, rev = dep.split("==", maxsplit=1) + if pkg == "flake8": + precommit_yesqa_rev = rev + break + else: + raise RuntimeError( + "flake8 not found, or not pinned, in additional dependencies of yesqa " + "hook in .pre-commit-config.yaml" + ) + + deps = environment["dependencies"] + for dep in deps: + if isinstance(dep, str) and "=" in dep: + pkg, rev = dep.split("=", maxsplit=1) + if pkg == "flake8": + environment_rev = rev + break + else: + raise RuntimeError("flake8 not found, or not pinned, in environment.yml") + + return Revisions(precommit_rev, precommit_yesqa_rev, environment_rev) + + +if __name__ == "__main__": + with open(".pre-commit-config.yaml") as fd: + precommit_config = yaml.safe_load(fd) + with open("environment.yml") as fd: + environment = yaml.safe_load(fd) + + revisions = get_revisions(precommit_config, environment) + + if revisions.environment_rev != revisions.precommit_rev: + sys.stdout.write( + f"flake8 pin in environment.yml is {revisions.environment_rev}, " + f"should be {revisions.precommit_rev}\n" + ) + sys.exit(1) + + if revisions.precommit_yesqa_rev != revisions.precommit_rev: + sys.stdout.write( + f"flake8 pin in yesqa is {revisions.precommit_yesqa_rev}, " + f"should be {revisions.precommit_rev}\n" + ) + sys.exit(1) + + sys.exit(0) diff --git a/scripts/tests/test_sync_flake8_versions.py b/scripts/tests/test_sync_flake8_versions.py new file mode 100644 index 0000000000000..fc559f3e5e982 --- /dev/null +++ b/scripts/tests/test_sync_flake8_versions.py @@ -0,0 +1,25 @@ +from ..sync_flake8_versions import ( + Revisions, + get_revisions, +) + + +def test_get_revisions(): + precommit_config = { + "repos": [ + { + "repo": "https://gitlab.com/pycqa/flake8", + "rev": "foo", + "hooks": [{"id": "flake8"}], + }, + { + "repo": "https://github.com/asottile/yesqa", + "rev": "v1.2.2", + "hooks": [{"id": "yesqa", "additional_dependencies": ["flake8==bar"]}], + }, + ] + } + environment = {"dependencies": ["flake8=qux"]} + result = get_revisions(precommit_config, environment) + expected = Revisions("foo", "bar", "qux") + assert result == expected