drop python 3.6 support

python 3.6 reached end of life on 2021-12-23
This commit is contained in:
Anthony Sottile 2022-01-18 17:36:17 -05:00
parent d3bdf1403d
commit 04de6a2e57
111 changed files with 401 additions and 286 deletions

View file

@ -28,12 +28,13 @@ repos:
rev: v2.31.0
hooks:
- id: pyupgrade
args: [--py36-plus]
args: [--py37-plus]
- repo: https://github.com/asottile/reorder_python_imports
rev: v2.6.0
hooks:
- id: reorder-python-imports
args: [--py3-plus]
args: [--py37-plus, --add-import, 'from __future__ import annotations']
exclude: ^testing/resources/python3_hooks_repo/
- repo: https://github.com/asottile/add-trailing-comma
rev: v2.2.1
hooks:

View file

@ -50,7 +50,7 @@ jobs:
displayName: install R
- template: job--python-tox.yml@asottile
parameters:
toxenvs: [pypy3, py36, py37, py38, py39]
toxenvs: [py37, py38, py39]
os: linux
pre_test:
- task: UseRubyVersion@0

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit.main import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import argparse
import functools
import logging
@ -5,8 +7,6 @@ import re
import shlex
import sys
from typing import Any
from typing import Dict
from typing import Optional
from typing import Sequence
import cfgv
@ -95,7 +95,7 @@ load_manifest = functools.partial(
)
def validate_manifest_main(argv: Optional[Sequence[str]] = None) -> int:
def validate_manifest_main(argv: Sequence[str] | None = None) -> int:
parser = _make_argparser('Manifest filenames.')
args = parser.parse_args(argv)
@ -116,7 +116,7 @@ META = 'meta'
# should inherit from cfgv.Conditional if sha support is dropped
class WarnMutableRev(cfgv.ConditionalOptional):
def check(self, dct: Dict[str, Any]) -> None:
def check(self, dct: dict[str, Any]) -> None:
super().check(dct)
if self.key in dct:
@ -135,7 +135,7 @@ class WarnMutableRev(cfgv.ConditionalOptional):
class OptionalSensibleRegexAtHook(cfgv.OptionalNoDefault):
def check(self, dct: Dict[str, Any]) -> None:
def check(self, dct: dict[str, Any]) -> None:
super().check(dct)
if '/*' in dct.get(self.key, ''):
@ -154,7 +154,7 @@ class OptionalSensibleRegexAtHook(cfgv.OptionalNoDefault):
class OptionalSensibleRegexAtTop(cfgv.OptionalNoDefault):
def check(self, dct: Dict[str, Any]) -> None:
def check(self, dct: dict[str, Any]) -> None:
super().check(dct)
if '/*' in dct.get(self.key, ''):
@ -183,7 +183,7 @@ class MigrateShaToRev:
ensure_absent=True,
)
def check(self, dct: Dict[str, Any]) -> None:
def check(self, dct: dict[str, Any]) -> None:
if dct.get('repo') in {LOCAL, META}:
self._cond('rev').check(dct)
self._cond('sha').check(dct)
@ -194,7 +194,7 @@ class MigrateShaToRev:
else:
self._cond('rev').check(dct)
def apply_default(self, dct: Dict[str, Any]) -> None:
def apply_default(self, dct: dict[str, Any]) -> None:
if 'sha' in dct:
dct['rev'] = dct.pop('sha')
@ -212,7 +212,7 @@ def _entry(modname: str) -> str:
def warn_unknown_keys_root(
extra: Sequence[str],
orig_keys: Sequence[str],
dct: Dict[str, str],
dct: dict[str, str],
) -> None:
logger.warning(f'Unexpected key(s) present at root: {", ".join(extra)}')
@ -220,7 +220,7 @@ def warn_unknown_keys_root(
def warn_unknown_keys_repo(
extra: Sequence[str],
orig_keys: Sequence[str],
dct: Dict[str, str],
dct: dict[str, str],
) -> None:
logger.warning(
f'Unexpected key(s) present on {dct["repo"]}: {", ".join(extra)}',
@ -253,7 +253,7 @@ _meta = (
class NotAllowed(cfgv.OptionalNoDefault):
def check(self, dct: Dict[str, Any]) -> None:
def check(self, dct: dict[str, Any]) -> None:
if self.key in dct:
raise cfgv.ValidationError(f'{self.key!r} cannot be overridden')
@ -377,7 +377,7 @@ class InvalidConfigError(FatalError):
pass
def ordered_load_normalize_legacy_config(contents: str) -> Dict[str, Any]:
def ordered_load_normalize_legacy_config(contents: str) -> dict[str, Any]:
data = yaml_load(contents)
if isinstance(data, list):
logger.warning(
@ -398,7 +398,7 @@ load_config = functools.partial(
)
def validate_config_main(argv: Optional[Sequence[str]] = None) -> int:
def validate_config_main(argv: Sequence[str] | None = None) -> int:
parser = _make_argparser('Config filenames.')
args = parser.parse_args(argv)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import argparse
import os
import sys

View file

@ -1,12 +1,10 @@
from __future__ import annotations
import os.path
import re
from typing import Any
from typing import Dict
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit import git
@ -29,13 +27,13 @@ from pre_commit.util import yaml_load
class RevInfo(NamedTuple):
repo: str
rev: str
frozen: Optional[str]
frozen: str | None
@classmethod
def from_config(cls, config: Dict[str, Any]) -> 'RevInfo':
def from_config(cls, config: dict[str, Any]) -> RevInfo:
return cls(config['repo'], config['rev'], None)
def update(self, tags_only: bool, freeze: bool) -> 'RevInfo':
def update(self, tags_only: bool, freeze: bool) -> RevInfo:
git_cmd = ('git', *git.NO_FS_MONITOR)
if tags_only:
@ -76,7 +74,7 @@ class RepositoryCannotBeUpdatedError(RuntimeError):
def _check_hooks_still_exist_at_rev(
repo_config: Dict[str, Any],
repo_config: dict[str, Any],
info: RevInfo,
store: Store,
) -> None:
@ -101,9 +99,9 @@ REV_LINE_RE = re.compile(r'^(\s+)rev:(\s*)([\'"]?)([^\s#]+)(.*)(\r?\n)$')
def _original_lines(
path: str,
rev_infos: List[Optional[RevInfo]],
rev_infos: list[RevInfo | None],
retry: bool = False,
) -> Tuple[List[str], List[int]]:
) -> tuple[list[str], list[int]]:
"""detect `rev:` lines or reformat the file"""
with open(path, newline='') as f:
original = f.read()
@ -120,7 +118,7 @@ def _original_lines(
return _original_lines(path, rev_infos, retry=True)
def _write_new_config(path: str, rev_infos: List[Optional[RevInfo]]) -> None:
def _write_new_config(path: str, rev_infos: list[RevInfo | None]) -> None:
lines, idxs = _original_lines(path, rev_infos)
for idx, rev_info in zip(idxs, rev_infos):
@ -152,7 +150,7 @@ def autoupdate(
"""Auto-update the pre-commit config to the latest versions of repos."""
migrate_config(config_file, quiet=True)
retv = 0
rev_infos: List[Optional[RevInfo]] = []
rev_infos: list[RevInfo | None] = []
changed = False
config = load_config(config_file)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
from pre_commit import output

View file

@ -1,8 +1,7 @@
from __future__ import annotations
import os.path
from typing import Any
from typing import Dict
from typing import Set
from typing import Tuple
import pre_commit.constants as C
from pre_commit import output
@ -17,9 +16,9 @@ from pre_commit.store import Store
def _mark_used_repos(
store: Store,
all_repos: Dict[Tuple[str, str], str],
unused_repos: Set[Tuple[str, str]],
repo: Dict[str, Any],
all_repos: dict[tuple[str, str], str],
unused_repos: set[tuple[str, str]],
repo: dict[str, Any],
) -> None:
if repo['repo'] == META:
return

View file

@ -1,10 +1,10 @@
from __future__ import annotations
import argparse
import os.path
import subprocess
import sys
from typing import Optional
from typing import Sequence
from typing import Tuple
from pre_commit.commands.run import run
from pre_commit.envcontext import envcontext
@ -18,7 +18,7 @@ def _run_legacy(
hook_type: str,
hook_dir: str,
args: Sequence[str],
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
if os.environ.get('PRE_COMMIT_RUNNING_LEGACY'):
raise SystemExit(
f"bug: pre-commit's script is installed in migration mode\n"
@ -69,16 +69,16 @@ def _ns(
color: bool,
*,
all_files: bool = False,
remote_branch: Optional[str] = None,
local_branch: Optional[str] = None,
from_ref: Optional[str] = None,
to_ref: Optional[str] = None,
remote_name: Optional[str] = None,
remote_url: Optional[str] = None,
commit_msg_filename: Optional[str] = None,
checkout_type: Optional[str] = None,
is_squash_merge: Optional[str] = None,
rewrite_command: Optional[str] = None,
remote_branch: str | None = None,
local_branch: str | None = None,
from_ref: str | None = None,
to_ref: str | None = None,
remote_name: str | None = None,
remote_url: str | None = None,
commit_msg_filename: str | None = None,
checkout_type: str | None = None,
is_squash_merge: str | None = None,
rewrite_command: str | None = None,
) -> argparse.Namespace:
return argparse.Namespace(
color=color,
@ -109,7 +109,7 @@ def _pre_push_ns(
color: bool,
args: Sequence[str],
stdin: bytes,
) -> Optional[argparse.Namespace]:
) -> argparse.Namespace | None:
remote_name = args[0]
remote_url = args[1]
@ -197,7 +197,7 @@ def _run_ns(
color: bool,
args: Sequence[str],
stdin: bytes,
) -> Optional[argparse.Namespace]:
) -> argparse.Namespace | None:
_check_args_length(hook_type, args)
if hook_type == 'pre-push':
return _pre_push_ns(color, args, stdin)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging
import os.path
from typing import Sequence

View file

@ -1,11 +1,11 @@
from __future__ import annotations
import logging
import os.path
import shlex
import shutil
import sys
from typing import Optional
from typing import Sequence
from typing import Tuple
from pre_commit import git
from pre_commit import output
@ -34,8 +34,8 @@ TEMPLATE_END = '# end templated\n'
def _hook_paths(
hook_type: str,
git_dir: Optional[str] = None,
) -> Tuple[str, str]:
git_dir: str | None = None,
) -> tuple[str, str]:
git_dir = git_dir if git_dir is not None else git.get_git_dir()
pth = os.path.join(git_dir, 'hooks', hook_type)
return pth, f'{pth}.legacy'
@ -54,7 +54,7 @@ def _install_hook_script(
hook_type: str,
overwrite: bool = False,
skip_on_missing_config: bool = False,
git_dir: Optional[str] = None,
git_dir: str | None = None,
) -> None:
hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)
@ -107,7 +107,7 @@ def install(
overwrite: bool = False,
hooks: bool = False,
skip_on_missing_config: bool = False,
git_dir: Optional[str] = None,
git_dir: str | None = None,
) -> int:
if git_dir is None and git.has_core_hookpaths_set():
logger.error(

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import re
import textwrap

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import argparse
import contextlib
import functools
@ -9,12 +11,8 @@ import time
import unicodedata
from typing import Any
from typing import Collection
from typing import Dict
from typing import List
from typing import MutableMapping
from typing import Sequence
from typing import Set
from typing import Tuple
from identify.identify import tags_from_path
@ -62,7 +60,7 @@ def filter_by_include_exclude(
names: Collection[str],
include: str,
exclude: str,
) -> List[str]:
) -> list[str]:
include_re, exclude_re = re.compile(include), re.compile(exclude)
return [
filename for filename in names
@ -76,7 +74,7 @@ class Classifier:
self.filenames = [f for f in filenames if os.path.lexists(f)]
@functools.lru_cache(maxsize=None)
def _types_for_file(self, filename: str) -> Set[str]:
def _types_for_file(self, filename: str) -> set[str]:
return tags_from_path(filename)
def by_types(
@ -85,7 +83,7 @@ class Classifier:
types: Collection[str],
types_or: Collection[str],
exclude_types: Collection[str],
) -> List[str]:
) -> list[str]:
types = frozenset(types)
types_or = frozenset(types_or)
exclude_types = frozenset(exclude_types)
@ -100,7 +98,7 @@ class Classifier:
ret.append(filename)
return ret
def filenames_for_hook(self, hook: Hook) -> Tuple[str, ...]:
def filenames_for_hook(self, hook: Hook) -> tuple[str, ...]:
names = self.filenames
names = filter_by_include_exclude(names, hook.files, hook.exclude)
names = self.by_types(
@ -117,7 +115,7 @@ class Classifier:
filenames: Collection[str],
include: str,
exclude: str,
) -> 'Classifier':
) -> Classifier:
# on windows we normalize all filenames to use forward slashes
# this makes it easier to filter using the `files:` regex
# this also makes improperly quoted shell-based hooks work better
@ -128,7 +126,7 @@ class Classifier:
return Classifier(filenames)
def _get_skips(environ: MutableMapping[str, str]) -> Set[str]:
def _get_skips(environ: MutableMapping[str, str]) -> set[str]:
skips = environ.get('SKIP', '')
return {skip.strip() for skip in skips.split(',') if skip.strip()}
@ -144,12 +142,12 @@ def _subtle_line(s: str, use_color: bool) -> None:
def _run_single_hook(
classifier: Classifier,
hook: Hook,
skips: Set[str],
skips: set[str],
cols: int,
diff_before: bytes,
verbose: bool,
use_color: bool,
) -> Tuple[bool, bytes]:
) -> tuple[bool, bytes]:
filenames = classifier.filenames_for_hook(hook)
if hook.id in skips or hook.alias in skips:
@ -271,9 +269,9 @@ def _get_diff() -> bytes:
def _run_hooks(
config: Dict[str, Any],
config: dict[str, Any],
hooks: Sequence[Hook],
skips: Set[str],
skips: set[str],
args: argparse.Namespace,
) -> int:
"""Actually run the hooks."""

View file

@ -2,6 +2,7 @@
# determine the latest revision? This adds ~200ms from my tests (and is
# significantly faster than https:// or http://). For now, periodically
# manually updating the revision is fine.
from __future__ import annotations
SAMPLE_CONFIG = '''\
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks

View file

@ -1,8 +1,8 @@
from __future__ import annotations
import argparse
import logging
import os.path
from typing import Optional
from typing import Tuple
import pre_commit.constants as C
from pre_commit import git
@ -18,7 +18,7 @@ from pre_commit.xargs import xargs
logger = logging.getLogger(__name__)
def _repo_ref(tmpdir: str, repo: str, ref: Optional[str]) -> Tuple[str, str]:
def _repo_ref(tmpdir: str, repo: str, ref: str | None) -> tuple[str, str]:
# if `ref` is explicitly passed, use it
if ref is not None:
return repo, ref

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys
if sys.version_info >= (3, 8): # pragma: >=3.8 cover

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import contextlib
import enum
import os
from typing import Generator
from typing import MutableMapping
from typing import NamedTuple
from typing import Optional
from typing import Tuple
from typing import Union
@ -32,7 +33,7 @@ def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str:
@contextlib.contextmanager
def envcontext(
patch: PatchesT,
_env: Optional[MutableMapping[str, str]] = None,
_env: MutableMapping[str, str] | None = None,
) -> Generator[None, None, None]:
"""In this context, `os.environ` is modified according to `patch`.

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import functools
import os.path

View file

@ -1,2 +1,5 @@
from __future__ import annotations
class FatalError(RuntimeError):
pass

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import errno
import sys
@ -20,13 +22,11 @@ if sys.platform == 'win32': # pragma: no cover (windows)
blocked_cb: Callable[[], None],
) -> Generator[None, None, None]:
try:
# TODO: https://github.com/python/typeshed/pull/3607
msvcrt.locking(fileno, msvcrt.LK_NBLCK, _region)
except OSError:
blocked_cb()
while True:
try:
# TODO: https://github.com/python/typeshed/pull/3607
msvcrt.locking(fileno, msvcrt.LK_LOCK, _region)
except OSError as e:
# Locking violation. Returned when the _LK_LOCK or _LK_RLCK
@ -45,7 +45,6 @@ if sys.platform == 'win32': # pragma: no cover (windows)
# The documentation however states:
# "Regions should be locked only briefly and should be unlocked
# before closing a file or exiting the program."
# TODO: https://github.com/python/typeshed/pull/3607
msvcrt.locking(fileno, msvcrt.LK_UNLCK, _region)
else: # pragma: win32 no cover
import fcntl

View file

@ -1,11 +1,9 @@
from __future__ import annotations
import logging
import os.path
import sys
from typing import Dict
from typing import List
from typing import MutableMapping
from typing import Optional
from typing import Set
from pre_commit.errors import FatalError
from pre_commit.util import CalledProcessError
@ -18,7 +16,7 @@ logger = logging.getLogger(__name__)
NO_FS_MONITOR = ('-c', 'core.useBuiltinFSMonitor=false')
def zsplit(s: str) -> List[str]:
def zsplit(s: str) -> list[str]:
s = s.strip('\0')
if s:
return s.split('\0')
@ -27,8 +25,8 @@ def zsplit(s: str) -> List[str]:
def no_git_env(
_env: Optional[MutableMapping[str, str]] = None,
) -> Dict[str, str]:
_env: MutableMapping[str, str] | None = None,
) -> dict[str, str]:
# Too many bugs dealing with environment variables and GIT:
# https://github.com/pre-commit/pre-commit/issues/300
# In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running
@ -95,7 +93,7 @@ def is_in_merge_conflict() -> bool:
)
def parse_merge_msg_for_conflicts(merge_msg: bytes) -> List[str]:
def parse_merge_msg_for_conflicts(merge_msg: bytes) -> list[str]:
# Conflicted files start with tabs
return [
line.lstrip(b'#').strip().decode()
@ -105,7 +103,7 @@ def parse_merge_msg_for_conflicts(merge_msg: bytes) -> List[str]:
]
def get_conflicted_files() -> Set[str]:
def get_conflicted_files() -> set[str]:
logger.info('Checking merge-conflict files only.')
# Need to get the conflicted files from the MERGE_MSG because they could
# have resolved the conflict by choosing one side or the other
@ -126,7 +124,7 @@ def get_conflicted_files() -> Set[str]:
return set(merge_conflict_filenames) | set(merge_diff_filenames)
def get_staged_files(cwd: Optional[str] = None) -> List[str]:
def get_staged_files(cwd: str | None = None) -> list[str]:
return zsplit(
cmd_output(
'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',
@ -137,7 +135,7 @@ def get_staged_files(cwd: Optional[str] = None) -> List[str]:
)
def intent_to_add_files() -> List[str]:
def intent_to_add_files() -> list[str]:
_, stdout, _ = cmd_output(
'git', 'status', '--ignore-submodules', '--porcelain', '-z',
)
@ -153,11 +151,11 @@ def intent_to_add_files() -> List[str]:
return intent_to_add
def get_all_files() -> List[str]:
def get_all_files() -> list[str]:
return zsplit(cmd_output('git', 'ls-files', '-z')[1])
def get_changed_files(old: str, new: str) -> List[str]:
def get_changed_files(old: str, new: str) -> list[str]:
diff_cmd = ('git', 'diff', '--name-only', '--no-ext-diff', '-z')
try:
_, out, _ = cmd_output(*diff_cmd, f'{old}...{new}')

View file

@ -1,10 +1,10 @@
from __future__ import annotations
import logging
import shlex
from typing import Any
from typing import Dict
from typing import NamedTuple
from typing import Sequence
from typing import Tuple
from pre_commit.prefix import Prefix
@ -38,11 +38,11 @@ class Hook(NamedTuple):
verbose: bool
@property
def cmd(self) -> Tuple[str, ...]:
def cmd(self) -> tuple[str, ...]:
return (*shlex.split(self.entry), *self.args)
@property
def install_key(self) -> Tuple[Prefix, str, str, Tuple[str, ...]]:
def install_key(self) -> tuple[Prefix, str, str, tuple[str, ...]]:
return (
self.prefix,
self.language,
@ -51,7 +51,7 @@ class Hook(NamedTuple):
)
@classmethod
def create(cls, src: str, prefix: Prefix, dct: Dict[str, Any]) -> 'Hook':
def create(cls, src: str, prefix: Prefix, dct: dict[str, Any]) -> Hook:
# TODO: have cfgv do this (?)
extra_keys = set(dct) - _KEYS
if extra_keys:

View file

@ -1,8 +1,8 @@
from __future__ import annotations
from typing import Callable
from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Tuple
from pre_commit.hook import Hook
from pre_commit.languages import conda
@ -30,7 +30,7 @@ from pre_commit.prefix import Prefix
class Language(NamedTuple):
name: str
# Use `None` for no installation / environment
ENVIRONMENT_DIR: Optional[str]
ENVIRONMENT_DIR: str | None
# return a value to replace `'default` for `language_version`
get_default_version: Callable[[], str]
# return whether the environment is healthy (or should be rebuilt)
@ -38,7 +38,7 @@ class Language(NamedTuple):
# install a repository for the given language and language_version
install_environment: Callable[[Prefix, str, Sequence[str]], None]
# execute a hook and return the exit code and output
run_hook: 'Callable[[Hook, Sequence[str], bool], Tuple[int, bytes]]'
run_hook: Callable[[Hook, Sequence[str], bool], tuple[int, bytes]]
# TODO: back to modules + Protocol: https://github.com/python/mypy/issues/5018

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import contextlib
import os
from typing import Generator
from typing import Sequence
from typing import Tuple
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
@ -86,7 +87,7 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
# TODO: Some rare commands need to be run using `conda run` but mostly we
# can run them without which is much quicker and produces a better
# output.

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import contextlib
import os
from typing import Generator
from typing import Sequence
from typing import Tuple
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
@ -66,6 +67,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
) -> tuple[int, bytes]: # pragma: win32 no cover
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import contextlib
import os.path
import shutil
import tempfile
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -76,7 +77,7 @@ def install_environment(
with tempfile.TemporaryDirectory() as dep_tmp:
dep, _, version = dep_s.partition(':')
if version:
dep_cmd: Tuple[str, ...] = (dep, '--version', version)
dep_cmd: tuple[str, ...] = (dep, '--version', version)
else:
dep_cmd = (dep,)
@ -104,6 +105,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import hashlib
import json
import os
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.hook import Hook
@ -76,7 +77,7 @@ def build_docker_image(
*,
pull: bool,
) -> None: # pragma: win32 no cover
cmd: Tuple[str, ...] = (
cmd: tuple[str, ...] = (
'docker', 'build',
'--tag', docker_tag(prefix),
'--label', PRE_COMMIT_LABEL,
@ -105,14 +106,14 @@ def install_environment(
os.mkdir(directory)
def get_docker_user() -> Tuple[str, ...]: # pragma: win32 no cover
def get_docker_user() -> tuple[str, ...]: # pragma: win32 no cover
try:
return ('-u', f'{os.getuid()}:{os.getgid()}')
except AttributeError:
return ()
def docker_cmd() -> Tuple[str, ...]: # pragma: win32 no cover
def docker_cmd() -> tuple[str, ...]: # pragma: win32 no cover
return (
'docker', 'run',
'--rm',
@ -129,7 +130,7 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
) -> tuple[int, bytes]: # pragma: win32 no cover
# Rebuild the docker image in case it has gone missing, as many people do
# automated cleanup of docker images.
build_docker_image(hook.prefix, pull=False)

View file

@ -1,5 +1,6 @@
from __future__ import annotations
from typing import Sequence
from typing import Tuple
from pre_commit.hook import Hook
from pre_commit.languages import helpers
@ -15,6 +16,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
) -> tuple[int, bytes]: # pragma: win32 no cover
cmd = docker_cmd() + hook.cmd
return helpers.run_xargs(hook, cmd, file_args, color=color)

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import contextlib
import os.path
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -84,6 +85,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,5 +1,6 @@
from __future__ import annotations
from typing import Sequence
from typing import Tuple
from pre_commit.hook import Hook
from pre_commit.languages import helpers
@ -14,7 +15,7 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
out = f'{hook.entry}\n\n'.encode()
out += b'\n'.join(f.encode() for f in file_args) + b'\n'
return 1, out

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import contextlib
import os.path
import sys
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit import git
@ -95,6 +96,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,13 +1,12 @@
from __future__ import annotations
import multiprocessing
import os
import random
import re
from typing import Any
from typing import List
from typing import Optional
from typing import overload
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
@ -32,7 +31,7 @@ def exe_exists(exe: str) -> bool:
homedir = os.path.expanduser('~')
try:
common: Optional[str] = os.path.commonpath((found, homedir))
common: str | None = os.path.commonpath((found, homedir))
except ValueError: # on windows, different drives raises ValueError
common = None
@ -48,7 +47,7 @@ def exe_exists(exe: str) -> bool:
)
def run_setup_cmd(prefix: Prefix, cmd: Tuple[str, ...], **kwargs: Any) -> None:
def run_setup_cmd(prefix: Prefix, cmd: tuple[str, ...], **kwargs: Any) -> None:
cmd_output_b(*cmd, cwd=prefix.prefix_dir, **kwargs)
@ -58,7 +57,7 @@ def environment_dir(d: None, language_version: str) -> None: ...
def environment_dir(d: str, language_version: str) -> str: ...
def environment_dir(d: Optional[str], language_version: str) -> Optional[str]:
def environment_dir(d: str | None, language_version: str) -> str | None:
if d is None:
return None
else:
@ -95,7 +94,7 @@ def no_install(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> 'NoReturn':
) -> NoReturn:
raise AssertionError('This type is not installable')
@ -113,7 +112,7 @@ def target_concurrency(hook: Hook) -> int:
return 1
def _shuffled(seq: Sequence[str]) -> List[str]:
def _shuffled(seq: Sequence[str]) -> list[str]:
"""Deterministically shuffle"""
fixed_random = random.Random()
fixed_random.seed(FIXED_RANDOM_SEED, version=1)
@ -125,10 +124,10 @@ def _shuffled(seq: Sequence[str]) -> List[str]:
def run_xargs(
hook: Hook,
cmd: Tuple[str, ...],
cmd: tuple[str, ...],
file_args: Sequence[str],
**kwargs: Any,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
# Shuffle the files so that they more evenly fill out the xargs partitions,
# but do it deterministically in case a hook cares about ordering.
file_args = _shuffled(file_args)

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import contextlib
import os
import sys
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -85,6 +86,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
) -> tuple[int, bytes]: # pragma: win32 no cover
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import contextlib
import functools
import os
import sys
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -122,6 +123,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import contextlib
import os
import shlex
from typing import Generator
from typing import Sequence
from typing import Tuple
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
@ -62,6 +63,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,11 +1,11 @@
from __future__ import annotations
import argparse
import re
import sys
from typing import NamedTuple
from typing import Optional
from typing import Pattern
from typing import Sequence
from typing import Tuple
from pre_commit import output
from pre_commit.hook import Hook
@ -90,12 +90,12 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
exe = (sys.executable, '-m', __name__) + tuple(hook.args) + (hook.entry,)
return xargs(exe, file_args, color=color)
def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description=(
'grep-like finder using python regexes. Unlike grep, this tool '

View file

@ -1,12 +1,11 @@
from __future__ import annotations
import contextlib
import functools
import os
import sys
from typing import Dict
from typing import Generator
from typing import Optional
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -35,7 +34,7 @@ def _version_info(exe: str) -> str:
return f'<<error retrieving version from {exe}>>'
def _read_pyvenv_cfg(filename: str) -> Dict[str, str]:
def _read_pyvenv_cfg(filename: str) -> dict[str, str]:
ret = {}
with open(filename, encoding='UTF-8') as f:
for line in f:
@ -65,7 +64,7 @@ def get_env_patch(venv: str) -> PatchesT:
def _find_by_py_launcher(
version: str,
) -> Optional[str]: # pragma: no cover (windows only)
) -> str | None: # pragma: no cover (windows only)
if version.startswith('python'):
num = version[len('python'):]
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
@ -77,8 +76,8 @@ def _find_by_py_launcher(
return None
def _find_by_sys_executable() -> Optional[str]:
def _norm(path: str) -> Optional[str]:
def _find_by_sys_executable() -> str | None:
def _norm(path: str) -> str | None:
_, exe = os.path.split(path.lower())
exe, _, _ = exe.partition('.exe')
if exe not in {'python', 'pythonw'} and find_executable(exe):
@ -133,7 +132,7 @@ def _sys_executable_matches(version: str) -> bool:
return sys.version_info[:len(info)] == info
def norm_version(version: str) -> Optional[str]:
def norm_version(version: str) -> str | None:
if version == C.DEFAULT: # use virtualenv's default
return None
elif _sys_executable_matches(version): # virtualenv defaults to our exe
@ -209,6 +208,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import contextlib
import os
import shlex
import shutil
from typing import Generator
from typing import Sequence
from typing import Tuple
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
@ -80,7 +81,7 @@ def _entry_validate(entry: Sequence[str]) -> None:
)
def _cmd_from_hook(hook: Hook) -> Tuple[str, ...]:
def _cmd_from_hook(hook: Hook) -> tuple[str, ...]:
entry = shlex.split(hook.entry)
_entry_validate(entry)
@ -148,7 +149,7 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(
hook, _cmd_from_hook(hook), file_args, color=color,

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import functools
import os.path
@ -5,7 +7,6 @@ import shutil
import tarfile
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -146,6 +147,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,9 +1,9 @@
from __future__ import annotations
import contextlib
import os.path
from typing import Generator
from typing import Sequence
from typing import Set
from typing import Tuple
import toml
@ -39,7 +39,7 @@ def in_env(prefix: Prefix) -> Generator[None, None, None]:
def _add_dependencies(
cargo_toml_path: str,
additional_dependencies: Set[str],
additional_dependencies: set[str],
) -> None:
with open(cargo_toml_path, 'r+') as f:
cargo_toml = toml.load(f)
@ -81,7 +81,7 @@ def install_environment(
_add_dependencies(prefix.path('Cargo.toml'), lib_deps)
with clean_path_on_failure(directory):
packages_to_install: Set[Tuple[str, ...]] = {('--path', '.')}
packages_to_install: set[tuple[str, ...]] = {('--path', '.')}
for cli_dep in cli_deps:
cli_dep = cli_dep[len('cli:'):]
package, _, version = cli_dep.partition(':')
@ -101,6 +101,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,5 +1,6 @@
from __future__ import annotations
from typing import Sequence
from typing import Tuple
from pre_commit.hook import Hook
from pre_commit.languages import helpers
@ -14,6 +15,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
cmd = (hook.prefix.path(hook.cmd[0]), *hook.cmd[1:])
return helpers.run_xargs(hook, cmd, file_args, color=color)

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import contextlib
import os
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
@ -59,6 +60,6 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
) -> tuple[int, bytes]: # pragma: win32 no cover
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,5 +1,6 @@
from __future__ import annotations
from typing import Sequence
from typing import Tuple
from pre_commit.hook import Hook
from pre_commit.languages import helpers
@ -15,5 +16,5 @@ def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import logging
from typing import Generator

View file

@ -1,11 +1,11 @@
from __future__ import annotations
import argparse
import logging
import os
import sys
from typing import Any
from typing import Optional
from typing import Sequence
from typing import Union
import pre_commit.constants as C
from pre_commit import git
@ -55,8 +55,8 @@ class AppendReplaceDefault(argparse.Action):
self,
parser: argparse.ArgumentParser,
namespace: argparse.Namespace,
values: Union[str, Sequence[str], None],
option_string: Optional[str] = None,
values: str | Sequence[str] | None,
option_string: str | None = None,
) -> None:
if not self.appended:
setattr(namespace, self.dest, [])
@ -175,7 +175,7 @@ def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
args.repo = os.path.relpath(args.repo)
def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
argv = argv if argv is not None else sys.argv[1:]
parser = argparse.ArgumentParser(prog='pre-commit')

View file

@ -1,5 +1,6 @@
from __future__ import annotations
import argparse
from typing import Optional
from typing import Sequence
import pre_commit.constants as C
@ -27,7 +28,7 @@ def check_all_hooks_match_files(config_file: str) -> int:
return retv
def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', default=[C.CONFIG_FILE])
args = parser.parse_args(argv)

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import argparse
import re
from typing import Optional
from typing import Sequence
from cfgv import apply_defaults
@ -65,7 +66,7 @@ def check_useless_excludes(config_file: str) -> int:
return retv
def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', default=[C.CONFIG_FILE])
args = parser.parse_args(argv)

View file

@ -1,11 +1,12 @@
from __future__ import annotations
import sys
from typing import Optional
from typing import Sequence
from pre_commit import output
def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
argv = argv if argv is not None else sys.argv[1:]
for arg in argv:
output.write_line(arg)

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import contextlib
import sys
from typing import Any
from typing import IO
from typing import Optional
def write(s: str, stream: IO[bytes] = sys.stdout.buffer) -> None:
@ -11,9 +12,9 @@ def write(s: str, stream: IO[bytes] = sys.stdout.buffer) -> None:
def write_line_b(
s: Optional[bytes] = None,
s: bytes | None = None,
stream: IO[bytes] = sys.stdout.buffer,
logfile_name: Optional[str] = None,
logfile_name: str | None = None,
) -> None:
with contextlib.ExitStack() as exit_stack:
output_streams = [stream]
@ -28,5 +29,5 @@ def write_line_b(
output_stream.flush()
def write_line(s: Optional[str] = None, **kwargs: Any) -> None:
def write_line(s: str | None = None, **kwargs: Any) -> None:
write_line_b(s.encode() if s is not None else s, **kwargs)

View file

@ -1,7 +1,7 @@
from __future__ import annotations
import os.path
from typing import Mapping
from typing import Optional
from typing import Tuple
from typing import TYPE_CHECKING
from identify.identify import parse_shebang_from_file
@ -11,11 +11,11 @@ if TYPE_CHECKING:
class ExecutableNotFoundError(OSError):
def to_output(self) -> Tuple[int, bytes, None]:
def to_output(self) -> tuple[int, bytes, None]:
return (1, self.args[0].encode(), None)
def parse_filename(filename: str) -> Tuple[str, ...]:
def parse_filename(filename: str) -> tuple[str, ...]:
if not os.path.exists(filename):
return ()
else:
@ -23,8 +23,8 @@ def parse_filename(filename: str) -> Tuple[str, ...]:
def find_executable(
exe: str, _environ: Optional[Mapping[str, str]] = None,
) -> Optional[str]:
exe: str, _environ: Mapping[str, str] | None = None,
) -> str | None:
exe = os.path.normpath(exe)
if os.sep in exe:
return exe
@ -47,7 +47,7 @@ def find_executable(
def normexe(orig: str) -> str:
def _error(msg: str) -> 'NoReturn':
def _error(msg: str) -> NoReturn:
raise ExecutableNotFoundError(f'Executable `{orig}` {msg}')
if os.sep not in orig and (not os.altsep or os.altsep not in orig):
@ -65,7 +65,7 @@ def normexe(orig: str) -> str:
return orig
def normalize_cmd(cmd: Tuple[str, ...]) -> Tuple[str, ...]:
def normalize_cmd(cmd: tuple[str, ...]) -> tuple[str, ...]:
"""Fixes for the following issues on windows
- https://bugs.python.org/issue8557
- windows does not parse shebangs

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import os.path
from typing import NamedTuple
from typing import Tuple
class Prefix(NamedTuple):
@ -12,6 +13,6 @@ class Prefix(NamedTuple):
def exists(self, *parts: str) -> bool:
return os.path.exists(self.path(*parts))
def star(self, end: str) -> Tuple[str, ...]:
def star(self, end: str) -> tuple[str, ...]:
paths = os.listdir(self.prefix_dir)
return tuple(path for path in paths if path.endswith(end))

View file

@ -1,13 +1,10 @@
from __future__ import annotations
import json
import logging
import os
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
import pre_commit.constants as C
from pre_commit.clientlib import load_manifest
@ -33,7 +30,7 @@ def _state_filename(prefix: Prefix, venv: str) -> str:
return prefix.path(venv, f'.install_state_v{C.INSTALLED_STATE_VERSION}')
def _read_state(prefix: Prefix, venv: str) -> Optional[object]:
def _read_state(prefix: Prefix, venv: str) -> object | None:
filename = _state_filename(prefix, venv)
if not os.path.exists(filename):
return None
@ -93,9 +90,9 @@ def _hook_install(hook: Hook) -> None:
def _hook(
*hook_dicts: Dict[str, Any],
root_config: Dict[str, Any],
) -> Dict[str, Any]:
*hook_dicts: dict[str, Any],
root_config: dict[str, Any],
) -> dict[str, Any]:
ret, rest = dict(hook_dicts[0]), hook_dicts[1:]
for dct in rest:
ret.update(dct)
@ -140,10 +137,10 @@ def _hook(
def _non_cloned_repository_hooks(
repo_config: Dict[str, Any],
repo_config: dict[str, Any],
store: Store,
root_config: Dict[str, Any],
) -> Tuple[Hook, ...]:
root_config: dict[str, Any],
) -> tuple[Hook, ...]:
def _prefix(language_name: str, deps: Sequence[str]) -> Prefix:
language = languages[language_name]
# pygrep / script / system / docker_image do not have
@ -164,10 +161,10 @@ def _non_cloned_repository_hooks(
def _cloned_repository_hooks(
repo_config: Dict[str, Any],
repo_config: dict[str, Any],
store: Store,
root_config: Dict[str, Any],
) -> Tuple[Hook, ...]:
root_config: dict[str, Any],
) -> tuple[Hook, ...]:
repo, rev = repo_config['repo'], repo_config['rev']
manifest_path = os.path.join(store.clone(repo, rev), C.MANIFEST_FILE)
by_id = {hook['id']: hook for hook in load_manifest(manifest_path)}
@ -196,10 +193,10 @@ def _cloned_repository_hooks(
def _repository_hooks(
repo_config: Dict[str, Any],
repo_config: dict[str, Any],
store: Store,
root_config: Dict[str, Any],
) -> Tuple[Hook, ...]:
root_config: dict[str, Any],
) -> tuple[Hook, ...]:
if repo_config['repo'] in {LOCAL, META}:
return _non_cloned_repository_hooks(repo_config, store, root_config)
else:
@ -207,8 +204,8 @@ def _repository_hooks(
def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None:
def _need_installed() -> List[Hook]:
seen: Set[Tuple[Prefix, str, str, Tuple[str, ...]]] = set()
def _need_installed() -> list[Hook]:
seen: set[tuple[Prefix, str, str, tuple[str, ...]]] = set()
ret = []
for hook in hooks:
if hook.install_key not in seen and not _hook_installed(hook):
@ -224,7 +221,7 @@ def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None:
_hook_install(hook)
def all_hooks(root_config: Dict[str, Any], store: Store) -> Tuple[Hook, ...]:
def all_hooks(root_config: dict[str, Any], store: Store) -> tuple[Hook, ...]:
return tuple(
hook
for repo in root_config['repos']

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from setuptools import setup

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import logging
import os.path

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import logging
import os.path
@ -5,10 +7,7 @@ import sqlite3
import tempfile
from typing import Callable
from typing import Generator
from typing import List
from typing import Optional
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit import file_lock
@ -40,7 +39,7 @@ def _get_default_directory() -> str:
class Store:
get_default_directory = staticmethod(_get_default_directory)
def __init__(self, directory: Optional[str] = None) -> None:
def __init__(self, directory: str | None = None) -> None:
self.directory = directory or Store.get_default_directory()
self.db_path = os.path.join(self.directory, 'db.db')
self.readonly = (
@ -92,7 +91,7 @@ class Store:
@contextlib.contextmanager
def connect(
self,
db_path: Optional[str] = None,
db_path: str | None = None,
) -> Generator[sqlite3.Connection, None, None]:
db_path = db_path or self.db_path
# sqlite doesn't close its fd with its contextmanager >.<
@ -119,7 +118,7 @@ class Store:
) -> str:
repo = self.db_repo_name(repo, deps)
def _get_result() -> Optional[str]:
def _get_result() -> str | None:
# Check if we already exist
with self.connect() as db:
result = db.execute(
@ -239,18 +238,18 @@ class Store:
self._create_config_table(db)
db.execute('INSERT OR IGNORE INTO configs VALUES (?)', (path,))
def select_all_configs(self) -> List[str]:
def select_all_configs(self) -> list[str]:
with self.connect() as db:
self._create_config_table(db)
rows = db.execute('SELECT path FROM configs').fetchall()
return [path for path, in rows]
def delete_configs(self, configs: List[str]) -> None:
def delete_configs(self, configs: list[str]) -> None:
with self.connect() as db:
rows = [(path,) for path in configs]
db.executemany('DELETE FROM configs WHERE path = ?', rows)
def select_all_repos(self) -> List[Tuple[str, str, str]]:
def select_all_repos(self) -> list[tuple[str, str, str]]:
with self.connect() as db:
return db.execute('SELECT repo, ref, path from repos').fetchall()

View file

@ -1,6 +1,9 @@
from __future__ import annotations
import contextlib
import errno
import functools
import importlib.resources
import os.path
import shutil
import stat
@ -10,24 +13,13 @@ import tempfile
from types import TracebackType
from typing import Any
from typing import Callable
from typing import Dict
from typing import Generator
from typing import IO
from typing import Optional
from typing import Tuple
from typing import Type
import yaml
from pre_commit import parse_shebang
if sys.version_info >= (3, 7): # pragma: >=3.7 cover
from importlib.resources import open_binary
from importlib.resources import read_text
else: # pragma: <3.7 cover
from importlib_resources import open_binary
from importlib_resources import read_text
Loader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader)
yaml_load = functools.partial(yaml.load, Loader=Loader)
Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper)
@ -73,11 +65,11 @@ def tmpdir() -> Generator[str, None, None]:
def resource_bytesio(filename: str) -> IO[bytes]:
return open_binary('pre_commit.resources', filename)
return importlib.resources.open_binary('pre_commit.resources', filename)
def resource_text(filename: str) -> str:
return read_text('pre_commit.resources', filename)
return importlib.resources.read_text('pre_commit.resources', filename)
def make_executable(filename: str) -> None:
@ -90,10 +82,10 @@ class CalledProcessError(RuntimeError):
def __init__(
self,
returncode: int,
cmd: Tuple[str, ...],
cmd: tuple[str, ...],
expected_returncode: int,
stdout: bytes,
stderr: Optional[bytes],
stderr: bytes | None,
) -> None:
super().__init__(returncode, cmd, expected_returncode, stdout, stderr)
self.returncode = returncode
@ -103,7 +95,7 @@ class CalledProcessError(RuntimeError):
self.stderr = stderr
def __bytes__(self) -> bytes:
def _indent_or_none(part: Optional[bytes]) -> bytes:
def _indent_or_none(part: bytes | None) -> bytes:
if part:
return b'\n ' + part.replace(b'\n', b'\n ')
else:
@ -121,20 +113,20 @@ class CalledProcessError(RuntimeError):
return self.__bytes__().decode()
def _setdefault_kwargs(kwargs: Dict[str, Any]) -> None:
def _setdefault_kwargs(kwargs: dict[str, Any]) -> None:
for arg in ('stdin', 'stdout', 'stderr'):
kwargs.setdefault(arg, subprocess.PIPE)
def _oserror_to_output(e: OSError) -> Tuple[int, bytes, None]:
def _oserror_to_output(e: OSError) -> tuple[int, bytes, None]:
return 1, force_bytes(e).rstrip(b'\n') + b'\n', None
def cmd_output_b(
*cmd: str,
retcode: Optional[int] = 0,
retcode: int | None = 0,
**kwargs: Any,
) -> Tuple[int, bytes, Optional[bytes]]:
) -> tuple[int, bytes, bytes | None]:
_setdefault_kwargs(kwargs)
try:
@ -156,7 +148,7 @@ def cmd_output_b(
return returncode, stdout_b, stderr_b
def cmd_output(*cmd: str, **kwargs: Any) -> Tuple[int, str, Optional[str]]:
def cmd_output(*cmd: str, **kwargs: Any) -> tuple[int, str, str | None]:
returncode, stdout_b, stderr_b = cmd_output_b(*cmd, **kwargs)
stdout = stdout_b.decode() if stdout_b is not None else None
stderr = stderr_b.decode() if stderr_b is not None else None
@ -169,10 +161,10 @@ if os.name != 'nt': # pragma: win32 no cover
class Pty:
def __init__(self) -> None:
self.r: Optional[int] = None
self.w: Optional[int] = None
self.r: int | None = None
self.w: int | None = None
def __enter__(self) -> 'Pty':
def __enter__(self) -> Pty:
self.r, self.w = openpty()
# tty flags normally change \n to \r\n
@ -195,18 +187,18 @@ if os.name != 'nt': # pragma: win32 no cover
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
self.close_w()
self.close_r()
def cmd_output_p(
*cmd: str,
retcode: Optional[int] = 0,
retcode: int | None = 0,
**kwargs: Any,
) -> Tuple[int, bytes, Optional[bytes]]:
) -> tuple[int, bytes, bytes | None]:
assert retcode is None
assert kwargs['stderr'] == subprocess.STDOUT, kwargs['stderr']
_setdefault_kwargs(kwargs)
@ -250,7 +242,7 @@ def rmtree(path: str) -> None:
def handle_remove_readonly(
func: Callable[..., Any],
path: str,
exc: Tuple[Type[OSError], OSError, TracebackType],
exc: tuple[type[OSError], OSError, TracebackType],
) -> None:
excvalue = exc[1]
if (
@ -265,7 +257,7 @@ def rmtree(path: str) -> None:
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
def parse_version(s: str) -> Tuple[int, ...]:
def parse_version(s: str) -> tuple[int, ...]:
"""poor man's version comparison"""
return tuple(int(p) for p in s.split('.'))

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import concurrent.futures
import contextlib
import math
@ -8,11 +10,8 @@ from typing import Any
from typing import Callable
from typing import Generator
from typing import Iterable
from typing import List
from typing import MutableMapping
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import TypeVar
from pre_commit import parse_shebang
@ -23,7 +22,7 @@ TArg = TypeVar('TArg')
TRet = TypeVar('TRet')
def _environ_size(_env: Optional[MutableMapping[str, str]] = None) -> int:
def _environ_size(_env: MutableMapping[str, str] | None = None) -> int:
environ = _env if _env is not None else getattr(os, 'environb', os.environ)
size = 8 * len(environ) # number of pointers in `envp`
for k, v in environ.items():
@ -62,8 +61,8 @@ def partition(
cmd: Sequence[str],
varargs: Sequence[str],
target_concurrency: int,
_max_length: Optional[int] = None,
) -> Tuple[Tuple[str, ...], ...]:
_max_length: int | None = None,
) -> tuple[tuple[str, ...], ...]:
_max_length = _max_length or _get_platform_max_length()
# Generally, we try to partition evenly into at least `target_concurrency`
@ -73,7 +72,7 @@ def partition(
cmd = tuple(cmd)
ret = []
ret_cmd: List[str] = []
ret_cmd: list[str] = []
# Reversed so arguments are in order
varargs = list(reversed(varargs))
@ -115,14 +114,14 @@ def _thread_mapper(maxsize: int) -> Generator[
def xargs(
cmd: Tuple[str, ...],
cmd: tuple[str, ...],
varargs: Sequence[str],
*,
color: bool = False,
target_concurrency: int = 1,
_max_length: int = _get_platform_max_length(),
**kwargs: Any,
) -> Tuple[int, bytes]:
) -> tuple[int, bytes]:
"""A simplified implementation of xargs.
color: Make a pty if on a platform that supports it
@ -152,8 +151,8 @@ def xargs(
partitions = partition(cmd, varargs, target_concurrency, _max_length)
def run_cmd_partition(
run_cmd: Tuple[str, ...],
) -> Tuple[int, bytes, Optional[bytes]]:
run_cmd: tuple[str, ...],
) -> tuple[int, bytes, bytes | None]:
return cmd_fn(
*run_cmd, retcode=None, stderr=subprocess.STDOUT, **kwargs,
)

View file

@ -13,7 +13,6 @@ classifiers =
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
@ -31,8 +30,7 @@ install_requires =
toml
virtualenv>=20.0.8
importlib-metadata;python_version<"3.8"
importlib-resources<5.3;python_version<"3.7"
python_requires = >=3.6.1
python_requires = >=3.7
[options.packages.find]
exclude =

View file

@ -1,2 +1,4 @@
from __future__ import annotations
from setuptools import setup
setup()

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import collections

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import os.path
import shutil

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3
from __future__ import annotations
import sys
LANGUAGES = [

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import gzip
import os.path
@ -6,7 +8,6 @@ import shutil
import subprocess
import tarfile
import tempfile
from typing import Optional
from typing import Sequence
@ -69,7 +70,7 @@ def make_archive(name: str, repo: str, ref: str, destdir: str) -> str:
return output_path
def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('--dest', default='pre_commit/resources')
args = parser.parse_args(argv)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from setuptools import setup
setup(

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from setuptools import setup
setup(

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib
import os.path
import subprocess

View file

@ -1,4 +1,4 @@
FROM ubuntu:bionic
FROM ubuntu:focal
RUN : \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
@ -10,5 +10,5 @@ RUN : \
ENV LANG=C.UTF-8 PATH=/venv/bin:$PATH
RUN : \
&& python3.6 -mvenv /venv \
&& python3 -mvenv /venv \
&& pip install --no-cache-dir pip setuptools wheel no-manylinux --upgrade

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3
from __future__ import annotations
import os.path
import shutil
import stat
@ -59,10 +61,7 @@ def main() -> int:
if sys.platform == 'win32': # https://bugs.python.org/issue19124
import subprocess
if sys.version_info < (3, 7): # https://bugs.python.org/issue25942
return subprocess.Popen(cmd).wait()
else:
return subprocess.call(cmd)
return subprocess.call(cmd)
else:
os.execvp(cmd[0], cmd)

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import base64
import hashlib

View file

@ -1,5 +1,7 @@
#!/usr/bin/env python3
"""A shim executable to put dependencies on sys.path"""
from __future__ import annotations
import argparse
import os.path
import runpy
@ -36,10 +38,7 @@ def main() -> int:
if sys.platform == 'win32': # https://bugs.python.org/issue19124
import subprocess
if sys.version_info < (3, 7): # https://bugs.python.org/issue25942
return subprocess.Popen(cmd).wait()
else:
return subprocess.call(cmd)
return subprocess.call(cmd)
else:
os.execvp(cmd[0], cmd)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging
import re

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import shlex
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os
import pre_commit.constants as C

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import subprocess
import sys
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import re

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pre_commit.constants as C
from pre_commit.commands.migrate_config import migrate_config

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import shlex
import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit.commands.sample_config import sample_config

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import re
import time

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import functools
import io
import logging

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import stat
import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest
from pre_commit import envcontext

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import builtins
import json
import ntpath

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest
from pre_commit.languages.golang import guess_go_dir

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import multiprocessing
import os.path
import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import json
import os
import shutil

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest
from pre_commit.languages import pygrep

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import sys
from unittest import mock
@ -47,16 +49,16 @@ def test_norm_version_of_default_is_sys_executable():
assert python.norm_version('default') is None
@pytest.mark.parametrize('v', ('python3.6', 'python3', 'python'))
@pytest.mark.parametrize('v', ('python3.9', 'python3', 'python'))
def test_sys_executable_matches(v):
with mock.patch.object(sys, 'version_info', (3, 6, 7)):
with mock.patch.object(sys, 'version_info', (3, 9, 10)):
assert python._sys_executable_matches(v)
assert python.norm_version(v) is None
@pytest.mark.parametrize('v', ('notpython', 'python3.x'))
def test_sys_executable_matches_does_not_match(v):
with mock.patch.object(sys, 'version_info', (3, 6, 7)):
with mock.patch.object(sys, 'version_info', (3, 9, 10)):
assert not python._sys_executable_matches(v)
@ -65,7 +67,7 @@ def test_sys_executable_matches_does_not_match(v):
('/usr/bin/python3', '/usr/bin/python3.7', 'python3'),
('/usr/bin/python', '/usr/bin/python3.7', 'python3.7'),
('/usr/bin/python', '/usr/bin/python', None),
('/usr/bin/python3.6m', '/usr/bin/python3.6m', 'python3.6m'),
('/usr/bin/python3.7m', '/usr/bin/python3.7m', 'python3.7m'),
('v/bin/python', 'v/bin/pypy', 'pypy'),
),
)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path
import tarfile
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging
from pre_commit import color

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import argparse
import os.path
from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit.meta_hooks import check_hooks_apply
from testing.fixtures import add_config_to_repo

Some files were not shown because too many files have changed in this diff Show more