mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2026-03-30 10:16:54 +00:00
Compare commits
No commits in common. "main" and "v4.3.0" have entirely different histories.
52 changed files with 545 additions and 431 deletions
19
.github/workflows/main.yml
vendored
19
.github/workflows/main.yml
vendored
|
|
@ -1,19 +0,0 @@
|
||||||
name: main
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main, test-me-*]
|
|
||||||
tags: '*'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main-windows:
|
|
||||||
uses: asottile/workflows/.github/workflows/tox.yml@v1.8.1
|
|
||||||
with:
|
|
||||||
env: '["py310"]'
|
|
||||||
os: windows-latest
|
|
||||||
main-linux:
|
|
||||||
uses: asottile/workflows/.github/workflows/tox.yml@v1.8.1
|
|
||||||
with:
|
|
||||||
env: '["py310", "py311", "py312", "py313"]'
|
|
||||||
os: ubuntu-latest
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v6.0.0
|
rev: v4.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
|
|
@ -10,32 +10,34 @@ repos:
|
||||||
- id: name-tests-test
|
- id: name-tests-test
|
||||||
- id: requirements-txt-fixer
|
- id: requirements-txt-fixer
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v3.2.0
|
rev: v1.20.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
- repo: https://github.com/asottile/reorder-python-imports
|
- repo: https://github.com/asottile/reorder_python_imports
|
||||||
rev: v3.16.0
|
rev: v3.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: reorder-python-imports
|
||||||
args: [--py310-plus, --add-import, 'from __future__ import annotations']
|
args: [--py37-plus, --add-import, 'from __future__ import annotations']
|
||||||
- repo: https://github.com/asottile/add-trailing-comma
|
- repo: https://github.com/asottile/add-trailing-comma
|
||||||
rev: v4.0.0
|
rev: v2.2.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: add-trailing-comma
|
- id: add-trailing-comma
|
||||||
|
args: [--py36-plus]
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.21.2
|
rev: v2.32.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py310-plus]
|
args: [--py37-plus]
|
||||||
- repo: https://github.com/hhatto/autopep8
|
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||||
rev: v2.3.2
|
rev: v1.6.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: autopep8
|
- id: autopep8
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 7.3.0
|
rev: 4.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v1.19.1
|
rev: v0.960
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
|
additional_dependencies: [types-all]
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
description: prevents giant files from being committed.
|
description: prevents giant files from being committed.
|
||||||
entry: check-added-large-files
|
entry: check-added-large-files
|
||||||
language: python
|
language: python
|
||||||
stages: [pre-commit, pre-push, manual]
|
stages: [commit, push, manual]
|
||||||
minimum_pre_commit_version: 3.2.0
|
|
||||||
- id: check-ast
|
- id: check-ast
|
||||||
name: check python ast
|
name: check python ast
|
||||||
description: simply checks whether the files parse as valid python.
|
description: simply checks whether the files parse as valid python.
|
||||||
|
|
@ -12,9 +11,9 @@
|
||||||
language: python
|
language: python
|
||||||
types: [python]
|
types: [python]
|
||||||
- id: check-byte-order-marker
|
- id: check-byte-order-marker
|
||||||
name: check-byte-order-marker (removed)
|
name: 'check BOM - deprecated: use fix-byte-order-marker'
|
||||||
description: (removed) use fix-byte-order-marker instead.
|
description: forbids files which have a utf-8 byte-order marker.
|
||||||
entry: pre-commit-hooks-removed check-byte-order-marker fix-byte-order-marker https://github.com/pre-commit/pre-commit-hooks
|
entry: check-byte-order-marker
|
||||||
language: python
|
language: python
|
||||||
types: [text]
|
types: [text]
|
||||||
- id: check-builtin-literals
|
- id: check-builtin-literals
|
||||||
|
|
@ -29,7 +28,7 @@
|
||||||
entry: check-case-conflict
|
entry: check-case-conflict
|
||||||
language: python
|
language: python
|
||||||
- id: check-docstring-first
|
- id: check-docstring-first
|
||||||
name: check docstring is first (deprecated)
|
name: check docstring is first
|
||||||
description: checks a common error of defining a docstring after code.
|
description: checks a common error of defining a docstring after code.
|
||||||
entry: check-docstring-first
|
entry: check-docstring-first
|
||||||
language: python
|
language: python
|
||||||
|
|
@ -40,13 +39,7 @@
|
||||||
entry: check-executables-have-shebangs
|
entry: check-executables-have-shebangs
|
||||||
language: python
|
language: python
|
||||||
types: [text, executable]
|
types: [text, executable]
|
||||||
stages: [pre-commit, pre-push, manual]
|
stages: [commit, 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
|
- id: check-json
|
||||||
name: check json
|
name: check json
|
||||||
description: checks json files for parseable syntax.
|
description: checks json files for parseable syntax.
|
||||||
|
|
@ -59,8 +52,7 @@
|
||||||
entry: check-shebang-scripts-are-executable
|
entry: check-shebang-scripts-are-executable
|
||||||
language: python
|
language: python
|
||||||
types: [text]
|
types: [text]
|
||||||
stages: [pre-commit, pre-push, manual]
|
stages: [commit, push, manual]
|
||||||
minimum_pre_commit_version: 3.2.0
|
|
||||||
- id: pretty-format-json
|
- id: pretty-format-json
|
||||||
name: pretty format json
|
name: pretty format json
|
||||||
description: sets a standard for formatting json files.
|
description: sets a standard for formatting json files.
|
||||||
|
|
@ -115,7 +107,6 @@
|
||||||
entry: destroyed-symlinks
|
entry: destroyed-symlinks
|
||||||
language: python
|
language: python
|
||||||
types: [file]
|
types: [file]
|
||||||
stages: [pre-commit, pre-push, manual]
|
|
||||||
- id: detect-aws-credentials
|
- id: detect-aws-credentials
|
||||||
name: detect aws credentials
|
name: detect aws credentials
|
||||||
description: detects *your* aws credentials from the aws cli credentials file.
|
description: detects *your* aws credentials from the aws cli credentials file.
|
||||||
|
|
@ -140,8 +131,7 @@
|
||||||
entry: end-of-file-fixer
|
entry: end-of-file-fixer
|
||||||
language: python
|
language: python
|
||||||
types: [text]
|
types: [text]
|
||||||
stages: [pre-commit, pre-push, manual]
|
stages: [commit, push, manual]
|
||||||
minimum_pre_commit_version: 3.2.0
|
|
||||||
- id: file-contents-sorter
|
- id: file-contents-sorter
|
||||||
name: 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.
|
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.
|
||||||
|
|
@ -155,10 +145,10 @@
|
||||||
language: python
|
language: python
|
||||||
types: [text]
|
types: [text]
|
||||||
- id: fix-encoding-pragma
|
- id: fix-encoding-pragma
|
||||||
name: fix python encoding pragma (removed)
|
name: fix python encoding pragma
|
||||||
description: (removed) use pyupgrade instead.
|
description: 'adds # -*- coding: utf-8 -*- to the top of python files.'
|
||||||
entry: pre-commit-hooks-removed fix-encoding-pragma pyupgrade https://github.com/asottile/pyupgrade
|
|
||||||
language: python
|
language: python
|
||||||
|
entry: fix-encoding-pragma
|
||||||
types: [python]
|
types: [python]
|
||||||
- id: forbid-new-submodules
|
- id: forbid-new-submodules
|
||||||
name: forbid new submodules
|
name: forbid new submodules
|
||||||
|
|
@ -166,12 +156,6 @@
|
||||||
language: python
|
language: python
|
||||||
entry: forbid-new-submodules
|
entry: forbid-new-submodules
|
||||||
types: [directory]
|
types: [directory]
|
||||||
- id: forbid-submodules
|
|
||||||
name: forbid submodules
|
|
||||||
description: forbids any submodules in the repository
|
|
||||||
language: fail
|
|
||||||
entry: 'submodules are not allowed in this repository:'
|
|
||||||
types: [directory]
|
|
||||||
- id: mixed-line-ending
|
- id: mixed-line-ending
|
||||||
name: mixed line ending
|
name: mixed line ending
|
||||||
description: replaces or checks mixed line ending.
|
description: replaces or checks mixed line ending.
|
||||||
|
|
@ -195,7 +179,7 @@
|
||||||
description: sorts entries in requirements.txt.
|
description: sorts entries in requirements.txt.
|
||||||
entry: requirements-txt-fixer
|
entry: requirements-txt-fixer
|
||||||
language: python
|
language: python
|
||||||
files: (requirements|constraints).*\.txt$
|
files: requirements.*\.txt$
|
||||||
- id: sort-simple-yaml
|
- id: sort-simple-yaml
|
||||||
name: sort simple yaml files
|
name: sort simple yaml files
|
||||||
description: sorts simple yaml files which consist only of top-level keys, preserving comments and blocks.
|
description: sorts simple yaml files which consist only of top-level keys, preserving comments and blocks.
|
||||||
|
|
@ -208,5 +192,4 @@
|
||||||
entry: trailing-whitespace-fixer
|
entry: trailing-whitespace-fixer
|
||||||
language: python
|
language: python
|
||||||
types: [text]
|
types: [text]
|
||||||
stages: [pre-commit, pre-push, manual]
|
stages: [commit, push, manual]
|
||||||
minimum_pre_commit_version: 3.2.0
|
|
||||||
|
|
|
||||||
91
CHANGELOG.md
91
CHANGELOG.md
|
|
@ -1,94 +1,3 @@
|
||||||
6.0.0 - 2025-08-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
## Fixes
|
|
||||||
- `check-shebang-scripts-are-executable`: improve error message.
|
|
||||||
- #1115 PR by @homebysix.
|
|
||||||
|
|
||||||
## Migrating
|
|
||||||
- now requires python >= 3.9.
|
|
||||||
- #1098 PR by @asottile.
|
|
||||||
- `file-contents-sorter`: disallow `--unique` and `--ignore-case` at the same
|
|
||||||
time.
|
|
||||||
- #1095 PR by @nemacysts.
|
|
||||||
- #794 issue by @teksturi.
|
|
||||||
- Removed `check-byte-order-marker` and `fix-encoding-pragma`.
|
|
||||||
- `check-byte-order-marker`: migrate to `fix-byte-order-marker`.
|
|
||||||
- `fix-encoding-pragma`: migrate to `pyupgrade`.
|
|
||||||
- #1034 PR by @mxr.
|
|
||||||
- #1032 issue by @mxr.
|
|
||||||
- #522 PR by @jgowdy.
|
|
||||||
|
|
||||||
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
|
|
||||||
==================
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- `forbid-submodules`: new hook which outright bans submodules.
|
|
||||||
- #815 PR by @asottile.
|
|
||||||
- #707 issue by @ChiefGokhlayeh.
|
|
||||||
|
|
||||||
4.3.0 - 2022-06-07
|
4.3.0 - 2022-06-07
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
|
||||||
35
README.md
35
README.md
|
|
@ -1,4 +1,5 @@
|
||||||
[](https://github.com/pre-commit/pre-commit-hooks/actions/workflows/main.yml)
|
[](https://asottile.visualstudio.com/asottile/_build/latest?definitionId=17&branchName=main)
|
||||||
|
[](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=17&branchName=main)
|
||||||
[](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit-hooks/main)
|
[](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit-hooks/main)
|
||||||
|
|
||||||
pre-commit-hooks
|
pre-commit-hooks
|
||||||
|
|
@ -15,7 +16,7 @@ Add this to your `.pre-commit-config.yaml`
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v6.0.0 # Use the ref you want to point at
|
rev: v4.3.0 # Use the ref you want to point at
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
# - id: ...
|
# - id: ...
|
||||||
|
|
@ -45,12 +46,12 @@ Require literal syntax when initializing empty or zero Python builtin types.
|
||||||
#### `check-case-conflict`
|
#### `check-case-conflict`
|
||||||
Check for files with names that would conflict on a case-insensitive filesystem like MacOS HFS+ or Windows FAT.
|
Check for files with names that would conflict on a case-insensitive filesystem like MacOS HFS+ or Windows FAT.
|
||||||
|
|
||||||
|
#### `check-docstring-first`
|
||||||
|
Checks for a common error of placing code before the docstring.
|
||||||
|
|
||||||
#### `check-executables-have-shebangs`
|
#### `check-executables-have-shebangs`
|
||||||
Checks that non-binary executables have a proper shebang.
|
Checks that non-binary executables have a proper shebang.
|
||||||
|
|
||||||
#### `check-illegal-windows-names`
|
|
||||||
Check for files that cannot be created on Windows.
|
|
||||||
|
|
||||||
#### `check-json`
|
#### `check-json`
|
||||||
Attempts to load all json files to verify syntax.
|
Attempts to load all json files to verify syntax.
|
||||||
|
|
||||||
|
|
@ -113,28 +114,25 @@ This hook replaces double quoted strings with single quoted strings.
|
||||||
#### `end-of-file-fixer`
|
#### `end-of-file-fixer`
|
||||||
Makes sure files end in a newline and only a newline.
|
Makes sure files end in a newline and only a newline.
|
||||||
|
|
||||||
|
#### `fix-byte-order-marker`
|
||||||
|
removes UTF-8 byte order marker
|
||||||
|
|
||||||
|
#### `fix-encoding-pragma`
|
||||||
|
Add `# -*- coding: utf-8 -*-` to the top of python files.
|
||||||
|
- To remove the coding pragma pass `--remove` (useful in a python3-only codebase)
|
||||||
|
|
||||||
#### `file-contents-sorter`
|
#### `file-contents-sorter`
|
||||||
Sort the lines in specified files (defaults to alphabetical).
|
Sort the lines in specified files (defaults to alphabetical).
|
||||||
You must provide the target [`files`](https://pre-commit.com/#config-files) as input.
|
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.
|
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:
|
The following arguments are available:
|
||||||
- `--ignore-case` - fold lower case to upper case characters.
|
- `--ignore-case` - fold lower case to upper case characters.
|
||||||
- `--unique` - ensure each line is unique.
|
- `--unique` - ensure each line is unique.
|
||||||
|
|
||||||
#### `fix-byte-order-marker`
|
|
||||||
removes UTF-8 byte order marker
|
|
||||||
|
|
||||||
#### `forbid-new-submodules`
|
#### `forbid-new-submodules`
|
||||||
Prevent addition of new git submodules.
|
Prevent addition of new git submodules.
|
||||||
|
|
||||||
This is intended as a helper to migrate away from submodules. If you want to
|
|
||||||
ban them entirely use `forbid-submodules`
|
|
||||||
|
|
||||||
#### `forbid-submodules`
|
|
||||||
forbids any submodules in the repository.
|
|
||||||
|
|
||||||
#### `mixed-line-ending`
|
#### `mixed-line-ending`
|
||||||
Replaces or checks mixed line ending.
|
Replaces or checks mixed line ending.
|
||||||
- `--fix={auto,crlf,lf,no}`
|
- `--fix={auto,crlf,lf,no}`
|
||||||
|
|
@ -176,7 +174,7 @@ the following commandline options:
|
||||||
- `--top-keys comma,separated,keys` - Keys to keep at the top of mappings.
|
- `--top-keys comma,separated,keys` - Keys to keep at the top of mappings.
|
||||||
|
|
||||||
#### `requirements-txt-fixer`
|
#### `requirements-txt-fixer`
|
||||||
Sorts entries in requirements.txt and constraints.txt and removes incorrect entry for `pkg-resources==0.0.0`
|
Sorts entries in requirements.txt and removes incorrect entry for `pkg-resources==0.0.0`
|
||||||
|
|
||||||
#### `sort-simple-yaml`
|
#### `sort-simple-yaml`
|
||||||
Sorts simple YAML files which consist only of top-level
|
Sorts simple YAML files which consist only of top-level
|
||||||
|
|
@ -203,9 +201,6 @@ Trims trailing whitespace.
|
||||||
### Deprecated / replaced hooks
|
### Deprecated / replaced hooks
|
||||||
|
|
||||||
- `check-byte-order-marker`: instead use fix-byte-order-marker
|
- `check-byte-order-marker`: instead use fix-byte-order-marker
|
||||||
- `fix-encoding-pragma`: instead use [`pyupgrade`](https://github.com/asottile/pyupgrade)
|
|
||||||
- `check-docstring-first`: fundamentally flawed, deprecated without replacement.
|
|
||||||
|
|
||||||
|
|
||||||
### As a standalone package
|
### As a standalone package
|
||||||
|
|
||||||
|
|
|
||||||
23
azure-pipelines.yml
Normal file
23
azure-pipelines.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
trigger:
|
||||||
|
branches:
|
||||||
|
include: [main, test-me-*]
|
||||||
|
tags:
|
||||||
|
include: ['*']
|
||||||
|
|
||||||
|
resources:
|
||||||
|
repositories:
|
||||||
|
- repository: asottile
|
||||||
|
type: github
|
||||||
|
endpoint: github
|
||||||
|
name: asottile/azure-pipeline-templates
|
||||||
|
ref: refs/tags/v2.4.0
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- template: job--python-tox.yml@asottile
|
||||||
|
parameters:
|
||||||
|
toxenvs: [py38]
|
||||||
|
os: windows
|
||||||
|
- template: job--python-tox.yml@asottile
|
||||||
|
parameters:
|
||||||
|
toxenvs: [py37, py38, py39, py310]
|
||||||
|
os: linux
|
||||||
|
|
@ -4,7 +4,7 @@ import argparse
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import added_files
|
from pre_commit_hooks.util import added_files
|
||||||
from pre_commit_hooks.util import zsplit
|
from pre_commit_hooks.util import zsplit
|
||||||
|
|
@ -46,7 +46,7 @@ def find_large_added_files(
|
||||||
filenames_filtered &= added_files()
|
filenames_filtered &= added_files()
|
||||||
|
|
||||||
for filename in filenames_filtered:
|
for filename in filenames_filtered:
|
||||||
kb = math.ceil(os.stat(filename).st_size / 1024)
|
kb = int(math.ceil(os.stat(filename).st_size / 1024))
|
||||||
if kb > maxkb:
|
if kb > maxkb:
|
||||||
print(f'{filename} ({kb} KB) exceeds {maxkb} KB.')
|
print(f'{filename} ({kb} KB) exceeds {maxkb} KB.')
|
||||||
retv = 1
|
retv = 1
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import ast
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import ast
|
import ast
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
BUILTIN_TYPES = {
|
BUILTIN_TYPES = {
|
||||||
|
|
@ -26,37 +26,36 @@ class Call(NamedTuple):
|
||||||
class Visitor(ast.NodeVisitor):
|
class Visitor(ast.NodeVisitor):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
ignore: set[str],
|
ignore: Sequence[str] | None = None,
|
||||||
allow_dict_kwargs: bool = True,
|
allow_dict_kwargs: bool = True,
|
||||||
) -> None:
|
) -> 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
|
self.allow_dict_kwargs = allow_dict_kwargs
|
||||||
self._disallowed = BUILTIN_TYPES.keys() - ignore
|
|
||||||
|
|
||||||
def _check_dict_call(self, node: ast.Call) -> bool:
|
def _check_dict_call(self, node: ast.Call) -> bool:
|
||||||
return self.allow_dict_kwargs and bool(node.keywords)
|
return self.allow_dict_kwargs and bool(node.keywords)
|
||||||
|
|
||||||
def visit_Call(self, node: ast.Call) -> None:
|
def visit_Call(self, node: ast.Call) -> None:
|
||||||
if (
|
if not isinstance(node.func, ast.Name):
|
||||||
# Ignore functions that are object attributes (`foo.bar()`).
|
# Ignore functions that are object attributes (`foo.bar()`).
|
||||||
# Assume that if the user calls `builtins.list()`, they know what
|
# Assume that if the user calls `builtins.list()`, they know what
|
||||||
# they're doing.
|
# they're doing.
|
||||||
isinstance(node.func, ast.Name) and
|
return
|
||||||
node.func.id in self._disallowed and
|
if node.func.id not in set(BUILTIN_TYPES).difference(self.ignore):
|
||||||
(node.func.id != 'dict' or not self._check_dict_call(node)) and
|
return
|
||||||
not node.args
|
if node.func.id == 'dict' and self._check_dict_call(node):
|
||||||
):
|
return
|
||||||
|
elif node.args:
|
||||||
|
return
|
||||||
self.builtin_type_calls.append(
|
self.builtin_type_calls.append(
|
||||||
Call(node.func.id, node.lineno, node.col_offset),
|
Call(node.func.id, node.lineno, node.col_offset),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.generic_visit(node)
|
|
||||||
|
|
||||||
|
|
||||||
def check_file(
|
def check_file(
|
||||||
filename: str,
|
filename: str,
|
||||||
*,
|
ignore: Sequence[str] | None = None,
|
||||||
ignore: set[str],
|
|
||||||
allow_dict_kwargs: bool = True,
|
allow_dict_kwargs: bool = True,
|
||||||
) -> list[Call]:
|
) -> list[Call]:
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
|
|
|
||||||
24
pre_commit_hooks/check_byte_order_marker.py
Normal file
24
pre_commit_hooks/check_byte_order_marker.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
retv = 0
|
||||||
|
|
||||||
|
for filename in args.filenames:
|
||||||
|
with open(filename, 'rb') as f:
|
||||||
|
if f.read(3) == b'\xef\xbb\xbf':
|
||||||
|
retv = 1
|
||||||
|
print(f'{filename}: Has a byte-order marker')
|
||||||
|
|
||||||
|
return retv
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main())
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections.abc import Iterable
|
from typing import Iterable
|
||||||
from collections.abc import Iterator
|
from typing import Iterator
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import added_files
|
from pre_commit_hooks.util import added_files
|
||||||
from pre_commit_hooks.util import cmd_output
|
from pre_commit_hooks.util import cmd_output
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import io
|
import io
|
||||||
import tokenize
|
import tokenize
|
||||||
from collections.abc import Sequence
|
|
||||||
from tokenize import tokenize as tokenize_tokenize
|
from tokenize import tokenize as tokenize_tokenize
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
NON_CODE_TOKENS = frozenset((
|
NON_CODE_TOKENS = frozenset((
|
||||||
tokenize.COMMENT, tokenize.ENDMARKER, tokenize.NEWLINE, tokenize.NL,
|
tokenize.COMMENT, tokenize.ENDMARKER, tokenize.NEWLINE, tokenize.NL,
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Generator
|
from typing import Generator
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import cmd_output
|
from pre_commit_hooks.util import cmd_output
|
||||||
from pre_commit_hooks.util import zsplit
|
from pre_commit_hooks.util import zsplit
|
||||||
|
|
@ -35,7 +35,7 @@ class GitLsFile(NamedTuple):
|
||||||
filename: str
|
filename: str
|
||||||
|
|
||||||
|
|
||||||
def git_ls_files(paths: Sequence[str]) -> Generator[GitLsFile]:
|
def git_ls_files(paths: Sequence[str]) -> Generator[GitLsFile, None, None]:
|
||||||
outs = cmd_output('git', 'ls-files', '-z', '--stage', '--', *paths)
|
outs = cmd_output('git', 'ls-files', '-z', '--stage', '--', *paths)
|
||||||
for out in zsplit(outs):
|
for out in zsplit(outs):
|
||||||
metadata, filename = out.split('\t')
|
metadata, filename = out.split('\t')
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def raise_duplicate_keys(
|
def raise_duplicate_keys(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os.path
|
import os.path
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import cmd_output
|
from pre_commit_hooks.util import cmd_output
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.check_executables_have_shebangs import EXECUTABLE_VALUES
|
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 git_ls_files
|
||||||
|
|
@ -36,7 +36,7 @@ def _message(path: str) -> None:
|
||||||
f'`chmod +x {shlex.quote(path)}`\n'
|
f'`chmod +x {shlex.quote(path)}`\n'
|
||||||
f' If on Windows, you may also need to: '
|
f' If on Windows, you may also need to: '
|
||||||
f'`git add --chmod=+x {shlex.quote(path)}`\n'
|
f'`git add --chmod=+x {shlex.quote(path)}`\n'
|
||||||
f' If it is not supposed to be executable, double-check its shebang '
|
f' If it not supposed to be executable, double-check its shebang '
|
||||||
f'is wanted.\n',
|
f'is wanted.\n',
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os.path
|
import os.path
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
if sys.version_info >= (3, 11): # pragma: >=3.11 cover
|
if sys.version_info >= (3, 11): # pragma: >=3.11 cover
|
||||||
import tomllib
|
import tomllib
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Sequence
|
from typing import Pattern
|
||||||
from re import Pattern
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def _get_pattern(domain: str) -> Pattern[bytes]:
|
def _get_pattern(domain: str) -> Pattern[bytes]:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import xml.sax.handler
|
import xml.sax.handler
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections.abc import Generator
|
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import Generator
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
import ruamel.yaml
|
import ruamel.yaml
|
||||||
|
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
yaml = ruamel.yaml.YAML(typ='safe')
|
||||||
|
|
||||||
|
|
||||||
def _exhaust(gen: Generator[str]) -> None:
|
def _exhaust(gen: Generator[str, None, None]) -> None:
|
||||||
for _ in gen:
|
for _ in gen:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||||
'--unsafe', action='store_true',
|
'--unsafe', action='store_true',
|
||||||
help=(
|
help=(
|
||||||
'Instead of loading the files, simply parse them for syntax. '
|
'Instead of loading the files, simply parse them for syntax. '
|
||||||
'A syntax-only check enables extensions and unsafe constructs '
|
'A syntax-only check enables extensions and unsafe contstructs '
|
||||||
'which would otherwise be forbidden. Using this option removes '
|
'which would otherwise be forbidden. Using this option removes '
|
||||||
'all guarantees of portability to other yaml implementations. '
|
'all guarantees of portability to other yaml implementations. '
|
||||||
'Implies --allow-multiple-documents'
|
'Implies --allow-multiple-documents'
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,11 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import ast
|
import ast
|
||||||
import traceback
|
import traceback
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
DEBUG_STATEMENTS = {
|
DEBUG_STATEMENTS = {
|
||||||
'bpdb',
|
|
||||||
'ipdb',
|
'ipdb',
|
||||||
'pdb',
|
'pdb',
|
||||||
'pdbr',
|
'pdbr',
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import cmd_output
|
from pre_commit_hooks.util import cmd_output
|
||||||
from pre_commit_hooks.util import zsplit
|
from pre_commit_hooks.util import zsplit
|
||||||
|
|
@ -76,7 +76,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||||
for destroyed_link in destroyed_links:
|
for destroyed_link in destroyed_links:
|
||||||
print(f'- {destroyed_link}')
|
print(f'- {destroyed_link}')
|
||||||
print('You should unstage affected files:')
|
print('You should unstage affected files:')
|
||||||
print(f'\tgit reset HEAD -- {shlex.join(destroyed_links)}')
|
print(
|
||||||
|
'\tgit reset HEAD -- {}'.format(
|
||||||
|
' '.join(shlex.quote(link) for link in destroyed_links),
|
||||||
|
),
|
||||||
|
)
|
||||||
print(
|
print(
|
||||||
'And retry commit. As a long term solution '
|
'And retry commit. As a long term solution '
|
||||||
'you may try to explicitly tell git that your '
|
'you may try to explicitly tell git that your '
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
class BadFile(NamedTuple):
|
class BadFile(NamedTuple):
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
BLACKLIST = [
|
BLACKLIST = [
|
||||||
b'BEGIN RSA PRIVATE KEY',
|
b'BEGIN RSA PRIVATE KEY',
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def fix_file(file_obj: IO[bytes]) -> int:
|
def fix_file(file_obj: IO[bytes]) -> int:
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ conflicts and keep the file nicely ordered.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections.abc import Callable
|
|
||||||
from collections.abc import Iterable
|
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import Callable
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
from typing import Iterable
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
PASS = 0
|
PASS = 0
|
||||||
FAIL = 1
|
FAIL = 1
|
||||||
|
|
@ -37,10 +37,7 @@ def sort_file_contents(
|
||||||
after = sorted(lines, key=key)
|
after = sorted(lines, key=key)
|
||||||
|
|
||||||
before_string = b''.join(before)
|
before_string = b''.join(before)
|
||||||
after_string = b'\n'.join(after)
|
after_string = b'\n'.join(after) + b'\n'
|
||||||
|
|
||||||
if after_string:
|
|
||||||
after_string += b'\n'
|
|
||||||
|
|
||||||
if before_string == after_string:
|
if before_string == after_string:
|
||||||
return PASS
|
return PASS
|
||||||
|
|
@ -54,21 +51,18 @@ def sort_file_contents(
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('filenames', nargs='+', help='Files to sort')
|
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',
|
'--ignore-case',
|
||||||
action='store_const',
|
action='store_const',
|
||||||
const=bytes.lower,
|
const=bytes.lower,
|
||||||
default=None,
|
default=None,
|
||||||
help='fold lower case to upper case characters',
|
help='fold lower case to upper case characters',
|
||||||
)
|
)
|
||||||
mutex.add_argument(
|
parser.add_argument(
|
||||||
'--unique',
|
'--unique',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='ensure each line is unique',
|
help='ensure each line is unique',
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
retv = PASS
|
retv = PASS
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
||||||
149
pre_commit_hooks/fix_encoding_pragma.py
Normal file
149
pre_commit_hooks/fix_encoding_pragma.py
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from typing import IO
|
||||||
|
from typing import NamedTuple
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-'
|
||||||
|
|
||||||
|
|
||||||
|
def has_coding(line: bytes) -> bool:
|
||||||
|
if not line.strip():
|
||||||
|
return False
|
||||||
|
return (
|
||||||
|
line.lstrip()[:1] == b'#' and (
|
||||||
|
b'unicode' in line or
|
||||||
|
b'encoding' in line or
|
||||||
|
b'coding:' in line or
|
||||||
|
b'coding=' in line
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExpectedContents(NamedTuple):
|
||||||
|
shebang: bytes
|
||||||
|
rest: bytes
|
||||||
|
# True: has exactly the coding pragma expected
|
||||||
|
# False: missing coding pragma entirely
|
||||||
|
# None: has a coding pragma, but it does not match
|
||||||
|
pragma_status: bool | None
|
||||||
|
ending: bytes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_any_pragma(self) -> bool:
|
||||||
|
return self.pragma_status is not False
|
||||||
|
|
||||||
|
def is_expected_pragma(self, remove: bool) -> bool:
|
||||||
|
expected_pragma_status = not remove
|
||||||
|
return self.pragma_status is expected_pragma_status
|
||||||
|
|
||||||
|
|
||||||
|
def _get_expected_contents(
|
||||||
|
first_line: bytes,
|
||||||
|
second_line: bytes,
|
||||||
|
rest: bytes,
|
||||||
|
expected_pragma: bytes,
|
||||||
|
) -> ExpectedContents:
|
||||||
|
ending = b'\r\n' if first_line.endswith(b'\r\n') else b'\n'
|
||||||
|
|
||||||
|
if first_line.startswith(b'#!'):
|
||||||
|
shebang = first_line
|
||||||
|
potential_coding = second_line
|
||||||
|
else:
|
||||||
|
shebang = b''
|
||||||
|
potential_coding = first_line
|
||||||
|
rest = second_line + rest
|
||||||
|
|
||||||
|
if potential_coding.rstrip(b'\r\n') == expected_pragma:
|
||||||
|
pragma_status: bool | None = True
|
||||||
|
elif has_coding(potential_coding):
|
||||||
|
pragma_status = None
|
||||||
|
else:
|
||||||
|
pragma_status = False
|
||||||
|
rest = potential_coding + rest
|
||||||
|
|
||||||
|
return ExpectedContents(
|
||||||
|
shebang=shebang, rest=rest, pragma_status=pragma_status, ending=ending,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def fix_encoding_pragma(
|
||||||
|
f: IO[bytes],
|
||||||
|
remove: bool = False,
|
||||||
|
expected_pragma: bytes = DEFAULT_PRAGMA,
|
||||||
|
) -> int:
|
||||||
|
expected = _get_expected_contents(
|
||||||
|
f.readline(), f.readline(), f.read(), expected_pragma,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Special cases for empty files
|
||||||
|
if not expected.rest.strip():
|
||||||
|
# If a file only has a shebang or a coding pragma, remove it
|
||||||
|
if expected.has_any_pragma or expected.shebang:
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
f.write(b'')
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if expected.is_expected_pragma(remove):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Otherwise, write out the new file
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
f.write(expected.shebang)
|
||||||
|
if not remove:
|
||||||
|
f.write(expected_pragma + expected.ending)
|
||||||
|
f.write(expected.rest)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_pragma(pragma: str) -> bytes:
|
||||||
|
return pragma.encode().rstrip()
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
'Fixes the encoding pragma of python files',
|
||||||
|
)
|
||||||
|
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
|
||||||
|
parser.add_argument(
|
||||||
|
'--pragma', default=DEFAULT_PRAGMA, type=_normalize_pragma,
|
||||||
|
help=(
|
||||||
|
f'The encoding pragma to use. '
|
||||||
|
f'Default: {DEFAULT_PRAGMA.decode()}'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--remove', action='store_true',
|
||||||
|
help='Remove the encoding pragma (Useful in a python3-only codebase)',
|
||||||
|
)
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
retv = 0
|
||||||
|
|
||||||
|
if args.remove:
|
||||||
|
fmt = 'Removed encoding pragma from {filename}'
|
||||||
|
else:
|
||||||
|
fmt = 'Added `{pragma}` to {filename}'
|
||||||
|
|
||||||
|
for filename in args.filenames:
|
||||||
|
with open(filename, 'r+b') as f:
|
||||||
|
file_ret = fix_encoding_pragma(
|
||||||
|
f, remove=args.remove, expected_pragma=args.pragma,
|
||||||
|
)
|
||||||
|
retv |= file_ret
|
||||||
|
if file_ret:
|
||||||
|
print(
|
||||||
|
fmt.format(pragma=args.pragma.decode(), filename=filename),
|
||||||
|
)
|
||||||
|
|
||||||
|
return retv
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main())
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import cmd_output
|
from pre_commit_hooks.util import cmd_output
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
CRLF = b'\r\n'
|
CRLF = b'\r\n'
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import AbstractSet
|
from typing import AbstractSet
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit_hooks.util import CalledProcessError
|
from pre_commit_hooks.util import CalledProcessError
|
||||||
from pre_commit_hooks.util import cmd_output
|
from pre_commit_hooks.util import cmd_output
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Mapping
|
|
||||||
from collections.abc import Sequence
|
|
||||||
from difflib import unified_diff
|
from difflib import unified_diff
|
||||||
|
from typing import Mapping
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def _get_pretty_format(
|
def _get_pretty_format(
|
||||||
|
|
@ -115,17 +115,13 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||||
f'Input File {json_file} is not a valid JSON, consider using '
|
f'Input File {json_file} is not a valid JSON, consider using '
|
||||||
f'check-json',
|
f'check-json',
|
||||||
)
|
)
|
||||||
status = 1
|
return 1
|
||||||
else:
|
|
||||||
if contents != pretty_contents:
|
if contents != pretty_contents:
|
||||||
if args.autofix:
|
if args.autofix:
|
||||||
_autofix(json_file, pretty_contents)
|
_autofix(json_file, pretty_contents)
|
||||||
else:
|
else:
|
||||||
diff_output = get_diff(
|
diff_output = get_diff(contents, pretty_contents, json_file)
|
||||||
contents,
|
|
||||||
pretty_contents,
|
|
||||||
json_file,
|
|
||||||
)
|
|
||||||
sys.stdout.buffer.write(diff_output.encode())
|
sys.stdout.buffer.write(diff_output.encode())
|
||||||
|
|
||||||
status = 1
|
status = 1
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
from collections.abc import Sequence
|
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
PASS = 0
|
PASS = 0
|
||||||
|
|
@ -45,11 +45,6 @@ class Requirement:
|
||||||
elif requirement.value == b'\n':
|
elif requirement.value == b'\n':
|
||||||
return False
|
return False
|
||||||
else:
|
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
|
return self.name < requirement.name
|
||||||
|
|
||||||
def is_complete(self) -> bool:
|
def is_complete(self) -> bool:
|
||||||
|
|
@ -115,20 +110,13 @@ def fix_requirements(f: IO[bytes]) -> int:
|
||||||
# which is automatically added by broken pip package under Debian
|
# which is automatically added by broken pip package under Debian
|
||||||
requirements = [
|
requirements = [
|
||||||
req for req in requirements
|
req for req in requirements
|
||||||
if req.value not in [
|
if req.value != b'pkg-resources==0.0.0\n'
|
||||||
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):
|
for requirement in sorted(requirements):
|
||||||
after.extend(requirement.comments)
|
after.extend(requirement.comments)
|
||||||
assert requirement.value, requirement.value
|
assert requirement.value, requirement.value
|
||||||
if prev is None or requirement.value != prev.value:
|
|
||||||
after.append(requirement.value)
|
after.append(requirement.value)
|
||||||
prev = requirement
|
|
||||||
after.extend(rest)
|
after.extend(rest)
|
||||||
|
|
||||||
after_string = b''.join(after)
|
after_string = b''.join(after)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ complicated YAML files.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
QUOTES = ["'", '"']
|
QUOTES = ["'", '"']
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,8 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import io
|
import io
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import tokenize
|
import tokenize
|
||||||
from collections.abc import Sequence
|
from typing 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]*"')
|
START_QUOTE_RE = re.compile('^[a-zA-Z]*"')
|
||||||
|
|
||||||
|
|
@ -47,17 +40,11 @@ def fix_strings(filename: str) -> int:
|
||||||
# Basically a mutable string
|
# Basically a mutable string
|
||||||
splitcontents = list(contents)
|
splitcontents = list(contents)
|
||||||
|
|
||||||
fstring_depth = 0
|
|
||||||
|
|
||||||
# Iterate in reverse so the offsets are always correct
|
# Iterate in reverse so the offsets are always correct
|
||||||
tokens_l = list(tokenize.generate_tokens(io.StringIO(contents).readline))
|
tokens_l = list(tokenize.generate_tokens(io.StringIO(contents).readline))
|
||||||
tokens = reversed(tokens_l)
|
tokens = reversed(tokens_l)
|
||||||
for token_type, token_text, (srow, scol), (erow, ecol), _ in tokens:
|
for token_type, token_text, (srow, scol), (erow, ecol), _ in tokens:
|
||||||
if token_type == FSTRING_START: # pragma: >=3.12 cover
|
if token_type == tokenize.STRING:
|
||||||
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)
|
new_text = handle_match(token_text)
|
||||||
splitcontents[
|
splitcontents[
|
||||||
line_offsets[srow] + scol:
|
line_offsets[srow] + scol:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
from collections.abc import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
def _fix_file(
|
def _fix_file(
|
||||||
|
|
|
||||||
14
setup.cfg
14
setup.cfg
|
|
@ -1,6 +1,6 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = pre_commit_hooks
|
name = pre_commit_hooks
|
||||||
version = 6.0.0
|
version = 4.3.0
|
||||||
description = Some out-of-the-box hooks for pre-commit.
|
description = Some out-of-the-box hooks for pre-commit.
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
|
|
@ -8,10 +8,15 @@ url = https://github.com/pre-commit/pre-commit-hooks
|
||||||
author = Anthony Sottile
|
author = Anthony Sottile
|
||||||
author_email = asottile@umich.edu
|
author_email = asottile@umich.edu
|
||||||
license = MIT
|
license = MIT
|
||||||
license_files = LICENSE
|
license_file = LICENSE
|
||||||
classifiers =
|
classifiers =
|
||||||
|
License :: OSI Approved :: MIT License
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3 :: Only
|
Programming Language :: Python :: 3 :: Only
|
||||||
|
Programming Language :: Python :: 3.7
|
||||||
|
Programming Language :: Python :: 3.8
|
||||||
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
Programming Language :: Python :: Implementation :: PyPy
|
Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
|
||||||
|
|
@ -20,7 +25,7 @@ packages = find:
|
||||||
install_requires =
|
install_requires =
|
||||||
ruamel.yaml>=0.15
|
ruamel.yaml>=0.15
|
||||||
tomli>=1.1.0;python_version<"3.11"
|
tomli>=1.1.0;python_version<"3.11"
|
||||||
python_requires = >=3.10
|
python_requires = >=3.7
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
exclude =
|
exclude =
|
||||||
|
|
@ -32,6 +37,7 @@ console_scripts =
|
||||||
check-added-large-files = pre_commit_hooks.check_added_large_files:main
|
check-added-large-files = pre_commit_hooks.check_added_large_files:main
|
||||||
check-ast = pre_commit_hooks.check_ast:main
|
check-ast = pre_commit_hooks.check_ast:main
|
||||||
check-builtin-literals = pre_commit_hooks.check_builtin_literals:main
|
check-builtin-literals = pre_commit_hooks.check_builtin_literals:main
|
||||||
|
check-byte-order-marker = pre_commit_hooks.check_byte_order_marker:main
|
||||||
check-case-conflict = pre_commit_hooks.check_case_conflict:main
|
check-case-conflict = pre_commit_hooks.check_case_conflict:main
|
||||||
check-docstring-first = pre_commit_hooks.check_docstring_first:main
|
check-docstring-first = pre_commit_hooks.check_docstring_first:main
|
||||||
check-executables-have-shebangs = pre_commit_hooks.check_executables_have_shebangs:main
|
check-executables-have-shebangs = pre_commit_hooks.check_executables_have_shebangs:main
|
||||||
|
|
@ -51,6 +57,7 @@ console_scripts =
|
||||||
end-of-file-fixer = pre_commit_hooks.end_of_file_fixer:main
|
end-of-file-fixer = pre_commit_hooks.end_of_file_fixer:main
|
||||||
file-contents-sorter = pre_commit_hooks.file_contents_sorter:main
|
file-contents-sorter = pre_commit_hooks.file_contents_sorter:main
|
||||||
fix-byte-order-marker = pre_commit_hooks.fix_byte_order_marker:main
|
fix-byte-order-marker = pre_commit_hooks.fix_byte_order_marker:main
|
||||||
|
fix-encoding-pragma = pre_commit_hooks.fix_encoding_pragma:main
|
||||||
forbid-new-submodules = pre_commit_hooks.forbid_new_submodules:main
|
forbid-new-submodules = pre_commit_hooks.forbid_new_submodules:main
|
||||||
mixed-line-ending = pre_commit_hooks.mixed_line_ending:main
|
mixed-line-ending = pre_commit_hooks.mixed_line_ending:main
|
||||||
name-tests-test = pre_commit_hooks.tests_should_end_in_test:main
|
name-tests-test = pre_commit_hooks.tests_should_end_in_test:main
|
||||||
|
|
@ -72,6 +79,7 @@ check_untyped_defs = true
|
||||||
disallow_any_generics = true
|
disallow_any_generics = true
|
||||||
disallow_incomplete_defs = true
|
disallow_incomplete_defs = true
|
||||||
disallow_untyped_defs = true
|
disallow_untyped_defs = true
|
||||||
|
no_implicit_optional = true
|
||||||
warn_redundant_casts = true
|
warn_redundant_casts = true
|
||||||
warn_unused_ignores = true
|
warn_unused_ignores = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,11 @@ t1 = ()
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def visitor():
|
||||||
|
return Visitor()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('expression', 'calls'),
|
('expression', 'calls'),
|
||||||
[
|
[
|
||||||
|
|
@ -80,8 +85,7 @@ t1 = ()
|
||||||
('builtins.tuple()', []),
|
('builtins.tuple()', []),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_non_dict_exprs(expression, calls):
|
def test_non_dict_exprs(visitor, expression, calls):
|
||||||
visitor = Visitor(ignore=set())
|
|
||||||
visitor.visit(ast.parse(expression))
|
visitor.visit(ast.parse(expression))
|
||||||
assert visitor.builtin_type_calls == calls
|
assert visitor.builtin_type_calls == calls
|
||||||
|
|
||||||
|
|
@ -98,8 +102,7 @@ def test_non_dict_exprs(expression, calls):
|
||||||
('builtins.dict()', []),
|
('builtins.dict()', []),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_dict_allow_kwargs_exprs(expression, calls):
|
def test_dict_allow_kwargs_exprs(visitor, expression, calls):
|
||||||
visitor = Visitor(ignore=set())
|
|
||||||
visitor.visit(ast.parse(expression))
|
visitor.visit(ast.parse(expression))
|
||||||
assert visitor.builtin_type_calls == calls
|
assert visitor.builtin_type_calls == calls
|
||||||
|
|
||||||
|
|
@ -111,18 +114,17 @@ def test_dict_allow_kwargs_exprs(expression, calls):
|
||||||
('dict(a=1, b=2, c=3)', [Call('dict', 1, 0)]),
|
('dict(a=1, b=2, c=3)', [Call('dict', 1, 0)]),
|
||||||
("dict(**{'a': 1, 'b': 2, 'c': 3})", [Call('dict', 1, 0)]),
|
("dict(**{'a': 1, 'b': 2, 'c': 3})", [Call('dict', 1, 0)]),
|
||||||
('builtins.dict()', []),
|
('builtins.dict()', []),
|
||||||
pytest.param('f(dict())', [Call('dict', 1, 2)], id='nested'),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_dict_no_allow_kwargs_exprs(expression, calls):
|
def test_dict_no_allow_kwargs_exprs(expression, calls):
|
||||||
visitor = Visitor(ignore=set(), allow_dict_kwargs=False)
|
visitor = Visitor(allow_dict_kwargs=False)
|
||||||
visitor.visit(ast.parse(expression))
|
visitor.visit(ast.parse(expression))
|
||||||
assert visitor.builtin_type_calls == calls
|
assert visitor.builtin_type_calls == calls
|
||||||
|
|
||||||
|
|
||||||
def test_ignore_constructors():
|
def test_ignore_constructors():
|
||||||
visitor = Visitor(
|
visitor = Visitor(
|
||||||
ignore={'complex', 'dict', 'float', 'int', 'list', 'str', 'tuple'},
|
ignore=('complex', 'dict', 'float', 'int', 'list', 'str', 'tuple'),
|
||||||
)
|
)
|
||||||
visitor.visit(ast.parse(BUILTIN_CONSTRUCTORS))
|
visitor.visit(ast.parse(BUILTIN_CONSTRUCTORS))
|
||||||
assert visitor.builtin_type_calls == []
|
assert visitor.builtin_type_calls == []
|
||||||
|
|
|
||||||
15
tests/check_byte_order_marker_test.py
Normal file
15
tests/check_byte_order_marker_test.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pre_commit_hooks import check_byte_order_marker
|
||||||
|
|
||||||
|
|
||||||
|
def test_failure(tmpdir):
|
||||||
|
f = tmpdir.join('f.txt')
|
||||||
|
f.write_text('ohai', encoding='utf-8-sig')
|
||||||
|
assert check_byte_order_marker.main((str(f),)) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_success(tmpdir):
|
||||||
|
f = tmpdir.join('f.txt')
|
||||||
|
f.write_text('ohai', encoding='utf-8')
|
||||||
|
assert check_byte_order_marker.main((str(f),)) == 0
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -27,10 +27,10 @@ def f1_is_a_conflict_file(tmpdir):
|
||||||
|
|
||||||
cmd_output('git', 'clone', str(repo1), str(repo2))
|
cmd_output('git', 'clone', str(repo1), str(repo2))
|
||||||
|
|
||||||
# Commit in mainline
|
# Commit in master
|
||||||
with repo1.as_cwd():
|
with repo1.as_cwd():
|
||||||
repo1_f1.write('parent\n')
|
repo1_f1.write('parent\n')
|
||||||
git_commit('-am', 'mainline commit2')
|
git_commit('-am', 'master commit2')
|
||||||
|
|
||||||
# Commit in clone and pull
|
# Commit in clone and pull
|
||||||
with repo2.as_cwd():
|
with repo2.as_cwd():
|
||||||
|
|
@ -82,10 +82,10 @@ def repository_pending_merge(tmpdir):
|
||||||
|
|
||||||
cmd_output('git', 'clone', str(repo1), str(repo2))
|
cmd_output('git', 'clone', str(repo1), str(repo2))
|
||||||
|
|
||||||
# Commit in mainline
|
# Commit in master
|
||||||
with repo1.as_cwd():
|
with repo1.as_cwd():
|
||||||
repo1_f1.write('parent\n')
|
repo1_f1.write('parent\n')
|
||||||
git_commit('-am', 'mainline commit2')
|
git_commit('-am', 'master commit2')
|
||||||
|
|
||||||
# Commit in clone and pull without committing
|
# Commit in clone and pull without committing
|
||||||
with repo2.as_cwd():
|
with repo2.as_cwd():
|
||||||
|
|
@ -112,7 +112,7 @@ def test_merge_conflicts_git(capsys):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'contents', (b'<<<<<<< HEAD\n', b'=======\n', b'>>>>>>> main\n'),
|
'contents', (b'<<<<<<< HEAD\n', b'=======\n', b'>>>>>>> master\n'),
|
||||||
)
|
)
|
||||||
def test_merge_conflicts_failing(contents, repository_pending_merge):
|
def test_merge_conflicts_failing(contents, repository_pending_merge):
|
||||||
repository_pending_merge.join('f2').write_binary(contents)
|
repository_pending_merge.join('f2').write_binary(contents)
|
||||||
|
|
@ -150,7 +150,7 @@ def test_worktree_merge_conflicts(f1_is_a_conflict_file, tmpdir, capsys):
|
||||||
cmd_output('git', 'worktree', 'add', str(worktree))
|
cmd_output('git', 'worktree', 'add', str(worktree))
|
||||||
with worktree.as_cwd():
|
with worktree.as_cwd():
|
||||||
cmd_output(
|
cmd_output(
|
||||||
'git', 'pull', '--no-rebase', 'origin', 'HEAD', retcode=None,
|
'git', 'pull', '--no-rebase', 'origin', 'master', retcode=None,
|
||||||
)
|
)
|
||||||
msg = f1_is_a_conflict_file.join('.git/worktrees/worktree/MERGE_MSG')
|
msg = f1_is_a_conflict_file.join('.git/worktrees/worktree/MERGE_MSG')
|
||||||
assert msg.exists()
|
assert msg.exists()
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ def test_passing(tmpdir):
|
||||||
# tags are ok
|
# tags are ok
|
||||||
b'https://github.com/asottile/test/blob/1.0.0/foo%20bar#L1\n'
|
b'https://github.com/asottile/test/blob/1.0.0/foo%20bar#L1\n'
|
||||||
# links to files but not line numbers are ok
|
# links to files but not line numbers are ok
|
||||||
b'https://github.com/asottile/test/blob/main/foo%20bar\n'
|
b'https://github.com/asottile/test/blob/master/foo%20bar\n'
|
||||||
# regression test for overly-greedy regex
|
# regression test for overly-greedy regex
|
||||||
b'https://github.com/ yes / no ? /blob/main/foo#L1\n',
|
b'https://github.com/ yes / no ? /blob/master/foo#L1\n',
|
||||||
)
|
)
|
||||||
assert not main((str(f),))
|
assert not main((str(f),))
|
||||||
|
|
||||||
|
|
@ -26,15 +26,17 @@ def test_passing(tmpdir):
|
||||||
def test_failing(tmpdir, capsys):
|
def test_failing(tmpdir, capsys):
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
tmpdir.join('f.txt').write_binary(
|
tmpdir.join('f.txt').write_binary(
|
||||||
b'https://github.com/asottile/test/blob/main/foo#L1\n'
|
b'https://github.com/asottile/test/blob/master/foo#L1\n'
|
||||||
|
b'https://example.com/asottile/test/blob/master/foo#L1\n'
|
||||||
b'https://example.com/asottile/test/blob/main/foo#L1\n',
|
b'https://example.com/asottile/test/blob/main/foo#L1\n',
|
||||||
)
|
)
|
||||||
|
|
||||||
assert main(('f.txt', '--additional-github-domain', 'example.com'))
|
assert main(('f.txt', '--additional-github-domain', 'example.com'))
|
||||||
out, _ = capsys.readouterr()
|
out, _ = capsys.readouterr()
|
||||||
assert out == (
|
assert out == (
|
||||||
'f.txt:1:https://github.com/asottile/test/blob/main/foo#L1\n'
|
'f.txt:1:https://github.com/asottile/test/blob/master/foo#L1\n'
|
||||||
'f.txt:2:https://example.com/asottile/test/blob/main/foo#L1\n'
|
'f.txt:2:https://example.com/asottile/test/blob/master/foo#L1\n'
|
||||||
|
'f.txt:3:https://example.com/asottile/test/blob/main/foo#L1\n'
|
||||||
'\n'
|
'\n'
|
||||||
'Non-permanent github link detected.\n'
|
'Non-permanent github link detected.\n'
|
||||||
'On any page on github press [y] to load a permalink.\n'
|
'On any page on github press [y] to load a permalink.\n'
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,7 @@ from pre_commit_hooks.file_contents_sorter import PASS
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('input_s', 'argv', 'expected_retval', 'output'),
|
('input_s', 'argv', 'expected_retval', 'output'),
|
||||||
(
|
(
|
||||||
(b'', [], PASS, b''),
|
(b'', [], FAIL, b'\n'),
|
||||||
(b'\n', [], FAIL, b''),
|
|
||||||
(b'\n\n', [], FAIL, b''),
|
|
||||||
(b'lonesome\n', [], PASS, b'lonesome\n'),
|
(b'lonesome\n', [], PASS, b'lonesome\n'),
|
||||||
(b'missing_newline', [], FAIL, b'missing_newline\n'),
|
(b'missing_newline', [], FAIL, b'missing_newline\n'),
|
||||||
(b'newline\nmissing', [], FAIL, b'missing\nnewline\n'),
|
(b'newline\nmissing', [], FAIL, b'missing\nnewline\n'),
|
||||||
|
|
@ -67,6 +65,18 @@ from pre_commit_hooks.file_contents_sorter import PASS
|
||||||
FAIL,
|
FAIL,
|
||||||
b'Fie\nFoe\nfee\nfum\n',
|
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):
|
def test_integration(input_s, argv, expected_retval, output, tmpdir):
|
||||||
|
|
@ -77,24 +87,3 @@ def test_integration(input_s, argv, expected_retval, output, tmpdir):
|
||||||
|
|
||||||
assert path.read_binary() == output
|
assert path.read_binary() == output
|
||||||
assert output_retval == expected_retval
|
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)
|
|
||||||
|
|
|
||||||
161
tests/fix_encoding_pragma_test.py
Normal file
161
tests/fix_encoding_pragma_test.py
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import io
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pre_commit_hooks.fix_encoding_pragma import _normalize_pragma
|
||||||
|
from pre_commit_hooks.fix_encoding_pragma import fix_encoding_pragma
|
||||||
|
from pre_commit_hooks.fix_encoding_pragma import main
|
||||||
|
|
||||||
|
|
||||||
|
def test_integration_inserting_pragma(tmpdir):
|
||||||
|
path = tmpdir.join('foo.py')
|
||||||
|
path.write_binary(b'import httplib\n')
|
||||||
|
|
||||||
|
assert main((str(path),)) == 1
|
||||||
|
|
||||||
|
assert path.read_binary() == (
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'import httplib\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_integration_ok(tmpdir):
|
||||||
|
path = tmpdir.join('foo.py')
|
||||||
|
path.write_binary(b'# -*- coding: utf-8 -*-\nx = 1\n')
|
||||||
|
assert main((str(path),)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_integration_remove(tmpdir):
|
||||||
|
path = tmpdir.join('foo.py')
|
||||||
|
path.write_binary(b'# -*- coding: utf-8 -*-\nx = 1\n')
|
||||||
|
|
||||||
|
assert main((str(path), '--remove')) == 1
|
||||||
|
|
||||||
|
assert path.read_binary() == b'x = 1\n'
|
||||||
|
|
||||||
|
|
||||||
|
def test_integration_remove_ok(tmpdir):
|
||||||
|
path = tmpdir.join('foo.py')
|
||||||
|
path.write_binary(b'x = 1\n')
|
||||||
|
assert main((str(path), '--remove')) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'input_str',
|
||||||
|
(
|
||||||
|
b'',
|
||||||
|
(
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'x = 1\n'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
b'#!/usr/bin/env python\n'
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'foo = "bar"\n'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_ok_inputs(input_str):
|
||||||
|
bytesio = io.BytesIO(input_str)
|
||||||
|
assert fix_encoding_pragma(bytesio) == 0
|
||||||
|
bytesio.seek(0)
|
||||||
|
assert bytesio.read() == input_str
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
('input_str', 'output'),
|
||||||
|
(
|
||||||
|
(
|
||||||
|
b'import httplib\n',
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'import httplib\n',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
b'#!/usr/bin/env python\n'
|
||||||
|
b'x = 1\n',
|
||||||
|
b'#!/usr/bin/env python\n'
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'x = 1\n',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
b'#coding=utf-8\n'
|
||||||
|
b'x = 1\n',
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'x = 1\n',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
b'#!/usr/bin/env python\n'
|
||||||
|
b'#coding=utf8\n'
|
||||||
|
b'x = 1\n',
|
||||||
|
b'#!/usr/bin/env python\n'
|
||||||
|
b'# -*- coding: utf-8 -*-\n'
|
||||||
|
b'x = 1\n',
|
||||||
|
),
|
||||||
|
# These should each get truncated
|
||||||
|
(b'#coding: utf-8\n', b''),
|
||||||
|
(b'# -*- coding: utf-8 -*-\n', b''),
|
||||||
|
(b'#!/usr/bin/env python\n', b''),
|
||||||
|
(b'#!/usr/bin/env python\n#coding: utf8\n', b''),
|
||||||
|
(b'#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n', b''),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_not_ok_inputs(input_str, output):
|
||||||
|
bytesio = io.BytesIO(input_str)
|
||||||
|
assert fix_encoding_pragma(bytesio) == 1
|
||||||
|
bytesio.seek(0)
|
||||||
|
assert bytesio.read() == output
|
||||||
|
|
||||||
|
|
||||||
|
def test_ok_input_alternate_pragma():
|
||||||
|
input_s = b'# coding: utf-8\nx = 1\n'
|
||||||
|
bytesio = io.BytesIO(input_s)
|
||||||
|
ret = fix_encoding_pragma(bytesio, expected_pragma=b'# coding: utf-8')
|
||||||
|
assert ret == 0
|
||||||
|
bytesio.seek(0)
|
||||||
|
assert bytesio.read() == input_s
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_ok_input_alternate_pragma():
|
||||||
|
bytesio = io.BytesIO(b'x = 1\n')
|
||||||
|
ret = fix_encoding_pragma(bytesio, expected_pragma=b'# coding: utf-8')
|
||||||
|
assert ret == 1
|
||||||
|
bytesio.seek(0)
|
||||||
|
assert bytesio.read() == b'# coding: utf-8\nx = 1\n'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
('input_s', 'expected'),
|
||||||
|
(
|
||||||
|
('# coding: utf-8', b'# coding: utf-8'),
|
||||||
|
# trailing whitespace
|
||||||
|
('# coding: utf-8\n', b'# coding: utf-8'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_normalize_pragma(input_s, expected):
|
||||||
|
assert _normalize_pragma(input_s) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_integration_alternate_pragma(tmpdir, capsys):
|
||||||
|
f = tmpdir.join('f.py')
|
||||||
|
f.write('x = 1\n')
|
||||||
|
|
||||||
|
pragma = '# coding: utf-8'
|
||||||
|
assert main((str(f), '--pragma', pragma)) == 1
|
||||||
|
assert f.read() == '# coding: utf-8\nx = 1\n'
|
||||||
|
out, _ = capsys.readouterr()
|
||||||
|
assert out == f'Added `# coding: utf-8` to {str(f)}\n'
|
||||||
|
|
||||||
|
|
||||||
|
def test_crlf_ok(tmpdir):
|
||||||
|
f = tmpdir.join('f.py')
|
||||||
|
f.write_binary(b'# -*- coding: utf-8 -*-\r\nx = 1\r\n')
|
||||||
|
assert not main((str(f),))
|
||||||
|
|
||||||
|
|
||||||
|
def test_crfl_adds(tmpdir):
|
||||||
|
f = tmpdir.join('f.py')
|
||||||
|
f.write_binary(b'x = 1\r\n')
|
||||||
|
assert main((str(f),))
|
||||||
|
assert f.read_binary() == b'# -*- coding: utf-8 -*-\r\nx = 1\r\n'
|
||||||
|
|
@ -11,13 +11,13 @@ from testing.util import git_commit
|
||||||
def test_other_branch(temp_git_dir):
|
def test_other_branch(temp_git_dir):
|
||||||
with temp_git_dir.as_cwd():
|
with temp_git_dir.as_cwd():
|
||||||
cmd_output('git', 'checkout', '-b', 'anotherbranch')
|
cmd_output('git', 'checkout', '-b', 'anotherbranch')
|
||||||
assert is_on_branch({'placeholder'}) is False
|
assert is_on_branch({'master'}) is False
|
||||||
|
|
||||||
|
|
||||||
def test_multi_branch(temp_git_dir):
|
def test_multi_branch(temp_git_dir):
|
||||||
with temp_git_dir.as_cwd():
|
with temp_git_dir.as_cwd():
|
||||||
cmd_output('git', 'checkout', '-b', 'another/branch')
|
cmd_output('git', 'checkout', '-b', 'another/branch')
|
||||||
assert is_on_branch({'placeholder'}) is False
|
assert is_on_branch({'master'}) is False
|
||||||
|
|
||||||
|
|
||||||
def test_multi_branch_fail(temp_git_dir):
|
def test_multi_branch_fail(temp_git_dir):
|
||||||
|
|
@ -26,10 +26,9 @@ def test_multi_branch_fail(temp_git_dir):
|
||||||
assert is_on_branch({'another/branch'}) is True
|
assert is_on_branch({'another/branch'}) is True
|
||||||
|
|
||||||
|
|
||||||
def test_exact_branch(temp_git_dir):
|
def test_master_branch(temp_git_dir):
|
||||||
with temp_git_dir.as_cwd():
|
with temp_git_dir.as_cwd():
|
||||||
cmd_output('git', 'checkout', '-b', 'branchname')
|
assert is_on_branch({'master'}) is True
|
||||||
assert is_on_branch({'branchname'}) is True
|
|
||||||
|
|
||||||
|
|
||||||
def test_main_branch_call(temp_git_dir):
|
def test_main_branch_call(temp_git_dir):
|
||||||
|
|
@ -51,11 +50,11 @@ def test_branch_pattern_fail(temp_git_dir):
|
||||||
assert is_on_branch(set(), {'another/.*'}) is True
|
assert is_on_branch(set(), {'another/.*'}) is True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('branch_name', ('somebranch', 'another/branch'))
|
@pytest.mark.parametrize('branch_name', ('master', 'another/branch'))
|
||||||
def test_branch_pattern_multiple_branches_fail(temp_git_dir, branch_name):
|
def test_branch_pattern_multiple_branches_fail(temp_git_dir, branch_name):
|
||||||
with temp_git_dir.as_cwd():
|
with temp_git_dir.as_cwd():
|
||||||
cmd_output('git', 'checkout', '-b', branch_name)
|
cmd_output('git', 'checkout', '-b', branch_name)
|
||||||
assert main(('--branch', 'somebranch', '--pattern', 'another/.*'))
|
assert main(('--branch', 'master', '--pattern', 'another/.*'))
|
||||||
|
|
||||||
|
|
||||||
def test_main_default_call(temp_git_dir):
|
def test_main_default_call(temp_git_dir):
|
||||||
|
|
|
||||||
|
|
@ -82,24 +82,6 @@ def test_autofix_main(tmpdir):
|
||||||
assert ret == 0
|
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():
|
def test_orderfile_get_pretty_format():
|
||||||
ret = main((
|
ret = main((
|
||||||
'--top-keys=alist', get_resource_path('pretty_formatted_json.json'),
|
'--top-keys=alist', get_resource_path('pretty_formatted_json.json'),
|
||||||
|
|
|
||||||
|
|
@ -68,12 +68,6 @@ from pre_commit_hooks.requirements_txt_fixer import Requirement
|
||||||
b'f<=2\n'
|
b'f<=2\n'
|
||||||
b'g<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'ocflib\nDjango\nPyMySQL\n', FAIL, b'Django\nocflib\nPyMySQL\n'),
|
||||||
(
|
(
|
||||||
b'-e git+ssh://git_url@tag#egg=ocflib\nDjango\nPyMySQL\n',
|
b'-e git+ssh://git_url@tag#egg=ocflib\nDjango\nPyMySQL\n',
|
||||||
|
|
@ -82,8 +76,6 @@ from pre_commit_hooks.requirements_txt_fixer import Requirement
|
||||||
),
|
),
|
||||||
(b'bar\npkg-resources==0.0.0\nfoo\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'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',
|
b'git+ssh://git_url@tag#egg=ocflib\nDjango\nijk\n',
|
||||||
FAIL,
|
FAIL,
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,6 @@ TESTS = (
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
('"foo""bar"', "'foo''bar'", 1),
|
('"foo""bar"', "'foo''bar'", 1),
|
||||||
pytest.param(
|
|
||||||
"f'hello{\"world\"}'",
|
|
||||||
"f'hello{\"world\"}'",
|
|
||||||
0,
|
|
||||||
id='ignore nested fstrings',
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
2
tox.ini
2
tox.ini
|
|
@ -1,5 +1,5 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py,pre-commit
|
envlist = py37,py38,pypy3,pre-commit
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = -rrequirements-dev.txt
|
deps = -rrequirements-dev.txt
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue