mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2026-03-29 10:16:52 +00:00
Merge branch 'main' into empty-object-with-newline
This commit is contained in:
commit
a23cc57a37
44 changed files with 321 additions and 97 deletions
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
|
|
@ -8,12 +8,12 @@ on:
|
|||
|
||||
jobs:
|
||||
main-windows:
|
||||
uses: asottile/workflows/.github/workflows/tox.yml@v1.5.0
|
||||
uses: asottile/workflows/.github/workflows/tox.yml@v1.8.1
|
||||
with:
|
||||
env: '["py38"]'
|
||||
env: '["py39"]'
|
||||
os: windows-latest
|
||||
main-linux:
|
||||
uses: asottile/workflows/.github/workflows/tox.yml@v1.5.0
|
||||
uses: asottile/workflows/.github/workflows/tox.yml@v1.8.1
|
||||
with:
|
||||
env: '["py38", "py39", "py310", "py311"]'
|
||||
env: '["py39", "py310", "py311", "py312"]'
|
||||
os: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
|
@ -10,33 +10,32 @@ repos:
|
|||
- id: name-tests-test
|
||||
- id: requirements-txt-fixer
|
||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||
rev: v2.4.0
|
||||
rev: v2.7.0
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
- repo: https://github.com/asottile/reorder-python-imports
|
||||
rev: v3.10.0
|
||||
rev: v3.14.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args: [--py38-plus, --add-import, 'from __future__ import annotations']
|
||||
args: [--py39-plus, --add-import, 'from __future__ import annotations']
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v3.0.1
|
||||
rev: v3.1.0
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.10.1
|
||||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||
rev: v2.0.2
|
||||
args: [--py39-plus]
|
||||
- repo: https://github.com/hhatto/autopep8
|
||||
rev: v2.3.2
|
||||
hooks:
|
||||
- id: autopep8
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.1.0
|
||||
rev: 7.1.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.4.1
|
||||
rev: v1.15.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies: [types-all]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
description: prevents giant files from being committed.
|
||||
entry: check-added-large-files
|
||||
language: python
|
||||
stages: [commit, push, manual]
|
||||
stages: [pre-commit, pre-push, manual]
|
||||
minimum_pre_commit_version: 3.2.0
|
||||
- id: check-ast
|
||||
name: check python ast
|
||||
description: simply checks whether the files parse as valid python.
|
||||
|
|
@ -39,7 +40,13 @@
|
|||
entry: check-executables-have-shebangs
|
||||
language: python
|
||||
types: [text, executable]
|
||||
stages: [commit, push, manual]
|
||||
stages: [pre-commit, pre-push, manual]
|
||||
minimum_pre_commit_version: 3.2.0
|
||||
- id: check-illegal-windows-names
|
||||
name: check illegal windows names
|
||||
entry: Illegal Windows filenames detected
|
||||
language: fail
|
||||
files: '(?i)((^|/)(CON|PRN|AUX|NUL|COM[\d¹²³]|LPT[\d¹²³])(\.|/|$)|[<>:\"\\|?*\x00-\x1F]|/[^/]*[\.\s]/|[^/]*[\.\s]$)'
|
||||
- id: check-json
|
||||
name: check json
|
||||
description: checks json files for parseable syntax.
|
||||
|
|
@ -52,7 +59,8 @@
|
|||
entry: check-shebang-scripts-are-executable
|
||||
language: python
|
||||
types: [text]
|
||||
stages: [commit, push, manual]
|
||||
stages: [pre-commit, pre-push, manual]
|
||||
minimum_pre_commit_version: 3.2.0
|
||||
- id: pretty-format-json
|
||||
name: pretty format json
|
||||
description: sets a standard for formatting json files.
|
||||
|
|
@ -107,6 +115,7 @@
|
|||
entry: destroyed-symlinks
|
||||
language: python
|
||||
types: [file]
|
||||
stages: [pre-commit, pre-push, manual]
|
||||
- id: detect-aws-credentials
|
||||
name: detect aws credentials
|
||||
description: detects *your* aws credentials from the aws cli credentials file.
|
||||
|
|
@ -131,7 +140,8 @@
|
|||
entry: end-of-file-fixer
|
||||
language: python
|
||||
types: [text]
|
||||
stages: [commit, push, manual]
|
||||
stages: [pre-commit, pre-push, manual]
|
||||
minimum_pre_commit_version: 3.2.0
|
||||
- id: file-contents-sorter
|
||||
name: file contents sorter
|
||||
description: sorts the lines in specified files (defaults to alphabetical). you must provide list of target files as input in your .pre-commit-config.yaml file.
|
||||
|
|
@ -145,7 +155,7 @@
|
|||
language: python
|
||||
types: [text]
|
||||
- id: fix-encoding-pragma
|
||||
name: fix python encoding pragma
|
||||
name: fix python encoding pragma (deprecated)
|
||||
description: 'adds # -*- coding: utf-8 -*- to the top of python files.'
|
||||
language: python
|
||||
entry: fix-encoding-pragma
|
||||
|
|
@ -198,4 +208,5 @@
|
|||
entry: trailing-whitespace-fixer
|
||||
language: python
|
||||
types: [text]
|
||||
stages: [commit, push, manual]
|
||||
stages: [pre-commit, pre-push, manual]
|
||||
minimum_pre_commit_version: 3.2.0
|
||||
|
|
|
|||
62
CHANGELOG.md
62
CHANGELOG.md
|
|
@ -1,3 +1,65 @@
|
|||
5.0.0 - 2024-10-05
|
||||
==================
|
||||
|
||||
### Features
|
||||
- `requirements-txt-fixer`: also remove `pkg_resources==...`.
|
||||
- #850 PR by @ericfrederich.
|
||||
- #1030 issue by @ericfrederich.
|
||||
- `check-illegal-windows-names`: new hook!
|
||||
- #1044 PR by @ericfrederich.
|
||||
- #589 issue by @ericfrederich.
|
||||
- #1049 PR by @Jeffrey-Lim.
|
||||
- `pretty-format-json`: continue processing even if a file has a json error.
|
||||
- #1039 PR by @amarvin.
|
||||
- #1038 issue by @amarvin.
|
||||
|
||||
### Fixes
|
||||
- `destroyed-symlinks`: set `stages` to `[pre-commit, pre-push, manual]`
|
||||
- PR #1085 by @AdrianDC.
|
||||
|
||||
### Migrating
|
||||
- pre-commit-hooks now requires `pre-commit>=3.2.0`.
|
||||
- use non-deprecated names for `stages`.
|
||||
- #1093 PR by @asottile.
|
||||
|
||||
4.6.0 - 2024-04-06
|
||||
==================
|
||||
|
||||
### Features
|
||||
- `requirements-txt-fixer`: remove duplicate packages.
|
||||
- #1014 PR by @vhoulbreque-withings.
|
||||
- #960 issue @csibe17.
|
||||
|
||||
### Migrating
|
||||
- `fix-encoding-pragma`: deprecated -- will be removed in 5.0.0. use
|
||||
[pyupgrade](https://github.com/asottile/pyupgrade) or some other tool.
|
||||
- #1033 PR by @mxr.
|
||||
- #1032 issue by @mxr.
|
||||
|
||||
4.5.0 - 2023-10-07
|
||||
==================
|
||||
|
||||
### Features
|
||||
- `requirements-txt-fixer`: also sort `constraints.txt` by default.
|
||||
- #857 PR by @lev-blit.
|
||||
- #830 issue by @PLPeeters.
|
||||
- `debug-statements`: add `bpdb` debugger.
|
||||
- #942 PR by @mwip.
|
||||
- #941 issue by @mwip.
|
||||
|
||||
### Fixes
|
||||
- `file-contents-sorter`: fix sorting an empty file.
|
||||
- #944 PR by @RoelAdriaans.
|
||||
- #935 issue by @paduszyk.
|
||||
- `double-quote-string-fixer`: don't rewrite inside f-strings in 3.12+.
|
||||
- #973 PR by @asottile.
|
||||
- #971 issue by @XuehaiPan.
|
||||
|
||||
## Migrating
|
||||
- now requires python >= 3.8.
|
||||
- #926 PR by @asottile.
|
||||
- #927 PR by @asottile.
|
||||
|
||||
4.4.0 - 2022-11-23
|
||||
==================
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Add this to your `.pre-commit-config.yaml`
|
|||
|
||||
```yaml
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0 # Use the ref you want to point at
|
||||
rev: v5.0.0 # Use the ref you want to point at
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
# - id: ...
|
||||
|
|
@ -51,6 +51,9 @@ Checks for a common error of placing code before the docstring.
|
|||
#### `check-executables-have-shebangs`
|
||||
Checks that non-binary executables have a proper shebang.
|
||||
|
||||
#### `check-illegal-windows-names`
|
||||
Check for files that cannot be created on Windows.
|
||||
|
||||
#### `check-json`
|
||||
Attempts to load all json files to verify syntax.
|
||||
|
||||
|
|
@ -117,6 +120,7 @@ Makes sure files end in a newline and only a newline.
|
|||
Sort the lines in specified files (defaults to alphabetical).
|
||||
You must provide the target [`files`](https://pre-commit.com/#config-files) as input.
|
||||
Note that this hook WILL remove blank lines and does NOT respect any comments.
|
||||
All newlines will be converted to line feeds (`\n`).
|
||||
|
||||
The following arguments are available:
|
||||
- `--ignore-case` - fold lower case to upper case characters.
|
||||
|
|
@ -126,6 +130,9 @@ The following arguments are available:
|
|||
removes UTF-8 byte order marker
|
||||
|
||||
#### `fix-encoding-pragma`
|
||||
|
||||
_Deprecated since py2 is EOL - use [pyupgrade](https://github.com/asottile/pyupgrade) instead._
|
||||
|
||||
Add `# -*- coding: utf-8 -*-` to the top of python files.
|
||||
- To remove the coding pragma pass `--remove` (useful in a python3-only codebase)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import argparse
|
|||
import math
|
||||
import os
|
||||
import subprocess
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
from pre_commit_hooks.util import added_files
|
||||
from pre_commit_hooks.util import zsplit
|
||||
|
|
@ -46,7 +46,7 @@ def find_large_added_files(
|
|||
filenames_filtered &= added_files()
|
||||
|
||||
for filename in filenames_filtered:
|
||||
kb = int(math.ceil(os.stat(filename).st_size / 1024))
|
||||
kb = math.ceil(os.stat(filename).st_size / 1024)
|
||||
if kb > maxkb:
|
||||
print(f'{filename} ({kb} KB) exceeds {maxkb} KB.')
|
||||
retv = 1
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import ast
|
|||
import platform
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import ast
|
||||
from collections.abc import Sequence
|
||||
from typing import NamedTuple
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
BUILTIN_TYPES = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
from typing import Sequence
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import Sequence
|
||||
|
||||
from pre_commit_hooks.util import added_files
|
||||
from pre_commit_hooks.util import cmd_output
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import io
|
||||
import tokenize
|
||||
from collections.abc import Sequence
|
||||
from tokenize import tokenize as tokenize_tokenize
|
||||
from typing import Sequence
|
||||
|
||||
NON_CODE_TOKENS = frozenset((
|
||||
tokenize.COMMENT, tokenize.ENDMARKER, tokenize.NEWLINE, tokenize.NL,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import shlex
|
||||
import sys
|
||||
from typing import Generator
|
||||
from collections.abc import Generator
|
||||
from collections.abc import Sequence
|
||||
from typing import NamedTuple
|
||||
from typing import Sequence
|
||||
|
||||
from pre_commit_hooks.util import cmd_output
|
||||
from pre_commit_hooks.util import zsplit
|
||||
|
|
@ -35,7 +35,7 @@ class GitLsFile(NamedTuple):
|
|||
filename: str
|
||||
|
||||
|
||||
def git_ls_files(paths: Sequence[str]) -> Generator[GitLsFile, None, None]:
|
||||
def git_ls_files(paths: Sequence[str]) -> Generator[GitLsFile]:
|
||||
outs = cmd_output('git', 'ls-files', '-z', '--stage', '--', *paths)
|
||||
for out in zsplit(outs):
|
||||
metadata, filename = out.split('\t')
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
def raise_duplicate_keys(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import os.path
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
from pre_commit_hooks.util import cmd_output
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import shlex
|
||||
import sys
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
from pre_commit_hooks.check_executables_have_shebangs import EXECUTABLE_VALUES
|
||||
from pre_commit_hooks.check_executables_have_shebangs import git_ls_files
|
||||
|
|
@ -36,7 +36,7 @@ def _message(path: str) -> None:
|
|||
f'`chmod +x {shlex.quote(path)}`\n'
|
||||
f' If on Windows, you may also need to: '
|
||||
f'`git add --chmod=+x {shlex.quote(path)}`\n'
|
||||
f' If it not supposed to be executable, double-check its shebang '
|
||||
f' If it is not supposed to be executable, double-check its shebang '
|
||||
f'is wanted.\n',
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import os.path
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import sys
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
if sys.version_info >= (3, 11): # pragma: >=3.11 cover
|
||||
import tomllib
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from typing import Pattern
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
from re import Pattern
|
||||
|
||||
|
||||
def _get_pattern(domain: str) -> Pattern[bytes]:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import xml.sax.handler
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from collections.abc import Generator
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
from typing import Generator
|
||||
from typing import NamedTuple
|
||||
from typing import Sequence
|
||||
|
||||
import ruamel.yaml
|
||||
|
||||
yaml = ruamel.yaml.YAML(typ='safe')
|
||||
|
||||
|
||||
def _exhaust(gen: Generator[str, None, None]) -> None:
|
||||
def _exhaust(gen: Generator[str]) -> None:
|
||||
for _ in gen:
|
||||
pass
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|||
'--unsafe', action='store_true',
|
||||
help=(
|
||||
'Instead of loading the files, simply parse them for syntax. '
|
||||
'A syntax-only check enables extensions and unsafe contstructs '
|
||||
'A syntax-only check enables extensions and unsafe constructs '
|
||||
'which would otherwise be forbidden. Using this option removes '
|
||||
'all guarantees of portability to other yaml implementations. '
|
||||
'Implies --allow-multiple-documents'
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import ast
|
||||
import traceback
|
||||
from collections.abc import Sequence
|
||||
from typing import NamedTuple
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
DEBUG_STATEMENTS = {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import shlex
|
||||
import subprocess
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
from pre_commit_hooks.util import cmd_output
|
||||
from pre_commit_hooks.util import zsplit
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
from collections.abc import Sequence
|
||||
from typing import NamedTuple
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
class BadFile(NamedTuple):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
BLACKLIST = [
|
||||
b'BEGIN RSA PRIVATE KEY',
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import os
|
||||
from collections.abc import Sequence
|
||||
from typing import IO
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
def fix_file(file_obj: IO[bytes]) -> int:
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ conflicts and keep the file nicely ordered.
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import IO
|
||||
from typing import Iterable
|
||||
from typing import Sequence
|
||||
|
||||
PASS = 0
|
||||
FAIL = 1
|
||||
|
|
@ -54,18 +54,21 @@ def sort_file_contents(
|
|||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('filenames', nargs='+', help='Files to sort')
|
||||
parser.add_argument(
|
||||
|
||||
mutex = parser.add_mutually_exclusive_group(required=False)
|
||||
mutex.add_argument(
|
||||
'--ignore-case',
|
||||
action='store_const',
|
||||
const=bytes.lower,
|
||||
default=None,
|
||||
help='fold lower case to upper case characters',
|
||||
)
|
||||
parser.add_argument(
|
||||
mutex.add_argument(
|
||||
'--unique',
|
||||
action='store_true',
|
||||
help='ensure each line is unique',
|
||||
)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
retv = PASS
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from collections.abc import Sequence
|
||||
from typing import IO
|
||||
from typing import NamedTuple
|
||||
from typing import Sequence
|
||||
|
||||
DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-'
|
||||
|
||||
|
|
@ -107,6 +108,13 @@ def _normalize_pragma(pragma: str) -> bytes:
|
|||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
print(
|
||||
'warning: this hook is deprecated and will be removed in a future '
|
||||
'release because py2 is EOL. instead, use '
|
||||
'https://github.com/asottile/pyupgrade',
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
'Fixes the encoding pragma of python files',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import os
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
from pre_commit_hooks.util import cmd_output
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import collections
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
CRLF = b'\r\n'
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from typing import AbstractSet
|
||||
from typing import Sequence
|
||||
|
||||
from pre_commit_hooks.util import CalledProcessError
|
||||
from pre_commit_hooks.util import cmd_output
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import argparse
|
|||
import json
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import Sequence
|
||||
from difflib import unified_diff
|
||||
from typing import Mapping
|
||||
from typing import Sequence
|
||||
|
||||
def _insert_linebreaks(json_str: str) -> str:
|
||||
return re.sub(
|
||||
|
|
@ -135,17 +135,22 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|||
f'Input File {json_file} is not a valid JSON, consider using '
|
||||
f'check-json',
|
||||
)
|
||||
return 1
|
||||
|
||||
if contents != pretty_contents:
|
||||
status = 1
|
||||
if args.autofix:
|
||||
_autofix(json_file, pretty_contents)
|
||||
if args.empty_object_with_newline:
|
||||
status = 0
|
||||
else:
|
||||
diff_output = get_diff(contents, pretty_contents, json_file)
|
||||
sys.stdout.buffer.write(diff_output.encode())
|
||||
else:
|
||||
if contents != pretty_contents:
|
||||
if args.autofix:
|
||||
_autofix(json_file, pretty_contents)
|
||||
if args.empty_object_with_newline:
|
||||
status = 0
|
||||
else:
|
||||
diff_output = get_diff(
|
||||
contents,
|
||||
pretty_contents,
|
||||
json_file
|
||||
)
|
||||
sys.stdout.buffer.write(diff_output.encode())
|
||||
|
||||
status = 1
|
||||
|
||||
return status
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from typing import IO
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
PASS = 0
|
||||
|
|
@ -45,6 +45,11 @@ class Requirement:
|
|||
elif requirement.value == b'\n':
|
||||
return False
|
||||
else:
|
||||
# if 2 requirements have the same name, the one with comments
|
||||
# needs to go first (so that when removing duplicates, the one
|
||||
# with comments is kept)
|
||||
if self.name == requirement.name:
|
||||
return bool(self.comments) > bool(requirement.comments)
|
||||
return self.name < requirement.name
|
||||
|
||||
def is_complete(self) -> bool:
|
||||
|
|
@ -110,13 +115,20 @@ def fix_requirements(f: IO[bytes]) -> int:
|
|||
# which is automatically added by broken pip package under Debian
|
||||
requirements = [
|
||||
req for req in requirements
|
||||
if req.value != b'pkg-resources==0.0.0\n'
|
||||
if req.value not in [
|
||||
b'pkg-resources==0.0.0\n',
|
||||
b'pkg_resources==0.0.0\n',
|
||||
]
|
||||
]
|
||||
|
||||
# sort the requirements and remove duplicates
|
||||
prev = None
|
||||
for requirement in sorted(requirements):
|
||||
after.extend(requirement.comments)
|
||||
assert requirement.value, requirement.value
|
||||
after.append(requirement.value)
|
||||
if prev is None or requirement.value != prev.value:
|
||||
after.append(requirement.value)
|
||||
prev = requirement
|
||||
after.extend(rest)
|
||||
|
||||
after_string = b''.join(after)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ complicated YAML files.
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
QUOTES = ["'", '"']
|
||||
|
|
|
|||
|
|
@ -3,8 +3,15 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
import tokenize
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
if sys.version_info >= (3, 12): # pragma: >=3.12 cover
|
||||
FSTRING_START = tokenize.FSTRING_START
|
||||
FSTRING_END = tokenize.FSTRING_END
|
||||
else: # pragma: <3.12 cover
|
||||
FSTRING_START = FSTRING_END = -1
|
||||
|
||||
START_QUOTE_RE = re.compile('^[a-zA-Z]*"')
|
||||
|
||||
|
|
@ -40,11 +47,17 @@ def fix_strings(filename: str) -> int:
|
|||
# Basically a mutable string
|
||||
splitcontents = list(contents)
|
||||
|
||||
fstring_depth = 0
|
||||
|
||||
# Iterate in reverse so the offsets are always correct
|
||||
tokens_l = list(tokenize.generate_tokens(io.StringIO(contents).readline))
|
||||
tokens = reversed(tokens_l)
|
||||
for token_type, token_text, (srow, scol), (erow, ecol), _ in tokens:
|
||||
if token_type == tokenize.STRING:
|
||||
if token_type == FSTRING_START: # pragma: >=3.12 cover
|
||||
fstring_depth += 1
|
||||
elif token_type == FSTRING_END: # pragma: >=3.12 cover
|
||||
fstring_depth -= 1
|
||||
elif fstring_depth == 0 and token_type == tokenize.STRING:
|
||||
new_text = handle_match(token_text)
|
||||
splitcontents[
|
||||
line_offsets[srow] + scol:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import os.path
|
||||
import re
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import os
|
||||
from typing import Sequence
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
def _fix_file(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[metadata]
|
||||
name = pre_commit_hooks
|
||||
version = 4.4.0
|
||||
version = 5.0.0
|
||||
description = Some out-of-the-box hooks for pre-commit.
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
|
|
@ -21,7 +21,7 @@ packages = find:
|
|||
install_requires =
|
||||
ruamel.yaml>=0.15
|
||||
tomli>=1.1.0;python_version<"3.11"
|
||||
python_requires = >=3.8
|
||||
python_requires = >=3.9
|
||||
|
||||
[options.packages.find]
|
||||
exclude =
|
||||
|
|
|
|||
63
tests/check_illegal_windows_names_test.py
Normal file
63
tests/check_illegal_windows_names_test.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os.path
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from pre_commit_hooks.check_yaml import yaml
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def hook_re():
|
||||
here = os.path.dirname(__file__)
|
||||
with open(os.path.join(here, '..', '.pre-commit-hooks.yaml')) as f:
|
||||
hook_defs = yaml.load(f)
|
||||
hook, = (
|
||||
hook
|
||||
for hook in hook_defs
|
||||
if hook['id'] == 'check-illegal-windows-names'
|
||||
)
|
||||
yield re.compile(hook['files'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
's',
|
||||
(
|
||||
pytest.param('aux.txt', id='with ext'),
|
||||
pytest.param('aux', id='without ext'),
|
||||
pytest.param('AuX.tXt', id='capitals'),
|
||||
pytest.param('com7.dat', id='com with digit'),
|
||||
pytest.param(':', id='bare colon'),
|
||||
pytest.param('file:Zone.Identifier', id='mid colon'),
|
||||
pytest.param('path/COM¹.json', id='com with superscript'),
|
||||
pytest.param('dir/LPT³.toml', id='lpt with superscript'),
|
||||
pytest.param('with < less than', id='with less than'),
|
||||
pytest.param('Fast or Slow?.md', id='with question mark'),
|
||||
pytest.param('with "double" quotes', id='with double quotes'),
|
||||
pytest.param('with_null\x00byte', id='with null byte'),
|
||||
pytest.param('ends_with.', id='ends with period'),
|
||||
pytest.param('ends_with ', id='ends with space'),
|
||||
pytest.param('ends_with\t', id='ends with tab'),
|
||||
pytest.param('dir/ends./with.txt', id='directory ends with period'),
|
||||
pytest.param('dir/ends /with.txt', id='directory ends with space'),
|
||||
),
|
||||
)
|
||||
def test_check_illegal_windows_names_matches(hook_re, s):
|
||||
assert hook_re.search(s)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
's',
|
||||
(
|
||||
pytest.param('README.md', id='standard file'),
|
||||
pytest.param('foo.aux', id='as ext'),
|
||||
pytest.param('com.dat', id='com without digit'),
|
||||
pytest.param('.python-version', id='starts with period'),
|
||||
pytest.param(' pseudo nan', id='with spaces'),
|
||||
pytest.param('!@#$%^&;=≤\'~`¡¿€🤗', id='with allowed characters'),
|
||||
pytest.param('path.to/file.py', id='standard path'),
|
||||
),
|
||||
)
|
||||
def test_check_illegal_windows_names_does_not_match(hook_re, s):
|
||||
assert hook_re.search(s) is None
|
||||
|
|
@ -67,18 +67,6 @@ from pre_commit_hooks.file_contents_sorter import PASS
|
|||
FAIL,
|
||||
b'Fie\nFoe\nfee\nfum\n',
|
||||
),
|
||||
(
|
||||
b'fee\nFie\nFoe\nfum\n',
|
||||
['--unique', '--ignore-case'],
|
||||
PASS,
|
||||
b'fee\nFie\nFoe\nfum\n',
|
||||
),
|
||||
(
|
||||
b'fee\nfee\nFie\nFoe\nfum\n',
|
||||
['--unique', '--ignore-case'],
|
||||
FAIL,
|
||||
b'fee\nFie\nFoe\nfum\n',
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_integration(input_s, argv, expected_retval, output, tmpdir):
|
||||
|
|
@ -89,3 +77,24 @@ def test_integration(input_s, argv, expected_retval, output, tmpdir):
|
|||
|
||||
assert path.read_binary() == output
|
||||
assert output_retval == expected_retval
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('input_s', 'argv'),
|
||||
(
|
||||
(
|
||||
b'fee\nFie\nFoe\nfum\n',
|
||||
['--unique', '--ignore-case'],
|
||||
),
|
||||
(
|
||||
b'fee\nfee\nFie\nFoe\nfum\n',
|
||||
['--unique', '--ignore-case'],
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_integration_invalid_args(input_s, argv, tmpdir):
|
||||
path = tmpdir.join('file.txt')
|
||||
path.write_binary(input_s)
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
main([str(path)] + argv)
|
||||
|
|
|
|||
|
|
@ -83,6 +83,24 @@ def test_autofix_main(tmpdir):
|
|||
assert ret == 0
|
||||
|
||||
|
||||
def test_invalid_main(tmpdir):
|
||||
srcfile1 = tmpdir.join('not_valid_json.json')
|
||||
srcfile1.write(
|
||||
'{\n'
|
||||
' // not json\n'
|
||||
' "a": "b"\n'
|
||||
'}',
|
||||
)
|
||||
srcfile2 = tmpdir.join('to_be_json_formatted.json')
|
||||
srcfile2.write('{ "a": "b" }')
|
||||
|
||||
# it should have skipped the first file and formatted the second one
|
||||
assert main(['--autofix', str(srcfile1), str(srcfile2)]) == 1
|
||||
|
||||
# confirm second file was formatted (shouldn't trigger linter again)
|
||||
assert main([str(srcfile2)]) == 0
|
||||
|
||||
|
||||
def test_orderfile_get_pretty_format():
|
||||
ret = main((
|
||||
'--top-keys=alist', get_resource_path('pretty_formatted_json.json'),
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ from pre_commit_hooks.requirements_txt_fixer import Requirement
|
|||
b'f<=2\n'
|
||||
b'g<2\n',
|
||||
),
|
||||
(b'a==1\nb==1\na==1\n', FAIL, b'a==1\nb==1\n'),
|
||||
(
|
||||
b'a==1\nb==1\n#comment about a\na==1\n',
|
||||
FAIL,
|
||||
b'#comment about a\na==1\nb==1\n',
|
||||
),
|
||||
(b'ocflib\nDjango\nPyMySQL\n', FAIL, b'Django\nocflib\nPyMySQL\n'),
|
||||
(
|
||||
b'-e git+ssh://git_url@tag#egg=ocflib\nDjango\nPyMySQL\n',
|
||||
|
|
@ -76,6 +82,8 @@ from pre_commit_hooks.requirements_txt_fixer import Requirement
|
|||
),
|
||||
(b'bar\npkg-resources==0.0.0\nfoo\n', FAIL, b'bar\nfoo\n'),
|
||||
(b'foo\npkg-resources==0.0.0\nbar\n', FAIL, b'bar\nfoo\n'),
|
||||
(b'bar\npkg_resources==0.0.0\nfoo\n', FAIL, b'bar\nfoo\n'),
|
||||
(b'foo\npkg_resources==0.0.0\nbar\n', FAIL, b'bar\nfoo\n'),
|
||||
(
|
||||
b'git+ssh://git_url@tag#egg=ocflib\nDjango\nijk\n',
|
||||
FAIL,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@ TESTS = (
|
|||
1,
|
||||
),
|
||||
('"foo""bar"', "'foo''bar'", 1),
|
||||
pytest.param(
|
||||
"f'hello{\"world\"}'",
|
||||
"f'hello{\"world\"}'",
|
||||
0,
|
||||
id='ignore nested fstrings',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue