1
0
Fork 0

Merging upstream version 3.6.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-09 21:46:04 +01:00
parent 391b92c4f9
commit 04fadfcf8e
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
55 changed files with 266 additions and 248 deletions

View file

@ -6,4 +6,4 @@ runs:
using: composite using: composite
steps: steps:
- uses: asottile/workflows/.github/actions/latest-git@v1.4.0 - uses: asottile/workflows/.github/actions/latest-git@v1.4.0
if: inputs.env == 'py38' && runner.os == 'Linux' if: inputs.env == 'py39' && runner.os == 'Linux'

View file

@ -21,7 +21,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
with: with:
python-version: 3.8 python-version: 3.9
- name: install deps - name: install deps
run: python -mpip install -e . -r requirements-dev.txt run: python -mpip install -e . -r requirements-dev.txt
- name: vars - name: vars
@ -39,7 +39,7 @@ jobs:
- uses: asottile/workflows/.github/actions/fast-checkout@v1.4.0 - uses: asottile/workflows/.github/actions/fast-checkout@v1.4.0
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
with: with:
python-version: 3.8 python-version: 3.9
- run: echo "$CONDA\Scripts" >> "$GITHUB_PATH" - run: echo "$CONDA\Scripts" >> "$GITHUB_PATH"
shell: bash shell: bash

View file

@ -12,12 +12,12 @@ concurrency:
jobs: jobs:
main-windows: main-windows:
uses: asottile/workflows/.github/workflows/tox.yml@v1.4.0 uses: asottile/workflows/.github/workflows/tox.yml@v1.6.0
with: with:
env: '["py38"]' env: '["py39"]'
os: windows-latest os: windows-latest
main-linux: main-linux:
uses: asottile/workflows/.github/workflows/tox.yml@v1.4.0 uses: asottile/workflows/.github/workflows/tox.yml@v1.6.0
with: with:
env: '["py38", "py39", "py310"]' env: '["py39", "py310", "py311", "py312"]'
os: ubuntu-latest os: ubuntu-latest

View file

@ -18,7 +18,7 @@ repos:
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/) 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 - repo: https://github.com/asottile/add-trailing-comma
rev: v3.1.0 rev: v3.1.0
hooks: hooks:
@ -27,7 +27,7 @@ repos:
rev: v3.15.0 rev: v3.15.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py38-plus] args: [--py39-plus]
- repo: https://github.com/hhatto/autopep8 - repo: https://github.com/hhatto/autopep8
rev: v2.0.4 rev: v2.0.4
hooks: hooks:
@ -37,7 +37,7 @@ repos:
hooks: hooks:
- id: flake8 - id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1 rev: v1.7.1
hooks: hooks:
- id: mypy - id: mypy
additional_dependencies: [types-all] additional_dependencies: [types-all]

View file

@ -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 3.5.0 - 2023-10-13
================== ==================

View file

@ -5,9 +5,9 @@ import logging
import re import re
import shlex import shlex
import sys import sys
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import NamedTuple from typing import NamedTuple
from typing import Sequence
import cfgv import cfgv
from identify.identify import ALL_TAGS from identify.identify import ALL_TAGS
@ -102,6 +102,13 @@ class StagesMigration(StagesMigrationNoDefault):
MANIFEST_HOOK_DICT = cfgv.Map( MANIFEST_HOOK_DICT = cfgv.Map(
'Hook', 'id', '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('id', cfgv.check_string),
cfgv.Required('name', cfgv.check_string), cfgv.Required('name', cfgv.check_string),
cfgv.Required('entry', 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('description', cfgv.check_string, ''),
cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT), cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
cfgv.Optional('log_file', cfgv.check_string, ''), 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), cfgv.Optional('require_serial', cfgv.check_bool, False),
StagesMigration('stages', []), StagesMigration('stages', []),
cfgv.Optional('verbose', cfgv.check_bool, False), cfgv.Optional('verbose', cfgv.check_bool, False),
@ -345,6 +351,13 @@ DEFAULT_LANGUAGE_VERSION = cfgv.Map(
CONFIG_SCHEMA = cfgv.Map( CONFIG_SCHEMA = cfgv.Map(
'Config', None, '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.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),
cfgv.Optional( cfgv.Optional(
'default_install_hook_types', 'default_install_hook_types',
@ -358,11 +371,6 @@ CONFIG_SCHEMA = cfgv.Map(
cfgv.Optional('files', check_string_regex, ''), cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'), cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('fail_fast', cfgv.check_bool, False), 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( cfgv.WarnAdditionalKeys(
( (
'repos', 'repos',

View file

@ -4,9 +4,9 @@ import concurrent.futures
import os.path import os.path
import re import re
import tempfile import tempfile
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import NamedTuple from typing import NamedTuple
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import git from pre_commit import git

View file

@ -4,7 +4,7 @@ import argparse
import os.path import os.path
import subprocess import subprocess
import sys import sys
from typing import Sequence from collections.abc import Sequence
from pre_commit.commands.run import run from pre_commit.commands.run import run
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -9,11 +9,11 @@ import re
import subprocess import subprocess
import time import time
import unicodedata 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 Any
from typing import Generator
from typing import Iterable
from typing import MutableMapping
from typing import Sequence
from identify.identify import tags_from_path from identify.identify import tags_from_path
@ -74,7 +74,7 @@ class Classifier:
def __init__(self, filenames: Iterable[str]) -> None: def __init__(self, filenames: Iterable[str]) -> None:
self.filenames = [f for f in filenames if os.path.lexists(f)] 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]: def _types_for_file(self, filename: str) -> set[str]:
return tags_from_path(filename) return tags_from_path(filename)

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import clientlib from pre_commit import clientlib

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import clientlib from pre_commit import clientlib

View file

@ -3,10 +3,9 @@ from __future__ import annotations
import contextlib import contextlib
import enum import enum
import os import os
from typing import Generator from collections.abc import Generator
from typing import MutableMapping from collections.abc import MutableMapping
from typing import NamedTuple from typing import NamedTuple
from typing import Tuple
from typing import Union from typing import Union
_Unset = enum.Enum('_Unset', 'UNSET') _Unset = enum.Enum('_Unset', 'UNSET')
@ -18,9 +17,9 @@ class Var(NamedTuple):
default: str = '' default: str = ''
SubstitutionT = Tuple[Union[str, Var], ...] SubstitutionT = tuple[Union[str, Var], ...]
ValueT = Union[str, _Unset, SubstitutionT] 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: def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str:

View file

@ -5,7 +5,7 @@ import functools
import os.path import os.path
import sys import sys
import traceback import traceback
from typing import Generator from collections.abc import Generator
from typing import IO from typing import IO
import pre_commit.constants as C import pre_commit.constants as C

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib import contextlib
import errno import errno
import sys import sys
from collections.abc import Generator
from typing import Callable from typing import Callable
from typing import Generator
if sys.platform == 'win32': # pragma: no cover (windows) if sys.platform == 'win32': # pragma: no cover (windows)

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import logging import logging
import os.path import os.path
import sys import sys
from typing import Mapping from collections.abc import Mapping
from pre_commit.errors import FatalError from pre_commit.errors import FatalError
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError

View file

@ -1,9 +1,9 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import NamedTuple from typing import NamedTuple
from typing import Sequence
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix

View file

@ -5,12 +5,12 @@ import os
import random import random
import re import re
import shlex import shlex
from collections.abc import Generator
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import ContextManager from typing import ContextManager
from typing import Generator
from typing import NoReturn from typing import NoReturn
from typing import Protocol from typing import Protocol
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import parse_shebang from pre_commit import parse_shebang

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -2,8 +2,8 @@ from __future__ import annotations
import contextlib import contextlib
import os.path import os.path
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -4,8 +4,8 @@ import contextlib
import os.path import os.path
import shutil import shutil
import tempfile import tempfile
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import hashlib import hashlib
import json import json
import os import os
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.languages.docker import docker_cmd from pre_commit.languages.docker import docker_cmd

View file

@ -6,8 +6,8 @@ import re
import tempfile import tempfile
import xml.etree.ElementTree import xml.etree.ElementTree
import zipfile import zipfile
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix

View file

@ -12,11 +12,11 @@ import tempfile
import urllib.error import urllib.error
import urllib.request import urllib.request
import zipfile import zipfile
from collections.abc import Generator
from collections.abc import Sequence
from typing import ContextManager from typing import ContextManager
from typing import Generator
from typing import IO from typing import IO
from typing import Protocol from typing import Protocol
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base from pre_commit import lang_base

View file

@ -2,8 +2,8 @@ from __future__ import annotations
import contextlib import contextlib
import os.path import os.path
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -4,8 +4,8 @@ import contextlib
import functools import functools
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base from pre_commit import lang_base

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
import shlex import shlex
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -3,9 +3,9 @@ from __future__ import annotations
import argparse import argparse
import re import re
import sys import sys
from collections.abc import Sequence
from re import Pattern
from typing import NamedTuple from typing import NamedTuple
from typing import Pattern
from typing import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit import output from pre_commit import output

View file

@ -4,8 +4,8 @@ import contextlib
import functools import functools
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base from pre_commit import lang_base
@ -24,7 +24,7 @@ ENVIRONMENT_DIR = 'py_env'
run_hook = lang_base.basic_run_hook run_hook = lang_base.basic_run_hook
@functools.lru_cache(maxsize=None) @functools.cache
def _version_info(exe: str) -> str: def _version_info(exe: str) -> str:
prog = 'import sys;print(".".join(str(p) for p in sys.version_info))' prog = 'import sys;print(".".join(str(p) for p in sys.version_info))'
try: try:
@ -65,7 +65,7 @@ def _find_by_py_launcher(
version: str, version: str,
) -> str | None: # pragma: no cover (windows only) ) -> str | None: # pragma: no cover (windows only)
if version.startswith('python'): if version.startswith('python'):
num = version[len('python'):] num = version.removeprefix('python')
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)') cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
env = dict(os.environ, PYTHONIOENCODING='UTF-8') env = dict(os.environ, PYTHONIOENCODING='UTF-8')
try: try:
@ -124,7 +124,7 @@ def _sys_executable_matches(version: str) -> bool:
return False return False
try: 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: except ValueError:
return False return False

View file

@ -6,8 +6,8 @@ import shlex
import shutil import shutil
import tempfile import tempfile
import textwrap import textwrap
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -6,9 +6,9 @@ import importlib.resources
import os.path import os.path
import shutil import shutil
import tarfile import tarfile
from typing import Generator from collections.abc import Generator
from collections.abc import Sequence
from typing import IO from typing import IO
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base from pre_commit import lang_base
@ -25,7 +25,8 @@ run_hook = lang_base.basic_run_hook
def _resource_bytesio(filename: str) -> IO[bytes]: 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) @functools.lru_cache(maxsize=1)

View file

@ -7,8 +7,8 @@ import shutil
import sys import sys
import tempfile import tempfile
import urllib.request import urllib.request
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base from pre_commit import lang_base
@ -134,7 +134,7 @@ def install_environment(
packages_to_install: set[tuple[str, ...]] = {('--path', '.')} packages_to_install: set[tuple[str, ...]] = {('--path', '.')}
for cli_dep in cli_deps: for cli_dep in cli_deps:
cli_dep = cli_dep[len('cli:'):] cli_dep = cli_dep.removeprefix('cli:')
package, _, crate_version = cli_dep.partition(':') package, _, crate_version = cli_dep.partition(':')
if crate_version != '': if crate_version != '':
packages_to_install.add((package, '--version', crate_version)) packages_to_install.add((package, '--version', crate_version))

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix

View file

@ -2,8 +2,8 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import contextlib import contextlib
import logging import logging
from typing import Generator from collections.abc import Generator
from pre_commit import color from pre_commit import color
from pre_commit import output from pre_commit import output

View file

@ -4,7 +4,7 @@ import argparse
import logging import logging
import os import os
import sys import sys
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import clientlib from pre_commit import clientlib

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import git from pre_commit import git

View file

@ -2,8 +2,8 @@ from __future__ import annotations
import argparse import argparse
import re import re
from typing import Iterable from collections.abc import Iterable
from typing import Sequence from collections.abc import Sequence
from cfgv import apply_defaults from cfgv import apply_defaults

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import sys import sys
from typing import Sequence from collections.abc import Sequence
from pre_commit import output from pre_commit import output

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os.path import os.path
from typing import Mapping from collections.abc import Mapping
from typing import NoReturn from typing import NoReturn
from identify.identify import parse_shebang_from_file from identify.identify import parse_shebang_from_file

View file

@ -4,15 +4,14 @@ import json
import logging import logging
import os import os
import shlex import shlex
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit.all_languages import languages from pre_commit.all_languages import languages
from pre_commit.clientlib import load_manifest from pre_commit.clientlib import load_manifest
from pre_commit.clientlib import LOCAL from pre_commit.clientlib import LOCAL
from pre_commit.clientlib import META from pre_commit.clientlib import META
from pre_commit.clientlib import parse_version
from pre_commit.hook import Hook from pre_commit.hook import Hook
from pre_commit.lang_base import environment_dir from pre_commit.lang_base import environment_dir
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
@ -124,15 +123,6 @@ def _hook(
for dct in rest: for dct in rest:
ret.update(dct) 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'] lang = ret['language']
if ret['language_version'] == C.DEFAULT: if ret['language_version'] == C.DEFAULT:
ret['language_version'] = root_config['default_language_version'][lang] ret['language_version'] = root_config['default_language_version'][lang]

View file

@ -4,7 +4,7 @@ import contextlib
import logging import logging
import os.path import os.path
import time import time
from typing import Generator from collections.abc import Generator
from pre_commit import git from pre_commit import git
from pre_commit.errors import FatalError from pre_commit.errors import FatalError

View file

@ -5,9 +5,9 @@ import logging
import os.path import os.path
import sqlite3 import sqlite3
import tempfile import tempfile
from collections.abc import Generator
from collections.abc import Sequence
from typing import Callable from typing import Callable
from typing import Generator
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import file_lock from pre_commit import file_lock

View file

@ -8,10 +8,10 @@ import shutil
import stat import stat
import subprocess import subprocess
import sys import sys
from collections.abc import Generator
from types import TracebackType from types import TracebackType
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Generator
from pre_commit import parse_shebang 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: 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: def make_executable(filename: str) -> None:
@ -201,24 +202,36 @@ else: # pragma: no cover
cmd_output_p = cmd_output_b cmd_output_p = cmd_output_b
def rmtree(path: str) -> None: def _handle_readonly(
"""On windows, rmtree fails for readonly dirs.""" func: Callable[[str], object],
def handle_remove_readonly( path: str,
func: Callable[..., Any], exc: OSError,
path: str, ) -> None:
exc: tuple[type[OSError], OSError, TracebackType], 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: ) -> None:
excvalue = exc[1] return _handle_readonly(func, path, excinfo[1])
if (
func in (os.rmdir, os.remove, os.unlink) and def rmtree(path: str) -> None:
excvalue.errno in {errno.EACCES, errno.EPERM} shutil.rmtree(path, ignore_errors=False, onerror=_handle_readonly_old)
): else: # pragma: >=3.12 cover
for p in (path, os.path.dirname(path)): def rmtree(path: str) -> None:
os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR) """On windows, rmtree fails for readonly dirs."""
func(path) shutil.rmtree(path, ignore_errors=False, onexc=_handle_readonly)
else:
raise
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
def win_exe(s: str) -> str: def win_exe(s: str) -> str:

View file

@ -7,12 +7,12 @@ import multiprocessing
import os import os
import subprocess import subprocess
import sys 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 Any
from typing import Callable 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 typing import TypeVar
from pre_commit import parse_shebang from pre_commit import parse_shebang

View file

@ -1,6 +1,6 @@
[metadata] [metadata]
name = pre_commit name = pre_commit
version = 3.5.0 version = 3.6.0
description = A framework for managing and maintaining multi-language pre-commit hooks. description = A framework for managing and maintaining multi-language pre-commit hooks.
long_description = file: README.md long_description = file: README.md
long_description_content_type = text/markdown long_description_content_type = text/markdown
@ -24,7 +24,7 @@ install_requires =
nodeenv>=0.11.1 nodeenv>=0.11.1
pyyaml>=5.1 pyyaml>=5.1
virtualenv>=20.10.0 virtualenv>=20.10.0
python_requires = >=3.8 python_requires = >=3.9
[options.packages.find] [options.packages.find]
exclude = exclude =

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os import os
from typing import Sequence from collections.abc import Sequence
from pre_commit.lang_base import Language from pre_commit.lang_base import Language
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix

View file

@ -8,7 +8,7 @@ import shutil
import subprocess import subprocess
import tarfile import tarfile
import tempfile import tempfile
from typing import Sequence from collections.abc import Sequence
# This is a script for generating the tarred resources for git repo # This is a script for generating the tarred resources for git repo

View file

@ -40,56 +40,51 @@ def test_check_type_tag_success():
@pytest.mark.parametrize( @pytest.mark.parametrize(
('config_obj', 'expected'), ( 'cfg',
( (
{ {
'repos': [{ 'repos': [{
'repo': 'git@github.com:pre-commit/pre-commit-hooks', 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
'hooks': [{'id': 'pyflakes', 'files': '\\.py$'}], 'hooks': [{'id': 'pyflakes', 'files': '\\.py$'}],
}], }],
}, },
True, {
), 'repos': [{
( 'repo': 'git@github.com:pre-commit/pre-commit-hooks',
{ 'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
'repos': [{ 'hooks': [
'repo': 'git@github.com:pre-commit/pre-commit-hooks', {
'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'id': 'pyflakes',
'hooks': [ 'files': '\\.py$',
{ 'args': ['foo', 'bar', 'baz'],
'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,
),
), ),
) )
def test_config_valid(config_obj, expected): def test_config_valid(cfg):
ret = is_valid_according_to_schema(config_obj, CONFIG_SCHEMA) assert is_valid_according_to_schema(cfg, CONFIG_SCHEMA)
assert ret is expected
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(): 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): 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: with pytest.raises(cfgv.ValidationError) as excinfo:
validator = validator_cls(key, cfgv.check_string) validator.check({'files': None})
validator.check({key: None})
assert str(excinfo.value) == ( assert str(excinfo.value) == (
'\n' '\n'
f'==> At key: {key}' '==> At key: files'
'\n' '\n'
'=====> Expected string got NoneType' '=====> Expected string got NoneType'
) )
@ -298,46 +292,36 @@ def test_validate_optional_sensible_regex_at_top_level(caplog, regex, warning):
@pytest.mark.parametrize( @pytest.mark.parametrize(
('manifest_obj', 'expected'), 'manifest_obj',
( (
( [{
[{ 'id': 'a',
'id': 'a', 'name': 'b',
'name': 'b', 'entry': 'c',
'entry': 'c', 'language': 'python',
'language': 'python', 'files': r'\.py$',
'files': r'\.py$', }],
}], [{
True, 'id': 'a',
), 'name': 'b',
( 'entry': 'c',
[{ 'language': 'python',
'id': 'a', 'language_version': 'python3.4',
'name': 'b', 'files': r'\.py$',
'entry': 'c', }],
'language': 'python', # A regression in 0.13.5: always_run and files are permissible
'language_version': 'python3.4', [{
'files': r'\.py$', 'id': 'a',
}], 'name': 'b',
True, 'entry': 'c',
), 'language': 'python',
( 'files': '',
# A regression in 0.13.5: always_run and files are permissible 'always_run': True,
[{ }],
'id': 'a',
'name': 'b',
'entry': 'c',
'language': 'python',
'files': '',
'always_run': True,
}],
True,
),
), ),
) )
def test_valid_manifests(manifest_obj, expected): def test_valid_manifests(manifest_obj):
ret = is_valid_according_to_schema(manifest_obj, MANIFEST_SCHEMA) assert is_valid_according_to_schema(manifest_obj, MANIFEST_SCHEMA)
assert ret is expected
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -393,8 +377,39 @@ def test_parse_version():
def test_minimum_pre_commit_version_failing(): 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: with pytest.raises(cfgv.ValidationError) as excinfo:
cfg = {'repos': [], 'minimum_pre_commit_version': '999'}
cfgv.validate(cfg, CONFIG_SCHEMA) cfgv.validate(cfg, CONFIG_SCHEMA)
assert str(excinfo.value) == ( assert str(excinfo.value) == (
f'\n' f'\n'

View file

@ -349,8 +349,9 @@ def test_install_existing_hooks_no_overwrite(tempdir_factory, store):
# We should run both the legacy and pre-commit hooks # We should run both the legacy and pre-commit hooks
ret, output = _get_commit_output(tempdir_factory) ret, output = _get_commit_output(tempdir_factory)
assert ret == 0 assert ret == 0
assert output.startswith('legacy hook\n') legacy = 'legacy hook\n'
NORMAL_PRE_COMMIT_RUN.assert_matches(output[len('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): 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 # We should run both the legacy and pre-commit hooks
ret, output = _get_commit_output(tempdir_factory) ret, output = _get_commit_output(tempdir_factory)
assert ret == 0 assert ret == 0
assert output.startswith('legacy hook\n') legacy = 'legacy hook\n'
NORMAL_PRE_COMMIT_RUN.assert_matches(output[len('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): def test_install_with_existing_non_utf8_script(tmpdir, store):

View file

@ -4,7 +4,7 @@ import os.path
import shlex import shlex
import sys import sys
import time import time
from typing import MutableMapping from collections.abc import MutableMapping
from unittest import mock from unittest import mock
import pytest import pytest

View file

@ -9,7 +9,6 @@ from unittest import mock
import cfgv import cfgv
import pytest import pytest
import re_assert
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base 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 pre_commit.util import cmd_output_b
from testing.fixtures import make_config_from_repo from testing.fixtures import make_config_from_repo
from testing.fixtures import make_repo from testing.fixtures import make_repo
from testing.fixtures import modify_manifest
from testing.language_helpers import run_language from testing.language_helpers import run_language
from testing.util import cwd from testing.util import cwd
from testing.util import get_resource_path 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): def test_manifest_hooks(tempdir_factory, store):
path = make_repo(tempdir_factory, 'script_hooks_repo') path = make_repo(tempdir_factory, 'script_hooks_repo')
config = make_config_from_repo(path) config = make_config_from_repo(path)

View file

@ -185,7 +185,7 @@ def test_db_repo_name(store):
def test_local_resources_reflects_reality(): def test_local_resources_reflects_reality():
on_disk = { on_disk = {
res[len('empty_template_'):] res.removeprefix('empty_template_')
for res in os.listdir('pre_commit/resources') for res in os.listdir('pre_commit/resources')
if res.startswith('empty_template_') if res.startswith('empty_template_')
} }