summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/pre-test/action.yml2
-rw-r--r--.github/workflows/languages.yaml4
-rw-r--r--.github/workflows/main.yml8
-rw-r--r--.pre-commit-config.yaml6
-rw-r--r--CHANGELOG.md18
-rw-r--r--pre_commit/clientlib.py22
-rw-r--r--pre_commit/commands/autoupdate.py2
-rw-r--r--pre_commit/commands/hook_impl.py2
-rw-r--r--pre_commit/commands/run.py10
-rw-r--r--pre_commit/commands/validate_config.py2
-rw-r--r--pre_commit/commands/validate_manifest.py2
-rw-r--r--pre_commit/envcontext.py9
-rw-r--r--pre_commit/error_handler.py2
-rw-r--r--pre_commit/file_lock.py2
-rw-r--r--pre_commit/git.py2
-rw-r--r--pre_commit/hook.py2
-rw-r--r--pre_commit/lang_base.py4
-rw-r--r--pre_commit/languages/conda.py4
-rw-r--r--pre_commit/languages/coursier.py4
-rw-r--r--pre_commit/languages/dart.py4
-rw-r--r--pre_commit/languages/docker.py2
-rw-r--r--pre_commit/languages/docker_image.py2
-rw-r--r--pre_commit/languages/dotnet.py4
-rw-r--r--pre_commit/languages/fail.py2
-rw-r--r--pre_commit/languages/golang.py4
-rw-r--r--pre_commit/languages/haskell.py4
-rw-r--r--pre_commit/languages/lua.py4
-rw-r--r--pre_commit/languages/node.py4
-rw-r--r--pre_commit/languages/perl.py4
-rw-r--r--pre_commit/languages/pygrep.py4
-rw-r--r--pre_commit/languages/python.py10
-rw-r--r--pre_commit/languages/r.py4
-rw-r--r--pre_commit/languages/ruby.py7
-rw-r--r--pre_commit/languages/rust.py6
-rw-r--r--pre_commit/languages/script.py2
-rw-r--r--pre_commit/languages/swift.py4
-rw-r--r--pre_commit/logging_handler.py2
-rw-r--r--pre_commit/main.py2
-rw-r--r--pre_commit/meta_hooks/check_hooks_apply.py2
-rw-r--r--pre_commit/meta_hooks/check_useless_excludes.py4
-rw-r--r--pre_commit/meta_hooks/identity.py2
-rw-r--r--pre_commit/parse_shebang.py2
-rw-r--r--pre_commit/repository.py12
-rw-r--r--pre_commit/staged_files_only.py2
-rw-r--r--pre_commit/store.py4
-rw-r--r--pre_commit/util.py51
-rw-r--r--pre_commit/xargs.py8
-rw-r--r--setup.cfg4
-rw-r--r--testing/language_helpers.py2
-rwxr-xr-xtesting/make-archives2
-rw-r--r--tests/clientlib_test.py195
-rw-r--r--tests/commands/install_uninstall_test.py10
-rw-r--r--tests/commands/run_test.py2
-rw-r--r--tests/repository_test.py28
-rw-r--r--tests/store_test.py2
55 files changed, 266 insertions, 248 deletions
diff --git a/.github/actions/pre-test/action.yml b/.github/actions/pre-test/action.yml
index 9d1eb2d..b70c942 100644
--- a/.github/actions/pre-test/action.yml
+++ b/.github/actions/pre-test/action.yml
@@ -6,4 +6,4 @@ runs:
using: composite
steps:
- uses: asottile/workflows/.github/actions/latest-git@v1.4.0
- if: inputs.env == 'py38' && runner.os == 'Linux'
+ if: inputs.env == 'py39' && runner.os == 'Linux'
diff --git a/.github/workflows/languages.yaml b/.github/workflows/languages.yaml
index 5a6ae9c..7d50535 100644
--- a/.github/workflows/languages.yaml
+++ b/.github/workflows/languages.yaml
@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-python@v4
with:
- python-version: 3.8
+ python-version: 3.9
- name: install deps
run: python -mpip install -e . -r requirements-dev.txt
- name: vars
@@ -39,7 +39,7 @@ jobs:
- uses: asottile/workflows/.github/actions/fast-checkout@v1.4.0
- uses: actions/setup-python@v4
with:
- python-version: 3.8
+ python-version: 3.9
- run: echo "$CONDA\Scripts" >> "$GITHUB_PATH"
shell: bash
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 903d247..2355b66 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -12,12 +12,12 @@ concurrency:
jobs:
main-windows:
- uses: asottile/workflows/.github/workflows/tox.yml@v1.4.0
+ uses: asottile/workflows/.github/workflows/tox.yml@v1.6.0
with:
- env: '["py38"]'
+ env: '["py39"]'
os: windows-latest
main-linux:
- uses: asottile/workflows/.github/workflows/tox.yml@v1.4.0
+ uses: asottile/workflows/.github/workflows/tox.yml@v1.6.0
with:
- env: '["py38", "py39", "py310"]'
+ env: '["py39", "py310", "py311", "py312"]'
os: ubuntu-latest
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5381cd6..4433e4e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -18,7 +18,7 @@ repos:
hooks:
- id: reorder-python-imports
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/)
- 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.1.0
hooks:
@@ -27,7 +27,7 @@ repos:
rev: v3.15.0
hooks:
- id: pyupgrade
- args: [--py38-plus]
+ args: [--py39-plus]
- repo: https://github.com/hhatto/autopep8
rev: v2.0.4
hooks:
@@ -37,7 +37,7 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.5.1
+ rev: v1.7.1
hooks:
- id: mypy
additional_dependencies: [types-all]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7a1b61a..340ac47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,21 @@
+3.6.0 - 2023-12-09
+==================
+
+### Features
+- Check `minimum_pre_commit_version` first when parsing configs.
+ - #3092 PR by @asottile.
+
+### Fixes
+- Fix deprecation warnings for `importlib.resources`.
+ - #3043 PR by @asottile.
+- Fix deprecation warnings for rmtree.
+ - #3079 PR by @edgarrmondragon.
+
+### Updating
+- Drop support for python<3.9.
+ - #3042 PR by @asottile.
+ - #3093 PR by @asottile.
+
3.5.0 - 2023-10-13
==================
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index d0651ca..a49465e 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -5,9 +5,9 @@ import logging
import re
import shlex
import sys
+from collections.abc import Sequence
from typing import Any
from typing import NamedTuple
-from typing import Sequence
import cfgv
from identify.identify import ALL_TAGS
@@ -102,6 +102,13 @@ class StagesMigration(StagesMigrationNoDefault):
MANIFEST_HOOK_DICT = cfgv.Map(
'Hook', 'id',
+ # check first in case it uses some newer, incompatible feature
+ cfgv.Optional(
+ 'minimum_pre_commit_version',
+ cfgv.check_and(cfgv.check_string, check_min_version),
+ '0',
+ ),
+
cfgv.Required('id', cfgv.check_string),
cfgv.Required('name', cfgv.check_string),
cfgv.Required('entry', cfgv.check_string),
@@ -124,7 +131,6 @@ MANIFEST_HOOK_DICT = cfgv.Map(
cfgv.Optional('description', cfgv.check_string, ''),
cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
cfgv.Optional('log_file', cfgv.check_string, ''),
- cfgv.Optional('minimum_pre_commit_version', cfgv.check_string, '0'),
cfgv.Optional('require_serial', cfgv.check_bool, False),
StagesMigration('stages', []),
cfgv.Optional('verbose', cfgv.check_bool, False),
@@ -345,6 +351,13 @@ DEFAULT_LANGUAGE_VERSION = cfgv.Map(
CONFIG_SCHEMA = cfgv.Map(
'Config', None,
+ # check first in case it uses some newer, incompatible feature
+ cfgv.Optional(
+ 'minimum_pre_commit_version',
+ cfgv.check_and(cfgv.check_string, check_min_version),
+ '0',
+ ),
+
cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),
cfgv.Optional(
'default_install_hook_types',
@@ -358,11 +371,6 @@ CONFIG_SCHEMA = cfgv.Map(
cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('fail_fast', cfgv.check_bool, False),
- cfgv.Optional(
- 'minimum_pre_commit_version',
- cfgv.check_and(cfgv.check_string, check_min_version),
- '0',
- ),
cfgv.WarnAdditionalKeys(
(
'repos',
diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py
index e7725fd..aa0c5e2 100644
--- a/pre_commit/commands/autoupdate.py
+++ b/pre_commit/commands/autoupdate.py
@@ -4,9 +4,9 @@ import concurrent.futures
import os.path
import re
import tempfile
+from collections.abc import Sequence
from typing import Any
from typing import NamedTuple
-from typing import Sequence
import pre_commit.constants as C
from pre_commit import git
diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py
index dab2135..49a80b7 100644
--- a/pre_commit/commands/hook_impl.py
+++ b/pre_commit/commands/hook_impl.py
@@ -4,7 +4,7 @@ import argparse
import os.path
import subprocess
import sys
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit.commands.run import run
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 41ba4ec..076f16d 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -9,11 +9,11 @@ import re
import subprocess
import time
import unicodedata
+from collections.abc import Generator
+from collections.abc import Iterable
+from collections.abc import MutableMapping
+from collections.abc import Sequence
from typing import Any
-from typing import Generator
-from typing import Iterable
-from typing import MutableMapping
-from typing import Sequence
from identify.identify import tags_from_path
@@ -74,7 +74,7 @@ class Classifier:
def __init__(self, filenames: Iterable[str]) -> None:
self.filenames = [f for f in filenames if os.path.lexists(f)]
- @functools.lru_cache(maxsize=None)
+ @functools.cache
def _types_for_file(self, filename: str) -> set[str]:
return tags_from_path(filename)
diff --git a/pre_commit/commands/validate_config.py b/pre_commit/commands/validate_config.py
index 24bd313..b3de635 100644
--- a/pre_commit/commands/validate_config.py
+++ b/pre_commit/commands/validate_config.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import clientlib
diff --git a/pre_commit/commands/validate_manifest.py b/pre_commit/commands/validate_manifest.py
index 419031a..8493c6e 100644
--- a/pre_commit/commands/validate_manifest.py
+++ b/pre_commit/commands/validate_manifest.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import clientlib
diff --git a/pre_commit/envcontext.py b/pre_commit/envcontext.py
index 4f59560..1f816ce 100644
--- a/pre_commit/envcontext.py
+++ b/pre_commit/envcontext.py
@@ -3,10 +3,9 @@ from __future__ import annotations
import contextlib
import enum
import os
-from typing import Generator
-from typing import MutableMapping
+from collections.abc import Generator
+from collections.abc import MutableMapping
from typing import NamedTuple
-from typing import Tuple
from typing import Union
_Unset = enum.Enum('_Unset', 'UNSET')
@@ -18,9 +17,9 @@ class Var(NamedTuple):
default: str = ''
-SubstitutionT = Tuple[Union[str, Var], ...]
+SubstitutionT = tuple[Union[str, Var], ...]
ValueT = Union[str, _Unset, SubstitutionT]
-PatchesT = Tuple[Tuple[str, ValueT], ...]
+PatchesT = tuple[tuple[str, ValueT], ...]
def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str:
diff --git a/pre_commit/error_handler.py b/pre_commit/error_handler.py
index d740ee3..73e608b 100644
--- a/pre_commit/error_handler.py
+++ b/pre_commit/error_handler.py
@@ -5,7 +5,7 @@ import functools
import os.path
import sys
import traceback
-from typing import Generator
+from collections.abc import Generator
from typing import IO
import pre_commit.constants as C
diff --git a/pre_commit/file_lock.py b/pre_commit/file_lock.py
index f67a586..d3dafb4 100644
--- a/pre_commit/file_lock.py
+++ b/pre_commit/file_lock.py
@@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib
import errno
import sys
+from collections.abc import Generator
from typing import Callable
-from typing import Generator
if sys.platform == 'win32': # pragma: no cover (windows)
diff --git a/pre_commit/git.py b/pre_commit/git.py
index 333dc7b..19aac38 100644
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -3,7 +3,7 @@ from __future__ import annotations
import logging
import os.path
import sys
-from typing import Mapping
+from collections.abc import Mapping
from pre_commit.errors import FatalError
from pre_commit.util import CalledProcessError
diff --git a/pre_commit/hook.py b/pre_commit/hook.py
index 6d436ca..309cd5b 100644
--- a/pre_commit/hook.py
+++ b/pre_commit/hook.py
@@ -1,9 +1,9 @@
from __future__ import annotations
import logging
+from collections.abc import Sequence
from typing import Any
from typing import NamedTuple
-from typing import Sequence
from pre_commit.prefix import Prefix
diff --git a/pre_commit/lang_base.py b/pre_commit/lang_base.py
index 4a993ea..5303948 100644
--- a/pre_commit/lang_base.py
+++ b/pre_commit/lang_base.py
@@ -5,12 +5,12 @@ import os
import random
import re
import shlex
+from collections.abc import Generator
+from collections.abc import Sequence
from typing import Any
from typing import ContextManager
-from typing import Generator
from typing import NoReturn
from typing import Protocol
-from typing import Sequence
import pre_commit.constants as C
from pre_commit import parse_shebang
diff --git a/pre_commit/languages/conda.py b/pre_commit/languages/conda.py
index 41c355e..80b3e15 100644
--- a/pre_commit/languages/conda.py
+++ b/pre_commit/languages/conda.py
@@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib
import os
import sys
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/coursier.py b/pre_commit/languages/coursier.py
index 9c5fbfe..6558bf6 100644
--- a/pre_commit/languages/coursier.py
+++ b/pre_commit/languages/coursier.py
@@ -2,8 +2,8 @@ from __future__ import annotations
import contextlib
import os.path
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/dart.py b/pre_commit/languages/dart.py
index e8539ca..129ac59 100644
--- a/pre_commit/languages/dart.py
+++ b/pre_commit/languages/dart.py
@@ -4,8 +4,8 @@ import contextlib
import os.path
import shutil
import tempfile
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py
index 8e53ca9..2632851 100644
--- a/pre_commit/languages/docker.py
+++ b/pre_commit/languages/docker.py
@@ -3,7 +3,7 @@ from __future__ import annotations
import hashlib
import json
import os
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.prefix import Prefix
diff --git a/pre_commit/languages/docker_image.py b/pre_commit/languages/docker_image.py
index 26f006e..a1a2c16 100644
--- a/pre_commit/languages/docker_image.py
+++ b/pre_commit/languages/docker_image.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.languages.docker import docker_cmd
diff --git a/pre_commit/languages/dotnet.py b/pre_commit/languages/dotnet.py
index e9568f2..e1202c4 100644
--- a/pre_commit/languages/dotnet.py
+++ b/pre_commit/languages/dotnet.py
@@ -6,8 +6,8 @@ import re
import tempfile
import xml.etree.ElementTree
import zipfile
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/fail.py b/pre_commit/languages/fail.py
index a8ec6a5..6ac4d76 100644
--- a/pre_commit/languages/fail.py
+++ b/pre_commit/languages/fail.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.prefix import Prefix
diff --git a/pre_commit/languages/golang.py b/pre_commit/languages/golang.py
index bea91e9..4c13d8f 100644
--- a/pre_commit/languages/golang.py
+++ b/pre_commit/languages/golang.py
@@ -12,11 +12,11 @@ import tempfile
import urllib.error
import urllib.request
import zipfile
+from collections.abc import Generator
+from collections.abc import Sequence
from typing import ContextManager
-from typing import Generator
from typing import IO
from typing import Protocol
-from typing import Sequence
import pre_commit.constants as C
from pre_commit import lang_base
diff --git a/pre_commit/languages/haskell.py b/pre_commit/languages/haskell.py
index 76442eb..c6945c8 100644
--- a/pre_commit/languages/haskell.py
+++ b/pre_commit/languages/haskell.py
@@ -2,8 +2,8 @@ from __future__ import annotations
import contextlib
import os.path
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/lua.py b/pre_commit/languages/lua.py
index 12d0661..a475ec9 100644
--- a/pre_commit/languages/lua.py
+++ b/pre_commit/languages/lua.py
@@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib
import os
import sys
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py
index 3e22dc7..d49c0e3 100644
--- a/pre_commit/languages/node.py
+++ b/pre_commit/languages/node.py
@@ -4,8 +4,8 @@ import contextlib
import functools
import os
import sys
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
import pre_commit.constants as C
from pre_commit import lang_base
diff --git a/pre_commit/languages/perl.py b/pre_commit/languages/perl.py
index 2a7f162..61b1d11 100644
--- a/pre_commit/languages/perl.py
+++ b/pre_commit/languages/perl.py
@@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib
import os
import shlex
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/pygrep.py b/pre_commit/languages/pygrep.py
index ec55560..72a9345 100644
--- a/pre_commit/languages/pygrep.py
+++ b/pre_commit/languages/pygrep.py
@@ -3,9 +3,9 @@ from __future__ import annotations
import argparse
import re
import sys
+from collections.abc import Sequence
+from re import Pattern
from typing import NamedTuple
-from typing import Pattern
-from typing import Sequence
from pre_commit import lang_base
from pre_commit import output
diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py
index 3ef3436..9f4bf69 100644
--- a/pre_commit/languages/python.py
+++ b/pre_commit/languages/python.py
@@ -4,8 +4,8 @@ import contextlib
import functools
import os
import sys
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
import pre_commit.constants as C
from pre_commit import lang_base
@@ -24,7 +24,7 @@ ENVIRONMENT_DIR = 'py_env'
run_hook = lang_base.basic_run_hook
-@functools.lru_cache(maxsize=None)
+@functools.cache
def _version_info(exe: str) -> str:
prog = 'import sys;print(".".join(str(p) for p in sys.version_info))'
try:
@@ -65,7 +65,7 @@ def _find_by_py_launcher(
version: str,
) -> str | None: # pragma: no cover (windows only)
if version.startswith('python'):
- num = version[len('python'):]
+ num = version.removeprefix('python')
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
env = dict(os.environ, PYTHONIOENCODING='UTF-8')
try:
@@ -124,7 +124,7 @@ def _sys_executable_matches(version: str) -> bool:
return False
try:
- info = tuple(int(p) for p in version[len('python'):].split('.'))
+ info = tuple(int(p) for p in version.removeprefix('python').split('.'))
except ValueError:
return False
diff --git a/pre_commit/languages/r.py b/pre_commit/languages/r.py
index 6feb065..93b62bd 100644
--- a/pre_commit/languages/r.py
+++ b/pre_commit/languages/r.py
@@ -6,8 +6,8 @@ import shlex
import shutil
import tempfile
import textwrap
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py
index c88269f..0438ae0 100644
--- a/pre_commit/languages/ruby.py
+++ b/pre_commit/languages/ruby.py
@@ -6,9 +6,9 @@ import importlib.resources
import os.path
import shutil
import tarfile
-from typing import Generator
+from collections.abc import Generator
+from collections.abc import Sequence
from typing import IO
-from typing import Sequence
import pre_commit.constants as C
from pre_commit import lang_base
@@ -25,7 +25,8 @@ run_hook = lang_base.basic_run_hook
def _resource_bytesio(filename: str) -> IO[bytes]:
- return importlib.resources.open_binary('pre_commit.resources', filename)
+ files = importlib.resources.files('pre_commit.resources')
+ return files.joinpath(filename).open('rb')
@functools.lru_cache(maxsize=1)
diff --git a/pre_commit/languages/rust.py b/pre_commit/languages/rust.py
index 7eec0e7..7b04d6c 100644
--- a/pre_commit/languages/rust.py
+++ b/pre_commit/languages/rust.py
@@ -7,8 +7,8 @@ import shutil
import sys
import tempfile
import urllib.request
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
import pre_commit.constants as C
from pre_commit import lang_base
@@ -134,7 +134,7 @@ def install_environment(
packages_to_install: set[tuple[str, ...]] = {('--path', '.')}
for cli_dep in cli_deps:
- cli_dep = cli_dep[len('cli:'):]
+ cli_dep = cli_dep.removeprefix('cli:')
package, _, crate_version = cli_dep.partition(':')
if crate_version != '':
packages_to_install.add((package, '--version', crate_version))
diff --git a/pre_commit/languages/script.py b/pre_commit/languages/script.py
index 89a3ab2..1eaa1e2 100644
--- a/pre_commit/languages/script.py
+++ b/pre_commit/languages/script.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.prefix import Prefix
diff --git a/pre_commit/languages/swift.py b/pre_commit/languages/swift.py
index f16bb04..f7bfe84 100644
--- a/pre_commit/languages/swift.py
+++ b/pre_commit/languages/swift.py
@@ -2,8 +2,8 @@ from __future__ import annotations
import contextlib
import os
-from typing import Generator
-from typing import Sequence
+from collections.abc import Generator
+from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
diff --git a/pre_commit/logging_handler.py b/pre_commit/logging_handler.py
index 1b68fc7..cd33953 100644
--- a/pre_commit/logging_handler.py
+++ b/pre_commit/logging_handler.py
@@ -2,7 +2,7 @@ from __future__ import annotations
import contextlib
import logging
-from typing import Generator
+from collections.abc import Generator
from pre_commit import color
from pre_commit import output
diff --git a/pre_commit/main.py b/pre_commit/main.py
index 9dfce2c..18c978a 100644
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -4,7 +4,7 @@ import argparse
import logging
import os
import sys
-from typing import Sequence
+from collections.abc import Sequence
import pre_commit.constants as C
from pre_commit import clientlib
diff --git a/pre_commit/meta_hooks/check_hooks_apply.py b/pre_commit/meta_hooks/check_hooks_apply.py
index 7f491a2..84c142b 100644
--- a/pre_commit/meta_hooks/check_hooks_apply.py
+++ b/pre_commit/meta_hooks/check_hooks_apply.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import argparse
-from typing import Sequence
+from collections.abc import Sequence
import pre_commit.constants as C
from pre_commit import git
diff --git a/pre_commit/meta_hooks/check_useless_excludes.py b/pre_commit/meta_hooks/check_useless_excludes.py
index 8b0c106..664251a 100644
--- a/pre_commit/meta_hooks/check_useless_excludes.py
+++ b/pre_commit/meta_hooks/check_useless_excludes.py
@@ -2,8 +2,8 @@ from __future__ import annotations
import argparse
import re
-from typing import Iterable
-from typing import Sequence
+from collections.abc import Iterable
+from collections.abc import Sequence
from cfgv import apply_defaults
diff --git a/pre_commit/meta_hooks/identity.py b/pre_commit/meta_hooks/identity.py
index 72ee440..3e20bbc 100644
--- a/pre_commit/meta_hooks/identity.py
+++ b/pre_commit/meta_hooks/identity.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import sys
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit import output
diff --git a/pre_commit/parse_shebang.py b/pre_commit/parse_shebang.py
index 3ee04e8..043a9b5 100644
--- a/pre_commit/parse_shebang.py
+++ b/pre_commit/parse_shebang.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import os.path
-from typing import Mapping
+from collections.abc import Mapping
from typing import NoReturn
from identify.identify import parse_shebang_from_file
diff --git a/pre_commit/repository.py b/pre_commit/repository.py
index 040f238..aa84185 100644
--- a/pre_commit/repository.py
+++ b/pre_commit/repository.py
@@ -4,15 +4,14 @@ import json
import logging
import os
import shlex
+from collections.abc import Sequence
from typing import Any
-from typing import Sequence
import pre_commit.constants as C
from pre_commit.all_languages import languages
from pre_commit.clientlib import load_manifest
from pre_commit.clientlib import LOCAL
from pre_commit.clientlib import META
-from pre_commit.clientlib import parse_version
from pre_commit.hook import Hook
from pre_commit.lang_base import environment_dir
from pre_commit.prefix import Prefix
@@ -124,15 +123,6 @@ def _hook(
for dct in rest:
ret.update(dct)
- version = ret['minimum_pre_commit_version']
- if parse_version(version) > parse_version(C.VERSION):
- logger.error(
- f'The hook `{ret["id"]}` requires pre-commit version {version} '
- f'but version {C.VERSION} is installed. '
- f'Perhaps run `pip install --upgrade pre-commit`.',
- )
- exit(1)
-
lang = ret['language']
if ret['language_version'] == C.DEFAULT:
ret['language_version'] = root_config['default_language_version'][lang]
diff --git a/pre_commit/staged_files_only.py b/pre_commit/staged_files_only.py
index 8812356..fd28e1c 100644
--- a/pre_commit/staged_files_only.py
+++ b/pre_commit/staged_files_only.py
@@ -4,7 +4,7 @@ import contextlib
import logging
import os.path
import time
-from typing import Generator
+from collections.abc import Generator
from pre_commit import git
from pre_commit.errors import FatalError
diff --git a/pre_commit/store.py b/pre_commit/store.py
index 487e3e7..84bc09a 100644
--- a/pre_commit/store.py
+++ b/pre_commit/store.py
@@ -5,9 +5,9 @@ import logging
import os.path
import sqlite3
import tempfile
+from collections.abc import Generator
+from collections.abc import Sequence
from typing import Callable
-from typing import Generator
-from typing import Sequence
import pre_commit.constants as C
from pre_commit import file_lock
diff --git a/pre_commit/util.py b/pre_commit/util.py
index 4f8e835..b3682d4 100644
--- a/pre_commit/util.py
+++ b/pre_commit/util.py
@@ -8,10 +8,10 @@ import shutil
import stat
import subprocess
import sys
+from collections.abc import Generator
from types import TracebackType
from typing import Any
from typing import Callable
-from typing import Generator
from pre_commit import parse_shebang
@@ -36,7 +36,8 @@ def clean_path_on_failure(path: str) -> Generator[None, None, None]:
def resource_text(filename: str) -> str:
- return importlib.resources.read_text('pre_commit.resources', filename)
+ files = importlib.resources.files('pre_commit.resources')
+ return files.joinpath(filename).read_text()
def make_executable(filename: str) -> None:
@@ -201,24 +202,36 @@ else: # pragma: no cover
cmd_output_p = cmd_output_b
-def rmtree(path: str) -> None:
- """On windows, rmtree fails for readonly dirs."""
- def handle_remove_readonly(
- func: Callable[..., Any],
- path: str,
- exc: tuple[type[OSError], OSError, TracebackType],
+def _handle_readonly(
+ func: Callable[[str], object],
+ path: str,
+ exc: OSError,
+) -> None:
+ if (
+ func in (os.rmdir, os.remove, os.unlink) and
+ exc.errno in {errno.EACCES, errno.EPERM}
+ ):
+ for p in (path, os.path.dirname(path)):
+ os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR)
+ func(path)
+ else:
+ raise
+
+
+if sys.version_info < (3, 12): # pragma: <3.12 cover
+ def _handle_readonly_old(
+ func: Callable[[str], object],
+ path: str,
+ excinfo: tuple[type[OSError], OSError, TracebackType],
) -> None:
- excvalue = exc[1]
- if (
- func in (os.rmdir, os.remove, os.unlink) and
- excvalue.errno in {errno.EACCES, errno.EPERM}
- ):
- for p in (path, os.path.dirname(path)):
- os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR)
- func(path)
- else:
- raise
- shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
+ return _handle_readonly(func, path, excinfo[1])
+
+ def rmtree(path: str) -> None:
+ shutil.rmtree(path, ignore_errors=False, onerror=_handle_readonly_old)
+else: # pragma: >=3.12 cover
+ def rmtree(path: str) -> None:
+ """On windows, rmtree fails for readonly dirs."""
+ shutil.rmtree(path, ignore_errors=False, onexc=_handle_readonly)
def win_exe(s: str) -> str:
diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py
index a7493c0..22580f5 100644
--- a/pre_commit/xargs.py
+++ b/pre_commit/xargs.py
@@ -7,12 +7,12 @@ import multiprocessing
import os
import subprocess
import sys
+from collections.abc import Generator
+from collections.abc import Iterable
+from collections.abc import MutableMapping
+from collections.abc import Sequence
from typing import Any
from typing import Callable
-from typing import Generator
-from typing import Iterable
-from typing import MutableMapping
-from typing import Sequence
from typing import TypeVar
from pre_commit import parse_shebang
diff --git a/setup.cfg b/setup.cfg
index 7543835..24b94e2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = pre_commit
-version = 3.5.0
+version = 3.6.0
description = A framework for managing and maintaining multi-language pre-commit hooks.
long_description = file: README.md
long_description_content_type = text/markdown
@@ -24,7 +24,7 @@ install_requires =
nodeenv>=0.11.1
pyyaml>=5.1
virtualenv>=20.10.0
-python_requires = >=3.8
+python_requires = >=3.9
[options.packages.find]
exclude =
diff --git a/testing/language_helpers.py b/testing/language_helpers.py
index ead8dae..05c94eb 100644
--- a/testing/language_helpers.py
+++ b/testing/language_helpers.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import os
-from typing import Sequence
+from collections.abc import Sequence
from pre_commit.lang_base import Language
from pre_commit.prefix import Prefix
diff --git a/testing/make-archives b/testing/make-archives
index 8ec05e2..3c7ab9d 100755
--- a/testing/make-archives
+++ b/testing/make-archives
@@ -8,7 +8,7 @@ import shutil
import subprocess
import tarfile
import tempfile
-from typing import Sequence
+from collections.abc import Sequence
# This is a script for generating the tarred resources for git repo
diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py
index 568b2e9..eaa8a04 100644
--- a/tests/clientlib_test.py
+++ b/tests/clientlib_test.py
@@ -40,56 +40,51 @@ def test_check_type_tag_success():
@pytest.mark.parametrize(
- ('config_obj', 'expected'), (
- (
- {
- 'repos': [{
- 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
- 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
- 'hooks': [{'id': 'pyflakes', 'files': '\\.py$'}],
- }],
- },
- True,
- ),
- (
- {
- 'repos': [{
- 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
- 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
- 'hooks': [
- {
- 'id': 'pyflakes',
- 'files': '\\.py$',
- 'args': ['foo', 'bar', 'baz'],
- },
- ],
- }],
- },
- True,
- ),
- (
- {
- 'repos': [{
- 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
- 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
- 'hooks': [
- {
- 'id': 'pyflakes',
- 'files': '\\.py$',
- # Exclude pattern must be a string
- 'exclude': 0,
- 'args': ['foo', 'bar', 'baz'],
- },
- ],
- }],
- },
- False,
- ),
+ 'cfg',
+ (
+ {
+ 'repos': [{
+ 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
+ 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
+ 'hooks': [{'id': 'pyflakes', 'files': '\\.py$'}],
+ }],
+ },
+ {
+ 'repos': [{
+ 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
+ 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
+ 'hooks': [
+ {
+ 'id': 'pyflakes',
+ 'files': '\\.py$',
+ 'args': ['foo', 'bar', 'baz'],
+ },
+ ],
+ }],
+ },
),
)
-def test_config_valid(config_obj, expected):
- ret = is_valid_according_to_schema(config_obj, CONFIG_SCHEMA)
- assert ret is expected
+def test_config_valid(cfg):
+ assert is_valid_according_to_schema(cfg, CONFIG_SCHEMA)
+
+
+def test_invalid_config_wrong_type():
+ cfg = {
+ 'repos': [{
+ 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
+ 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
+ 'hooks': [
+ {
+ 'id': 'pyflakes',
+ 'files': '\\.py$',
+ # Exclude pattern must be a string
+ 'exclude': 0,
+ 'args': ['foo', 'bar', 'baz'],
+ },
+ ],
+ }],
+ }
+ assert not is_valid_according_to_schema(cfg, CONFIG_SCHEMA)
def test_local_hooks_with_rev_fails():
@@ -198,14 +193,13 @@ def test_warn_mutable_rev_conditional():
),
)
def test_sensible_regex_validators_dont_pass_none(validator_cls):
- key = 'files'
+ validator = validator_cls('files', cfgv.check_string)
with pytest.raises(cfgv.ValidationError) as excinfo:
- validator = validator_cls(key, cfgv.check_string)
- validator.check({key: None})
+ validator.check({'files': None})
assert str(excinfo.value) == (
'\n'
- f'==> At key: {key}'
+ '==> At key: files'
'\n'
'=====> Expected string got NoneType'
)
@@ -298,46 +292,36 @@ def test_validate_optional_sensible_regex_at_top_level(caplog, regex, warning):
@pytest.mark.parametrize(
- ('manifest_obj', 'expected'),
+ 'manifest_obj',
(
- (
- [{
- 'id': 'a',
- 'name': 'b',
- 'entry': 'c',
- 'language': 'python',
- 'files': r'\.py$',
- }],
- True,
- ),
- (
- [{
- 'id': 'a',
- 'name': 'b',
- 'entry': 'c',
- 'language': 'python',
- 'language_version': 'python3.4',
- 'files': r'\.py$',
- }],
- True,
- ),
- (
- # A regression in 0.13.5: always_run and files are permissible
- [{
- 'id': 'a',
- 'name': 'b',
- 'entry': 'c',
- 'language': 'python',
- 'files': '',
- 'always_run': True,
- }],
- True,
- ),
+ [{
+ 'id': 'a',
+ 'name': 'b',
+ 'entry': 'c',
+ 'language': 'python',
+ 'files': r'\.py$',
+ }],
+ [{
+ 'id': 'a',
+ 'name': 'b',
+ 'entry': 'c',
+ 'language': 'python',
+ 'language_version': 'python3.4',
+ 'files': r'\.py$',
+ }],
+ # A regression in 0.13.5: always_run and files are permissible
+ [{
+ 'id': 'a',
+ 'name': 'b',
+ 'entry': 'c',
+ 'language': 'python',
+ 'files': '',
+ 'always_run': True,
+ }],
),
)
-def test_valid_manifests(manifest_obj, expected):
- ret = is_valid_according_to_schema(manifest_obj, MANIFEST_SCHEMA)
- assert ret is expected
+def test_valid_manifests(manifest_obj):
+ assert is_valid_according_to_schema(manifest_obj, MANIFEST_SCHEMA)
@pytest.mark.parametrize(
@@ -393,8 +377,39 @@ def test_parse_version():
def test_minimum_pre_commit_version_failing():
+ cfg = {'repos': [], 'minimum_pre_commit_version': '999'}
+ with pytest.raises(cfgv.ValidationError) as excinfo:
+ cfgv.validate(cfg, CONFIG_SCHEMA)
+ assert str(excinfo.value) == (
+ f'\n'
+ f'==> At Config()\n'
+ f'==> At key: minimum_pre_commit_version\n'
+ f'=====> pre-commit version 999 is required but version {C.VERSION} '
+ f'is installed. Perhaps run `pip install --upgrade pre-commit`.'
+ )
+
+
+def test_minimum_pre_commit_version_failing_in_config():
+ cfg = {'repos': [sample_local_config()]}
+ cfg['repos'][0]['hooks'][0]['minimum_pre_commit_version'] = '999'
+ with pytest.raises(cfgv.ValidationError) as excinfo:
+ cfgv.validate(cfg, CONFIG_SCHEMA)
+ assert str(excinfo.value) == (
+ f'\n'
+ f'==> At Config()\n'
+ f'==> At key: repos\n'
+ f"==> At Repository(repo='local')\n"
+ f'==> At key: hooks\n'
+ f"==> At Hook(id='do_not_commit')\n"
+ f'==> At key: minimum_pre_commit_version\n'
+ f'=====> pre-commit version 999 is required but version {C.VERSION} '
+ f'is installed. Perhaps run `pip install --upgrade pre-commit`.'
+ )
+
+
+def test_minimum_pre_commit_version_failing_before_other_error():
+ cfg = {'repos': 5, 'minimum_pre_commit_version': '999'}
with pytest.raises(cfgv.ValidationError) as excinfo:
- cfg = {'repos': [], 'minimum_pre_commit_version': '999'}
cfgv.validate(cfg, CONFIG_SCHEMA)
assert str(excinfo.value) == (
f'\n'
diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py
index 8b0d3ec..9eb0e74 100644
--- a/tests/commands/install_uninstall_test.py
+++ b/tests/commands/install_uninstall_test.py
@@ -349,8 +349,9 @@ def test_install_existing_hooks_no_overwrite(tempdir_factory, store):
# We should run both the legacy and pre-commit hooks
ret, output = _get_commit_output(tempdir_factory)
assert ret == 0
- assert output.startswith('legacy hook\n')
- NORMAL_PRE_COMMIT_RUN.assert_matches(output[len('legacy hook\n'):])
+ legacy = 'legacy hook\n'
+ assert output.startswith(legacy)
+ NORMAL_PRE_COMMIT_RUN.assert_matches(output.removeprefix(legacy))
def test_legacy_overwriting_legacy_hook(tempdir_factory, store):
@@ -375,8 +376,9 @@ def test_install_existing_hook_no_overwrite_idempotent(tempdir_factory, store):
# We should run both the legacy and pre-commit hooks
ret, output = _get_commit_output(tempdir_factory)
assert ret == 0
- assert output.startswith('legacy hook\n')
- NORMAL_PRE_COMMIT_RUN.assert_matches(output[len('legacy hook\n'):])
+ legacy = 'legacy hook\n'
+ assert output.startswith(legacy)
+ NORMAL_PRE_COMMIT_RUN.assert_matches(output.removeprefix(legacy))
def test_install_with_existing_non_utf8_script(tmpdir, store):
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 6a0cd85..e36a3ca 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -4,7 +4,7 @@ import os.path
import shlex
import sys
import time
-from typing import MutableMapping
+from collections.abc import MutableMapping
from unittest import mock
import pytest
diff --git a/tests/repository_test.py b/tests/repository_test.py
index b8dde99..ac065ec 100644
--- a/tests/repository_test.py
+++ b/tests/repository_test.py
@@ -9,7 +9,6 @@ from unittest import mock
import cfgv
import pytest
-import re_assert
import pre_commit.constants as C
from pre_commit import lang_base
@@ -27,7 +26,6 @@ from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b
from testing.fixtures import make_config_from_repo
from testing.fixtures import make_repo
-from testing.fixtures import modify_manifest
from testing.language_helpers import run_language
from testing.util import cwd
from testing.util import get_resource_path
@@ -433,32 +431,6 @@ def test_hook_id_not_present(tempdir_factory, store, caplog):
)
-def test_too_new_version(tempdir_factory, store, caplog):
- path = make_repo(tempdir_factory, 'script_hooks_repo')
- with modify_manifest(path) as manifest:
- manifest[0]['minimum_pre_commit_version'] = '999.0.0'
- config = make_config_from_repo(path)
- with pytest.raises(SystemExit):
- _get_hook(config, store, 'bash_hook')
- _, msg = caplog.messages
- pattern = re_assert.Matches(
- r'^The hook `bash_hook` requires pre-commit version 999\.0\.0 but '
- r'version \d+\.\d+\.\d+ is installed. '
- r'Perhaps run `pip install --upgrade pre-commit`\.$',
- )
- pattern.assert_matches(msg)
-
-
-@pytest.mark.parametrize('version', ('0.1.0', C.VERSION))
-def test_versions_ok(tempdir_factory, store, version):
- path = make_repo(tempdir_factory, 'script_hooks_repo')
- with modify_manifest(path) as manifest:
- manifest[0]['minimum_pre_commit_version'] = version
- config = make_config_from_repo(path)
- # Should succeed
- _get_hook(config, store, 'bash_hook')
-
-
def test_manifest_hooks(tempdir_factory, store):
path = make_repo(tempdir_factory, 'script_hooks_repo')
config = make_config_from_repo(path)
diff --git a/tests/store_test.py b/tests/store_test.py
index eaab940..45ec732 100644
--- a/tests/store_test.py
+++ b/tests/store_test.py
@@ -185,7 +185,7 @@ def test_db_repo_name(store):
def test_local_resources_reflects_reality():
on_disk = {
- res[len('empty_template_'):]
+ res.removeprefix('empty_template_')
for res in os.listdir('pre_commit/resources')
if res.startswith('empty_template_')
}