diff --git a/.gitignore b/.gitignore index 32c2fec0..4f6c5b7c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,4 @@ .*.sw[a-z] .coverage .tox -.venv.touch -/.mypy_cache -/.pytest_cache -/venv* -coverage-html dist diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4639bc6b..8804d9d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,47 +1,43 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.2.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - - id: check-docstring-first - - id: check-json - - id: check-added-large-files - id: check-yaml - id: debug-statements - - id: name-tests-test - id: double-quote-string-fixer + - id: name-tests-test - id: requirements-txt-fixer -- repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 - hooks: - - id: flake8 - additional_dependencies: [flake8-typing-imports==1.7.0] -- repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v1.5.7 +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v1.20.1 hooks: - - id: autopep8 + - id: setup-cfg-fmt - repo: https://github.com/asottile/reorder_python_imports - rev: v2.6.0 + rev: v3.0.1 hooks: - id: reorder-python-imports - args: [--py3-plus] -- repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 - hooks: - - id: pyupgrade - args: [--py36-plus] + args: [--py37-plus, --add-import, 'from __future__ import annotations'] - repo: https://github.com/asottile/add-trailing-comma - rev: v2.2.1 + rev: v2.2.2 hooks: - id: add-trailing-comma args: [--py36-plus] -- repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.20.0 +- repo: https://github.com/asottile/pyupgrade + rev: v2.31.1 hooks: - - id: setup-cfg-fmt + - id: pyupgrade + args: [--py37-plus] +- repo: https://github.com/pre-commit/mirrors-autopep8 + rev: v1.6.0 + hooks: + - id: autopep8 +- repo: https://github.com/PyCQA/flake8 + rev: 4.0.1 + hooks: + - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.920 + rev: v0.942 hooks: - id: mypy additional_dependencies: [types-all] diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 56906603..9e835d58 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -162,7 +162,7 @@ language: python types: [text] - id: name-tests-test - name: tests should end in _test.py + name: python tests naming description: this verifies that test files are named correctly. entry: name-tests-test language: python diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e72022..6a2ae68b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +4.2.0 - 2022-04-06 +================== + +### Features +- `name-tests-test`: updated display text. + - #713 PR by @asottile. +- `check-docstring-first`: make output more parsable. + - #748 PR by @asottile. +- `check-merge-conflict`: make output more parsable. + - #748 PR by @asottile. +- `debug-statements`: make output more parsable. + - #748 PR by @asottile. + +### Fixes +- `check-merge-conflict`: fix detection of `======` conflict marker on windows. + - #748 PR by @asottile. + +### Updating +- Drop python<3.7. + - #719 PR by @asottile. +- Changed default branch from `master` to `main`. + - #744 PR by @asottile. + 4.1.0 - 2021-12-22 ================== diff --git a/README.md b/README.md index c5a199e1..73688319 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Build Status](https://asottile.visualstudio.com/asottile/_apis/build/status/pre-commit.pre-commit-hooks?branchName=master)](https://asottile.visualstudio.com/asottile/_build/latest?definitionId=17&branchName=master) -[![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/asottile/asottile/17/master.svg)](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=17&branchName=master) -[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pre-commit/pre-commit-hooks/master.svg)](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit-hooks/master) +[![Build Status](https://asottile.visualstudio.com/asottile/_apis/build/status/pre-commit.pre-commit-hooks?branchName=main)](https://asottile.visualstudio.com/asottile/_build/latest?definitionId=17&branchName=main) +[![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/asottile/asottile/17/main.svg)](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=17&branchName=main) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pre-commit/pre-commit-hooks/main.svg)](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit-hooks/main) pre-commit-hooks ================ @@ -16,7 +16,7 @@ Add this to your `.pre-commit-config.yaml` ```yaml - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 # Use the ref you want to point at + rev: v4.2.0 # Use the ref you want to point at hooks: - id: trailing-whitespace # - id: ... @@ -125,6 +125,10 @@ Sort the lines in specified files (defaults to alphabetical). You must provide list of target files as input to it. Note that this hook WILL remove blank lines and does NOT respect any comments. +The following arguments are available: +- `--ignore-case` - fold lower case to upper case characters. +- `--unique` - ensure each line is unique. + #### `forbid-new-submodules` Prevent addition of new git submodules. @@ -142,8 +146,8 @@ Assert that files in tests/ end in `_test.py`. #### `no-commit-to-branch` Protect specific branches from direct checkins. - - Use `args: [--branch, staging, --branch, master]` to set the branch. - Both `master` and `main` are protected by default if no branch argument is set. + - Use `args: [--branch, staging, --branch, main]` to set the branch. + Both `main` and `master` are protected by default if no branch argument is set. - `-b` / `--branch` may be specified multiple times to protect multiple branches. - `-p` / `--pattern` can be used to protect branches that match a supplied regex diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 58dc61dd..117b014f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,6 @@ trigger: branches: - include: [master, test-me-*] + include: [main, test-me-*] tags: include: ['*'] @@ -10,7 +10,7 @@ resources: type: github endpoint: github name: asottile/azure-pipeline-templates - ref: refs/tags/v2.1.0 + ref: refs/tags/v2.4.0 jobs: - template: job--python-tox.yml@asottile @@ -19,5 +19,5 @@ jobs: os: windows - template: job--python-tox.yml@asottile parameters: - toxenvs: [pypy3, py36, py37, py38] + toxenvs: [py37, py38, py39, py310] os: linux diff --git a/pre_commit_hooks/check_added_large_files.py b/pre_commit_hooks/check_added_large_files.py index e1beb4ef..79c8d4e3 100644 --- a/pre_commit_hooks/check_added_large_files.py +++ b/pre_commit_hooks/check_added_large_files.py @@ -1,16 +1,16 @@ +from __future__ import annotations + import argparse import math import os import subprocess -from typing import Optional from typing import Sequence -from typing import Set from pre_commit_hooks.util import added_files from pre_commit_hooks.util import zsplit -def filter_lfs_files(filenames: Set[str]) -> None: # pragma: no cover (lfs) +def filter_lfs_files(filenames: set[str]) -> None: # pragma: no cover (lfs) """Remove files tracked by git-lfs from the set.""" if not filenames: return @@ -54,7 +54,7 @@ def find_large_added_files( return retv -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( 'filenames', nargs='*', diff --git a/pre_commit_hooks/check_ast.py b/pre_commit_hooks/check_ast.py index ab5661dc..fdac3617 100644 --- a/pre_commit_hooks/check_ast.py +++ b/pre_commit_hooks/check_ast.py @@ -1,13 +1,14 @@ +from __future__ import annotations + import argparse import ast import platform import sys import traceback -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_builtin_literals.py b/pre_commit_hooks/check_builtin_literals.py index 3fbae3e8..d3054aa0 100644 --- a/pre_commit_hooks/check_builtin_literals.py +++ b/pre_commit_hooks/check_builtin_literals.py @@ -1,10 +1,9 @@ +from __future__ import annotations + import argparse import ast -from typing import List from typing import NamedTuple -from typing import Optional from typing import Sequence -from typing import Set BUILTIN_TYPES = { @@ -27,10 +26,10 @@ class Call(NamedTuple): class Visitor(ast.NodeVisitor): def __init__( self, - ignore: Optional[Sequence[str]] = None, + ignore: Sequence[str] | None = None, allow_dict_kwargs: bool = True, ) -> None: - self.builtin_type_calls: List[Call] = [] + self.builtin_type_calls: list[Call] = [] self.ignore = set(ignore) if ignore else set() self.allow_dict_kwargs = allow_dict_kwargs @@ -56,9 +55,9 @@ def visit_Call(self, node: ast.Call) -> None: def check_file( filename: str, - ignore: Optional[Sequence[str]] = None, + ignore: Sequence[str] | None = None, allow_dict_kwargs: bool = True, -) -> List[Call]: +) -> list[Call]: with open(filename, 'rb') as f: tree = ast.parse(f.read(), filename=filename) visitor = Visitor(ignore=ignore, allow_dict_kwargs=allow_dict_kwargs) @@ -66,11 +65,11 @@ def check_file( return visitor.builtin_type_calls -def parse_ignore(value: str) -> Set[str]: +def parse_ignore(value: str) -> set[str]: return set(value.split(',')) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') parser.add_argument('--ignore', type=parse_ignore, default=set()) diff --git a/pre_commit_hooks/check_byte_order_marker.py b/pre_commit_hooks/check_byte_order_marker.py index fda05e84..59cc5612 100644 --- a/pre_commit_hooks/check_byte_order_marker.py +++ b/pre_commit_hooks/check_byte_order_marker.py @@ -1,9 +1,10 @@ +from __future__ import annotations + import argparse -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to check') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_case_conflict.py b/pre_commit_hooks/check_case_conflict.py index c3f39db5..33a13f1b 100644 --- a/pre_commit_hooks/check_case_conflict.py +++ b/pre_commit_hooks/check_case_conflict.py @@ -1,15 +1,15 @@ +from __future__ import annotations + import argparse from typing import Iterable from typing import Iterator -from typing import Optional from typing import Sequence -from typing import Set from pre_commit_hooks.util import added_files from pre_commit_hooks.util import cmd_output -def lower_set(iterable: Iterable[str]) -> Set[str]: +def lower_set(iterable: Iterable[str]) -> set[str]: return {x.lower() for x in iterable} @@ -21,7 +21,7 @@ def parents(file: str) -> Iterator[str]: path_parts.pop() -def directories_for(files: Set[str]) -> Set[str]: +def directories_for(files: set[str]) -> set[str]: return {parent for file in files for parent in parents(file)} @@ -56,7 +56,7 @@ def find_conflicting_filenames(filenames: Sequence[str]) -> int: return retv -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( 'filenames', nargs='*', diff --git a/pre_commit_hooks/check_docstring_first.py b/pre_commit_hooks/check_docstring_first.py index 875c0fba..d55f08a5 100644 --- a/pre_commit_hooks/check_docstring_first.py +++ b/pre_commit_hooks/check_docstring_first.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import argparse import io import tokenize from tokenize import tokenize as tokenize_tokenize -from typing import Optional from typing import Sequence NON_CODE_TOKENS = frozenset(( @@ -27,13 +28,13 @@ def check_docstring_first(src: bytes, filename: str = '') -> int: if tok_type == tokenize.STRING and scol == 0: if found_docstring_line is not None: print( - f'{filename}:{sline} Multiple module docstrings ' + f'{filename}:{sline}: Multiple module docstrings ' f'(first docstring on line {found_docstring_line}).', ) return 1 elif found_code_line is not None: print( - f'{filename}:{sline} Module docstring appears after code ' + f'{filename}:{sline}: Module docstring appears after code ' f'(code seen on line {found_code_line}).', ) return 1 @@ -45,7 +46,7 @@ def check_docstring_first(src: bytes, filename: str = '') -> int: return 0 -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_executables_have_shebangs.py b/pre_commit_hooks/check_executables_have_shebangs.py index 34af5cac..6b5402ac 100644 --- a/pre_commit_hooks/check_executables_have_shebangs.py +++ b/pre_commit_hooks/check_executables_have_shebangs.py @@ -1,13 +1,12 @@ """Check that executable text files have a shebang.""" +from __future__ import annotations + import argparse import shlex import sys from typing import Generator -from typing import List from typing import NamedTuple -from typing import Optional from typing import Sequence -from typing import Set from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import zsplit @@ -15,7 +14,7 @@ EXECUTABLE_VALUES = frozenset(('1', '3', '5', '7')) -def check_executables(paths: List[str]) -> int: +def check_executables(paths: list[str]) -> int: if sys.platform == 'win32': # pragma: win32 cover return _check_git_filemode(paths) else: # pragma: win32 no cover @@ -42,7 +41,7 @@ def git_ls_files(paths: Sequence[str]) -> Generator[GitLsFile, None, None]: def _check_git_filemode(paths: Sequence[str]) -> int: - seen: Set[str] = set() + seen: set[str] = set() for ls_file in git_ls_files(paths): is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:]) if is_executable and not has_shebang(ls_file.filename): @@ -71,7 +70,7 @@ def _message(path: str) -> None: ) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('filenames', nargs='*') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_json.py b/pre_commit_hooks/check_json.py index 96ba024e..6a679fee 100644 --- a/pre_commit_hooks/check_json.py +++ b/pre_commit_hooks/check_json.py @@ -1,16 +1,14 @@ +from __future__ import annotations + import argparse import json from typing import Any -from typing import Dict -from typing import List -from typing import Optional from typing import Sequence -from typing import Tuple def raise_duplicate_keys( - ordered_pairs: List[Tuple[str, Any]], -) -> Dict[str, Any]: + ordered_pairs: list[tuple[str, Any]], +) -> dict[str, Any]: d = {} for key, val in ordered_pairs: if key in d: @@ -20,7 +18,7 @@ def raise_duplicate_keys( return d -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to check.') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_merge_conflict.py b/pre_commit_hooks/check_merge_conflict.py index aed1e9ca..15ec284a 100644 --- a/pre_commit_hooks/check_merge_conflict.py +++ b/pre_commit_hooks/check_merge_conflict.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import argparse import os.path -from typing import Optional from typing import Sequence from pre_commit_hooks.util import cmd_output @@ -9,6 +10,7 @@ CONFLICT_PATTERNS = [ b'<<<<<<< ', b'======= ', + b'=======\r\n', b'=======\n', b'>>>>>>> ', ] @@ -26,7 +28,7 @@ def is_in_merge() -> bool: ) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') parser.add_argument('--assume-in-merge', action='store_true') @@ -38,12 +40,12 @@ def main(argv: Optional[Sequence[str]] = None) -> int: retcode = 0 for filename in args.filenames: with open(filename, 'rb') as inputfile: - for i, line in enumerate(inputfile): + for i, line in enumerate(inputfile, start=1): for pattern in CONFLICT_PATTERNS: if line.startswith(pattern): print( - f'Merge conflict string "{pattern.decode()}" ' - f'found in {filename}:{i + 1}', + f'{filename}:{i}: Merge conflict string ' + f'{pattern.strip().decode()!r} found', ) retcode = 1 diff --git a/pre_commit_hooks/check_shebang_scripts_are_executable.py b/pre_commit_hooks/check_shebang_scripts_are_executable.py index 50bc9c0c..0f35650b 100644 --- a/pre_commit_hooks/check_shebang_scripts_are_executable.py +++ b/pre_commit_hooks/check_shebang_scripts_are_executable.py @@ -1,18 +1,17 @@ """Check that text files with a shebang are executable.""" +from __future__ import annotations + import argparse import shlex import sys -from typing import List -from typing import Optional from typing import Sequence -from typing import Set from pre_commit_hooks.check_executables_have_shebangs import EXECUTABLE_VALUES from pre_commit_hooks.check_executables_have_shebangs import git_ls_files from pre_commit_hooks.check_executables_have_shebangs import has_shebang -def check_shebangs(paths: List[str]) -> int: +def check_shebangs(paths: list[str]) -> int: # Cannot optimize on non-executability here if we intend this check to # work on win32 -- and that's where problems caused by non-executability # (elsewhere) are most likely to arise from. @@ -20,7 +19,7 @@ def check_shebangs(paths: List[str]) -> int: def _check_git_filemode(paths: Sequence[str]) -> int: - seen: Set[str] = set() + seen: set[str] = set() for ls_file in git_ls_files(paths): is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:]) if not is_executable and has_shebang(ls_file.filename): @@ -41,7 +40,7 @@ def _message(path: str) -> None: ) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('filenames', nargs='*') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_symlinks.py b/pre_commit_hooks/check_symlinks.py index db3b4fea..a85c82a1 100644 --- a/pre_commit_hooks/check_symlinks.py +++ b/pre_commit_hooks/check_symlinks.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import argparse import os.path -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser(description='Checks for broken symlinks.') parser.add_argument('filenames', nargs='*', help='Filenames to check') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_toml.py b/pre_commit_hooks/check_toml.py index f623e688..88f70865 100644 --- a/pre_commit_hooks/check_toml.py +++ b/pre_commit_hooks/check_toml.py @@ -1,11 +1,12 @@ +from __future__ import annotations + import argparse -from typing import Optional from typing import Sequence import toml -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to check.') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_vcs_permalinks.py b/pre_commit_hooks/check_vcs_permalinks.py index 1c77b9a3..68639bd9 100644 --- a/pre_commit_hooks/check_vcs_permalinks.py +++ b/pre_commit_hooks/check_vcs_permalinks.py @@ -1,8 +1,8 @@ +from __future__ import annotations + import argparse import re import sys -from typing import List -from typing import Optional from typing import Pattern from typing import Sequence @@ -15,7 +15,7 @@ def _get_pattern(domain: str) -> Pattern[bytes]: return re.compile(regex.encode()) -def _check_filename(filename: str, patterns: List[Pattern[bytes]]) -> int: +def _check_filename(filename: str, patterns: list[Pattern[bytes]]) -> int: retv = 0 with open(filename, 'rb') as f: for i, line in enumerate(f, 1): @@ -28,7 +28,7 @@ def _check_filename(filename: str, patterns: List[Pattern[bytes]]) -> int: return retv -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') parser.add_argument( diff --git a/pre_commit_hooks/check_xml.py b/pre_commit_hooks/check_xml.py index 0fa6bb23..c256af9b 100644 --- a/pre_commit_hooks/check_xml.py +++ b/pre_commit_hooks/check_xml.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import argparse import xml.sax.handler -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='XML filenames to check.') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/check_yaml.py b/pre_commit_hooks/check_yaml.py index 5e86b73a..250794ef 100644 --- a/pre_commit_hooks/check_yaml.py +++ b/pre_commit_hooks/check_yaml.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import argparse from typing import Any from typing import Generator from typing import NamedTuple -from typing import Optional from typing import Sequence import ruamel.yaml @@ -36,7 +37,7 @@ class Key(NamedTuple): } -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( '-m', '--multi', '--allow-multiple-documents', action='store_true', diff --git a/pre_commit_hooks/debug_statement_hook.py b/pre_commit_hooks/debug_statement_hook.py index f78d6d62..9ada657a 100644 --- a/pre_commit_hooks/debug_statement_hook.py +++ b/pre_commit_hooks/debug_statement_hook.py @@ -1,9 +1,9 @@ +from __future__ import annotations + import argparse import ast import traceback -from typing import List from typing import NamedTuple -from typing import Optional from typing import Sequence @@ -29,7 +29,7 @@ class Debug(NamedTuple): class DebugStatementParser(ast.NodeVisitor): def __init__(self) -> None: - self.breakpoints: List[Debug] = [] + self.breakpoints: list[Debug] = [] def visit_Import(self, node: ast.Import) -> None: for name in node.names: @@ -65,12 +65,12 @@ def check_file(filename: str) -> int: visitor.visit(ast_obj) for bp in visitor.breakpoints: - print(f'{filename}:{bp.line}:{bp.col} - {bp.name} {bp.reason}') + print(f'{filename}:{bp.line}:{bp.col}: {bp.name} {bp.reason}') return int(bool(visitor.breakpoints)) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to run') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/destroyed_symlinks.py b/pre_commit_hooks/destroyed_symlinks.py index a3f122ff..88253c0b 100644 --- a/pre_commit_hooks/destroyed_symlinks.py +++ b/pre_commit_hooks/destroyed_symlinks.py @@ -1,8 +1,8 @@ +from __future__ import annotations + import argparse import shlex import subprocess -from typing import List -from typing import Optional from typing import Sequence from pre_commit_hooks.util import cmd_output @@ -13,8 +13,8 @@ PERMS_NONEXIST = '000000' -def find_destroyed_symlinks(files: Sequence[str]) -> List[str]: - destroyed_links: List[str] = [] +def find_destroyed_symlinks(files: Sequence[str]) -> list[str]: + destroyed_links: list[str] = [] if not files: return destroyed_links for line in zsplit( @@ -66,7 +66,7 @@ def find_destroyed_symlinks(files: Sequence[str]) -> List[str]: return destroyed_links -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to check.') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/detect_aws_credentials.py b/pre_commit_hooks/detect_aws_credentials.py index ba1d789a..4f59d9cf 100644 --- a/pre_commit_hooks/detect_aws_credentials.py +++ b/pre_commit_hooks/detect_aws_credentials.py @@ -1,11 +1,10 @@ +from __future__ import annotations + import argparse import configparser import os -from typing import List from typing import NamedTuple -from typing import Optional from typing import Sequence -from typing import Set class BadFile(NamedTuple): @@ -13,7 +12,7 @@ class BadFile(NamedTuple): key: str -def get_aws_cred_files_from_env() -> Set[str]: +def get_aws_cred_files_from_env() -> set[str]: """Extract credential file paths from environment variables.""" return { os.environ[env_var] @@ -25,7 +24,7 @@ def get_aws_cred_files_from_env() -> Set[str]: } -def get_aws_secrets_from_env() -> Set[str]: +def get_aws_secrets_from_env() -> set[str]: """Extract AWS secrets from environment variables.""" keys = set() for env_var in ( @@ -36,7 +35,7 @@ def get_aws_secrets_from_env() -> Set[str]: return keys -def get_aws_secrets_from_file(credentials_file: str) -> Set[str]: +def get_aws_secrets_from_file(credentials_file: str) -> set[str]: """Extract AWS secrets from configuration files. Read an ini-style configuration file and return a set with all found AWS @@ -69,8 +68,8 @@ def get_aws_secrets_from_file(credentials_file: str) -> Set[str]: def check_file_for_aws_keys( filenames: Sequence[str], - keys: Set[bytes], -) -> List[BadFile]: + keys: set[bytes], +) -> list[BadFile]: """Check if files contain AWS secrets. Return a list of all files containing AWS secrets and keys found, with all @@ -90,7 +89,7 @@ def check_file_for_aws_keys( return bad_files -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='+', help='Filenames to run') parser.add_argument( @@ -119,7 +118,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: # of files to to gather AWS secrets from. credential_files |= get_aws_cred_files_from_env() - keys: Set[str] = set() + keys: set[str] = set() for credential_file in credential_files: keys |= get_aws_secrets_from_file(credential_file) diff --git a/pre_commit_hooks/detect_private_key.py b/pre_commit_hooks/detect_private_key.py index 18f95394..cd51f901 100644 --- a/pre_commit_hooks/detect_private_key.py +++ b/pre_commit_hooks/detect_private_key.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import argparse -from typing import Optional from typing import Sequence BLACKLIST = [ @@ -16,7 +17,7 @@ ] -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to check') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/end_of_file_fixer.py b/pre_commit_hooks/end_of_file_fixer.py index 40e8821d..a30dce92 100644 --- a/pre_commit_hooks/end_of_file_fixer.py +++ b/pre_commit_hooks/end_of_file_fixer.py @@ -1,7 +1,8 @@ +from __future__ import annotations + import argparse import os from typing import IO -from typing import Optional from typing import Sequence @@ -48,7 +49,7 @@ def fix_file(file_obj: IO[bytes]) -> int: return 0 -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to fix') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/file_contents_sorter.py b/pre_commit_hooks/file_contents_sorter.py index 392e2261..c5691f0b 100644 --- a/pre_commit_hooks/file_contents_sorter.py +++ b/pre_commit_hooks/file_contents_sorter.py @@ -9,12 +9,13 @@ this hook on that file should reduce the instances of git merge conflicts and keep the file nicely ordered. """ +from __future__ import annotations + import argparse from typing import Any from typing import Callable from typing import IO from typing import Iterable -from typing import Optional from typing import Sequence PASS = 0 @@ -23,7 +24,7 @@ def sort_file_contents( f: IO[bytes], - key: Optional[Callable[[bytes], Any]], + key: Callable[[bytes], Any] | None, *, unique: bool = False, ) -> int: @@ -47,7 +48,7 @@ def sort_file_contents( return FAIL -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='+', help='Files to sort') parser.add_argument( diff --git a/pre_commit_hooks/fix_byte_order_marker.py b/pre_commit_hooks/fix_byte_order_marker.py index 51b94e16..22a49909 100644 --- a/pre_commit_hooks/fix_byte_order_marker.py +++ b/pre_commit_hooks/fix_byte_order_marker.py @@ -1,9 +1,10 @@ +from __future__ import annotations + import argparse -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to check') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/fix_encoding_pragma.py b/pre_commit_hooks/fix_encoding_pragma.py index c704774f..60c71eeb 100644 --- a/pre_commit_hooks/fix_encoding_pragma.py +++ b/pre_commit_hooks/fix_encoding_pragma.py @@ -1,7 +1,8 @@ +from __future__ import annotations + import argparse from typing import IO from typing import NamedTuple -from typing import Optional from typing import Sequence DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-' @@ -26,7 +27,7 @@ class ExpectedContents(NamedTuple): # True: has exactly the coding pragma expected # False: missing coding pragma entirely # None: has a coding pragma, but it does not match - pragma_status: Optional[bool] + pragma_status: bool | None ending: bytes @property @@ -55,7 +56,7 @@ def _get_expected_contents( rest = second_line + rest if potential_coding.rstrip(b'\r\n') == expected_pragma: - pragma_status: Optional[bool] = True + pragma_status: bool | None = True elif has_coding(potential_coding): pragma_status = None else: @@ -105,7 +106,7 @@ def _normalize_pragma(pragma: str) -> bytes: return pragma.encode().rstrip() -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser( 'Fixes the encoding pragma of python files', ) diff --git a/pre_commit_hooks/forbid_new_submodules.py b/pre_commit_hooks/forbid_new_submodules.py index 02758089..b806cad2 100644 --- a/pre_commit_hooks/forbid_new_submodules.py +++ b/pre_commit_hooks/forbid_new_submodules.py @@ -1,12 +1,13 @@ +from __future__ import annotations + import argparse import os -from typing import Optional from typing import Sequence from pre_commit_hooks.util import cmd_output -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/mixed_line_ending.py b/pre_commit_hooks/mixed_line_ending.py index 4e07ed96..0328e865 100644 --- a/pre_commit_hooks/mixed_line_ending.py +++ b/pre_commit_hooks/mixed_line_ending.py @@ -1,7 +1,7 @@ +from __future__ import annotations + import argparse import collections -from typing import Dict -from typing import Optional from typing import Sequence @@ -25,7 +25,7 @@ def fix_filename(filename: str, fix: str) -> int: with open(filename, 'rb') as f: contents = f.read() - counts: Dict[bytes, int] = collections.defaultdict(int) + counts: dict[bytes, int] = collections.defaultdict(int) for line in contents.splitlines(True): for ending in ALL_ENDINGS: @@ -62,7 +62,7 @@ def fix_filename(filename: str, fix: str) -> int: return other_endings -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( '-f', '--fix', diff --git a/pre_commit_hooks/no_commit_to_branch.py b/pre_commit_hooks/no_commit_to_branch.py index db848507..741f7267 100644 --- a/pre_commit_hooks/no_commit_to_branch.py +++ b/pre_commit_hooks/no_commit_to_branch.py @@ -1,7 +1,8 @@ +from __future__ import annotations + import argparse import re from typing import AbstractSet -from typing import Optional from typing import Sequence from pre_commit_hooks.util import CalledProcessError @@ -23,7 +24,7 @@ def is_on_branch( ) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( '-b', '--branch', action='append', diff --git a/pre_commit_hooks/pretty_format_json.py b/pre_commit_hooks/pretty_format_json.py index 33ad5a1c..627a11cc 100644 --- a/pre_commit_hooks/pretty_format_json.py +++ b/pre_commit_hooks/pretty_format_json.py @@ -1,13 +1,11 @@ +from __future__ import annotations + import argparse import json import sys from difflib import unified_diff -from typing import List from typing import Mapping -from typing import Optional from typing import Sequence -from typing import Tuple -from typing import Union def _get_pretty_format( @@ -17,7 +15,7 @@ def _get_pretty_format( sort_keys: bool = True, top_keys: Sequence[str] = (), ) -> str: - def pairs_first(pairs: Sequence[Tuple[str, str]]) -> Mapping[str, str]: + def pairs_first(pairs: Sequence[tuple[str, str]]) -> Mapping[str, str]: before = [pair for pair in pairs if pair[0] in top_keys] before = sorted(before, key=lambda x: top_keys.index(x[0])) after = [pair for pair in pairs if pair[0] not in top_keys] @@ -38,7 +36,7 @@ def _autofix(filename: str, new_contents: str) -> None: f.write(new_contents) -def parse_num_to_int(s: str) -> Union[int, str]: +def parse_num_to_int(s: str) -> int | str: """Convert string numbers to int, leaving strings as is.""" try: return int(s) @@ -46,7 +44,7 @@ def parse_num_to_int(s: str) -> Union[int, str]: return s -def parse_topkeys(s: str) -> List[str]: +def parse_topkeys(s: str) -> list[str]: return s.split(',') @@ -57,7 +55,7 @@ def get_diff(source: str, target: str, file: str) -> str: return ''.join(diff) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( '--autofix', diff --git a/pre_commit_hooks/removed.py b/pre_commit_hooks/removed.py index 236cbf83..6f6c7b72 100644 --- a/pre_commit_hooks/removed.py +++ b/pre_commit_hooks/removed.py @@ -1,9 +1,10 @@ +from __future__ import annotations + import sys -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: argv = argv if argv is not None else sys.argv[1:] hookid, new_hookid, url = argv[:3] raise SystemExit( diff --git a/pre_commit_hooks/requirements_txt_fixer.py b/pre_commit_hooks/requirements_txt_fixer.py index 63f891f7..58843940 100644 --- a/pre_commit_hooks/requirements_txt_fixer.py +++ b/pre_commit_hooks/requirements_txt_fixer.py @@ -1,8 +1,8 @@ +from __future__ import annotations + import argparse import re from typing import IO -from typing import List -from typing import Optional from typing import Sequence @@ -15,8 +15,8 @@ class Requirement: UNTIL_SEP = re.compile(rb'[^;\s]+') def __init__(self) -> None: - self.value: Optional[bytes] = None - self.comments: List[bytes] = [] + self.value: bytes | None = None + self.comments: list[bytes] = [] @property def name(self) -> bytes: @@ -36,7 +36,7 @@ def name(self) -> bytes: return name[:m.start()] - def __lt__(self, requirement: 'Requirement') -> bool: + def __lt__(self, requirement: Requirement) -> bool: # \n means top of file comment, so always return True, # otherwise just do a string comparison with value. assert self.value is not None, self.value @@ -61,9 +61,9 @@ def append_value(self, value: bytes) -> None: def fix_requirements(f: IO[bytes]) -> int: - requirements: List[Requirement] = [] + requirements: list[Requirement] = [] before = list(f) - after: List[bytes] = [] + after: list[bytes] = [] before_string = b''.join(before) @@ -130,7 +130,7 @@ def fix_requirements(f: IO[bytes]) -> int: return FAIL -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to fix') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/sort_simple_yaml.py b/pre_commit_hooks/sort_simple_yaml.py index 39f683e4..116b5c19 100644 --- a/pre_commit_hooks/sort_simple_yaml.py +++ b/pre_commit_hooks/sort_simple_yaml.py @@ -17,16 +17,16 @@ In other words, we don't sort deeper than the top layer, and might corrupt complicated YAML files. """ +from __future__ import annotations + import argparse -from typing import List -from typing import Optional from typing import Sequence QUOTES = ["'", '"'] -def sort(lines: List[str]) -> List[str]: +def sort(lines: list[str]) -> list[str]: """Sort a YAML file in alphabetical order, keeping blocks together. :param lines: array of strings (without newlines) @@ -44,7 +44,7 @@ def sort(lines: List[str]) -> List[str]: return new_lines -def parse_block(lines: List[str], header: bool = False) -> List[str]: +def parse_block(lines: list[str], header: bool = False) -> list[str]: """Parse and return a single block, popping off the start of `lines`. If parsing a header block, we stop after we reach a line that is not a @@ -60,7 +60,7 @@ def parse_block(lines: List[str], header: bool = False) -> List[str]: return block_lines -def parse_blocks(lines: List[str]) -> List[List[str]]: +def parse_blocks(lines: list[str]) -> list[list[str]]: """Parse and return all possible blocks, popping off the start of `lines`. :param lines: list of lines @@ -77,7 +77,7 @@ def parse_blocks(lines: List[str]) -> List[List[str]]: return blocks -def first_key(lines: List[str]) -> str: +def first_key(lines: list[str]) -> str: """Returns a string representing the sort key of a block. The sort key is the first YAML key we encounter, ignoring comments, and @@ -99,7 +99,7 @@ def first_key(lines: List[str]) -> str: return '' # not actually reached in reality -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to fix') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/string_fixer.py b/pre_commit_hooks/string_fixer.py index a08a5f76..0ef9bc7a 100644 --- a/pre_commit_hooks/string_fixer.py +++ b/pre_commit_hooks/string_fixer.py @@ -1,9 +1,9 @@ +from __future__ import annotations + import argparse import io import re import tokenize -from typing import List -from typing import Optional from typing import Sequence START_QUOTE_RE = re.compile('^[a-zA-Z]*"') @@ -24,7 +24,7 @@ def handle_match(token_text: str) -> str: return token_text -def get_line_offsets_by_line_no(src: str) -> List[int]: +def get_line_offsets_by_line_no(src: str) -> list[int]: # Padded so we can index with line number offsets = [-1, 0] for line in src.splitlines(True): @@ -60,7 +60,7 @@ def fix_strings(filename: str) -> int: return 0 -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help='Filenames to fix') args = parser.parse_args(argv) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index bffb0c42..e1ffe367 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -1,11 +1,12 @@ +from __future__ import annotations + import argparse import os.path import re -from typing import Optional from typing import Sequence -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') parser.add_argument( diff --git a/pre_commit_hooks/trailing_whitespace_fixer.py b/pre_commit_hooks/trailing_whitespace_fixer.py index 82faa2dc..84f50671 100644 --- a/pre_commit_hooks/trailing_whitespace_fixer.py +++ b/pre_commit_hooks/trailing_whitespace_fixer.py @@ -1,13 +1,14 @@ +from __future__ import annotations + import argparse import os -from typing import Optional from typing import Sequence def _fix_file( filename: str, is_markdown: bool, - chars: Optional[bytes], + chars: bytes | None, ) -> bool: with open(filename, mode='rb') as file_processed: lines = file_processed.readlines() @@ -24,7 +25,7 @@ def _fix_file( def _process_line( line: bytes, is_markdown: bool, - chars: Optional[bytes], + chars: bytes | None, ) -> bytes: if line[-2:] == b'\r\n': eol = b'\r\n' @@ -40,7 +41,7 @@ def _process_line( return line.rstrip(chars) + eol -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( '--no-markdown-linebreak-ext', diff --git a/pre_commit_hooks/util.py b/pre_commit_hooks/util.py index 402e33e6..d6c90ae0 100644 --- a/pre_commit_hooks/util.py +++ b/pre_commit_hooks/util.py @@ -1,20 +1,19 @@ +from __future__ import annotations + import subprocess from typing import Any -from typing import List -from typing import Optional -from typing import Set class CalledProcessError(RuntimeError): pass -def added_files() -> Set[str]: +def added_files() -> set[str]: cmd = ('git', 'diff', '--staged', '--name-only', '--diff-filter=A') return set(cmd_output(*cmd).splitlines()) -def cmd_output(*cmd: str, retcode: Optional[int] = 0, **kwargs: Any) -> str: +def cmd_output(*cmd: str, retcode: int | None = 0, **kwargs: Any) -> str: kwargs.setdefault('stdout', subprocess.PIPE) kwargs.setdefault('stderr', subprocess.PIPE) proc = subprocess.Popen(cmd, **kwargs) @@ -25,7 +24,7 @@ def cmd_output(*cmd: str, retcode: Optional[int] = 0, **kwargs: Any) -> str: return stdout -def zsplit(s: str) -> List[str]: +def zsplit(s: str) -> list[str]: s = s.strip('\0') if s: return s.split('\0') diff --git a/setup.cfg b/setup.cfg index 45498ebe..8247f311 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pre_commit_hooks -version = 4.1.0 +version = 4.2.0 description = Some out-of-the-box hooks for pre-commit. long_description = file: README.md long_description_content_type = text/markdown @@ -13,7 +13,6 @@ classifiers = License :: OSI Approved :: MIT License Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -26,7 +25,7 @@ packages = find: install_requires = ruamel.yaml>=0.15 toml -python_requires = >=3.6.1 +python_requires = >=3.7 [options.packages.find] exclude = diff --git a/setup.py b/setup.py index 8bf1ba93..3d93aefb 100644 --- a/setup.py +++ b/setup.py @@ -1,2 +1,4 @@ +from __future__ import annotations + from setuptools import setup setup() diff --git a/testing/util.py b/testing/util.py index 50437544..2bbbe644 100644 --- a/testing/util.py +++ b/testing/util.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os.path import subprocess diff --git a/tests/check_added_large_files_test.py b/tests/check_added_large_files_test.py index c16bf5a9..54c4e689 100644 --- a/tests/check_added_large_files_test.py +++ b/tests/check_added_large_files_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import shutil import pytest diff --git a/tests/check_ast_test.py b/tests/check_ast_test.py index 686fd116..62439661 100644 --- a/tests/check_ast_test.py +++ b/tests/check_ast_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks.check_ast import main from testing.util import get_resource_path diff --git a/tests/check_builtin_literals_test.py b/tests/check_builtin_literals_test.py index e9367989..1b182573 100644 --- a/tests/check_builtin_literals_test.py +++ b/tests/check_builtin_literals_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import ast import pytest diff --git a/tests/check_byte_order_marker_test.py b/tests/check_byte_order_marker_test.py index 4c402476..909a39bb 100644 --- a/tests/check_byte_order_marker_test.py +++ b/tests/check_byte_order_marker_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks import check_byte_order_marker diff --git a/tests/check_case_conflict_test.py b/tests/check_case_conflict_test.py index d9211b57..a914f452 100644 --- a/tests/check_case_conflict_test.py +++ b/tests/check_case_conflict_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import pytest diff --git a/tests/check_docstring_first_test.py b/tests/check_docstring_first_test.py index ed5c08ef..8bafae82 100644 --- a/tests/check_docstring_first_test.py +++ b/tests/check_docstring_first_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.check_docstring_first import check_docstring_first @@ -15,7 +17,7 @@ b'from __future__ import unicode_literals\n' b'"foo"\n', 1, - '{filename}:2 Module docstring appears after code ' + '{filename}:2: Module docstring appears after code ' '(code seen on line 1).\n', ), # Test double docstring @@ -24,7 +26,7 @@ b'from __future__ import absolute_import\n' b'"fake docstring"\n', 1, - '{filename}:3 Multiple module docstrings ' + '{filename}:3: Multiple module docstrings ' '(first docstring on line 1).\n', ), # Test multiple lines of code above @@ -33,7 +35,7 @@ b'import sys\n' b'"docstring"\n', 1, - '{filename}:3 Module docstring appears after code ' + '{filename}:3: Module docstring appears after code ' '(code seen on line 1).\n', ), # String literals in expressions are ok. diff --git a/tests/check_executables_have_shebangs_test.py b/tests/check_executables_have_shebangs_test.py index 5703eded..82d03e3d 100644 --- a/tests/check_executables_have_shebangs_test.py +++ b/tests/check_executables_have_shebangs_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys diff --git a/tests/check_json_test.py b/tests/check_json_test.py index 3ec67f1c..53e1f52d 100644 --- a/tests/check_json_test.py +++ b/tests/check_json_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.check_json import main diff --git a/tests/check_merge_conflict_test.py b/tests/check_merge_conflict_test.py index 79c1b118..76c4283c 100644 --- a/tests/check_merge_conflict_test.py +++ b/tests/check_merge_conflict_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import shutil @@ -99,8 +101,14 @@ def repository_pending_merge(tmpdir): @pytest.mark.usefixtures('f1_is_a_conflict_file') -def test_merge_conflicts_git(): +def test_merge_conflicts_git(capsys): assert main(['f1']) == 1 + out, _ = capsys.readouterr() + assert out == ( + "f1:1: Merge conflict string '<<<<<<<' found\n" + "f1:3: Merge conflict string '=======' found\n" + "f1:5: Merge conflict string '>>>>>>>' found\n" + ) @pytest.mark.parametrize( @@ -137,7 +145,7 @@ def test_care_when_assumed_merge(tmpdir): assert main([str(f.realpath()), '--assume-in-merge']) == 1 -def test_worktree_merge_conflicts(f1_is_a_conflict_file, tmpdir): +def test_worktree_merge_conflicts(f1_is_a_conflict_file, tmpdir, capsys): worktree = tmpdir.join('worktree') cmd_output('git', 'worktree', 'add', str(worktree)) with worktree.as_cwd(): @@ -146,4 +154,4 @@ def test_worktree_merge_conflicts(f1_is_a_conflict_file, tmpdir): ) msg = f1_is_a_conflict_file.join('.git/worktrees/worktree/MERGE_MSG') assert msg.exists() - test_merge_conflicts_git() + test_merge_conflicts_git(capsys) diff --git a/tests/check_shebang_scripts_are_executable_test.py b/tests/check_shebang_scripts_are_executable_test.py index 9e78b06c..e4bd07ca 100644 --- a/tests/check_shebang_scripts_are_executable_test.py +++ b/tests/check_shebang_scripts_are_executable_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pytest diff --git a/tests/check_symlinks_test.py b/tests/check_symlinks_test.py index 07c11687..e2c2c78f 100644 --- a/tests/check_symlinks_test.py +++ b/tests/check_symlinks_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pytest diff --git a/tests/check_toml_test.py b/tests/check_toml_test.py index c7251eb0..d594f815 100644 --- a/tests/check_toml_test.py +++ b/tests/check_toml_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks.check_toml import main diff --git a/tests/check_vcs_permalinks_test.py b/tests/check_vcs_permalinks_test.py index ad591515..01ce94de 100644 --- a/tests/check_vcs_permalinks_test.py +++ b/tests/check_vcs_permalinks_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks.check_vcs_permalinks import main diff --git a/tests/check_xml_test.py b/tests/check_xml_test.py index 357bad64..767619f1 100644 --- a/tests/check_xml_test.py +++ b/tests/check_xml_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.check_xml import main diff --git a/tests/check_yaml_test.py b/tests/check_yaml_test.py index 1a017a12..54eb16e8 100644 --- a/tests/check_yaml_test.py +++ b/tests/check_yaml_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.check_yaml import main diff --git a/tests/conftest.py b/tests/conftest.py index f92cfc18..807f15b8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.util import cmd_output diff --git a/tests/debug_statement_hook_test.py b/tests/debug_statement_hook_test.py index 428421a0..5a8e0bb2 100644 --- a/tests/debug_statement_hook_test.py +++ b/tests/debug_statement_hook_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import ast from pre_commit_hooks.debug_statement_hook import Debug @@ -53,7 +55,9 @@ def test_non_utf8_file(tmpdir): assert main((str(f_py),)) == 0 -def test_py37_breakpoint(tmpdir): +def test_py37_breakpoint(tmpdir, capsys): f_py = tmpdir.join('f.py') f_py.write('def f():\n breakpoint()\n') assert main((str(f_py),)) == 1 + out, _ = capsys.readouterr() + assert out == f'{f_py}:2:4: breakpoint called\n' diff --git a/tests/destroyed_symlinks_test.py b/tests/destroyed_symlinks_test.py index cde06cfb..39c474a1 100644 --- a/tests/destroyed_symlinks_test.py +++ b/tests/destroyed_symlinks_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import subprocess diff --git a/tests/detect_aws_credentials_test.py b/tests/detect_aws_credentials_test.py index 72125099..afda47a9 100644 --- a/tests/detect_aws_credentials_test.py +++ b/tests/detect_aws_credentials_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from unittest.mock import patch import pytest diff --git a/tests/detect_private_key_test.py b/tests/detect_private_key_test.py index d2c724f0..41f8bae5 100644 --- a/tests/detect_private_key_test.py +++ b/tests/detect_private_key_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.detect_private_key import main diff --git a/tests/end_of_file_fixer_test.py b/tests/end_of_file_fixer_test.py index 60b9e82f..8a5d889e 100644 --- a/tests/end_of_file_fixer_test.py +++ b/tests/end_of_file_fixer_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import pytest diff --git a/tests/file_contents_sorter_test.py b/tests/file_contents_sorter_test.py index 15f11342..5e79e401 100644 --- a/tests/file_contents_sorter_test.py +++ b/tests/file_contents_sorter_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.file_contents_sorter import FAIL diff --git a/tests/fix_byte_order_marker_test.py b/tests/fix_byte_order_marker_test.py index da150e37..d7a65990 100644 --- a/tests/fix_byte_order_marker_test.py +++ b/tests/fix_byte_order_marker_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks import fix_byte_order_marker diff --git a/tests/fix_encoding_pragma_test.py b/tests/fix_encoding_pragma_test.py index f3afa094..98557e98 100644 --- a/tests/fix_encoding_pragma_test.py +++ b/tests/fix_encoding_pragma_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import pytest diff --git a/tests/forbid_new_submodules_test.py b/tests/forbid_new_submodules_test.py index 0326d941..058a3294 100644 --- a/tests/forbid_new_submodules_test.py +++ b/tests/forbid_new_submodules_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import subprocess from unittest import mock diff --git a/tests/mixed_line_ending_test.py b/tests/mixed_line_ending_test.py index f1c26418..a7e79719 100644 --- a/tests/mixed_line_ending_test.py +++ b/tests/mixed_line_ending_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.mixed_line_ending import main diff --git a/tests/no_commit_to_branch_test.py b/tests/no_commit_to_branch_test.py index 9fcb5808..eaae5e62 100644 --- a/tests/no_commit_to_branch_test.py +++ b/tests/no_commit_to_branch_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.no_commit_to_branch import is_on_branch diff --git a/tests/pretty_format_json_test.py b/tests/pretty_format_json_test.py index 7fda23b3..5ded724a 100644 --- a/tests/pretty_format_json_test.py +++ b/tests/pretty_format_json_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import shutil diff --git a/tests/readme_test.py b/tests/readme_test.py index 7df7fcfe..038868d8 100644 --- a/tests/readme_test.py +++ b/tests/readme_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks.check_yaml import yaml diff --git a/tests/removed_test.py b/tests/removed_test.py index d635eb1e..cd669578 100644 --- a/tests/removed_test.py +++ b/tests/removed_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.removed import main diff --git a/tests/requirements_txt_fixer_test.py b/tests/requirements_txt_fixer_test.py index e3c6ed50..b725afa2 100644 --- a/tests/requirements_txt_fixer_test.py +++ b/tests/requirements_txt_fixer_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.requirements_txt_fixer import FAIL diff --git a/tests/sort_simple_yaml_test.py b/tests/sort_simple_yaml_test.py index a682c158..6cbda857 100644 --- a/tests/sort_simple_yaml_test.py +++ b/tests/sort_simple_yaml_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pytest diff --git a/tests/string_fixer_test.py b/tests/string_fixer_test.py index 6ddb0ac8..9dd73152 100644 --- a/tests/string_fixer_test.py +++ b/tests/string_fixer_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import textwrap import pytest diff --git a/tests/tests_should_end_in_test_test.py b/tests/tests_should_end_in_test_test.py index 4df2963f..dc3744b8 100644 --- a/tests/tests_should_end_in_test_test.py +++ b/tests/tests_should_end_in_test_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pre_commit_hooks.tests_should_end_in_test import main diff --git a/tests/trailing_whitespace_fixer_test.py b/tests/trailing_whitespace_fixer_test.py index bb3b62d4..c07497a2 100644 --- a/tests/trailing_whitespace_fixer_test.py +++ b/tests/trailing_whitespace_fixer_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.trailing_whitespace_fixer import main diff --git a/tests/util_test.py b/tests/util_test.py index 7f488161..92473e59 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from pre_commit_hooks.util import CalledProcessError diff --git a/tox.ini b/tox.ini index 965eba92..cb2b92ab 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38,pypy3,pre-commit +envlist = py37,py38,pypy3,pre-commit [testenv] deps = -rrequirements-dev.txt @@ -11,7 +11,7 @@ setenv = commands = coverage erase coverage run -m pytest {posargs:tests} - coverage report --fail-under 100 + coverage report [testenv:pre-commit] skip_install = true