Adding upstream version 3.5.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
0db83eaf7b
commit
3e8db4df26
14 changed files with 130 additions and 106 deletions
|
@ -1,6 +1,6 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
|
@ -10,25 +10,25 @@ repos:
|
||||||
- id: name-tests-test
|
- id: name-tests-test
|
||||||
- id: requirements-txt-fixer
|
- id: requirements-txt-fixer
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v2.4.0
|
rev: v2.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
- repo: https://github.com/asottile/reorder-python-imports
|
- repo: https://github.com/asottile/reorder-python-imports
|
||||||
rev: v3.10.0
|
rev: v3.12.0
|
||||||
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: [--py38-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.0.1
|
rev: v3.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: add-trailing-comma
|
- id: add-trailing-comma
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.10.1
|
rev: v3.15.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py38-plus]
|
args: [--py38-plus]
|
||||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
- repo: https://github.com/hhatto/autopep8
|
||||||
rev: v2.0.4
|
rev: v2.0.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: autopep8
|
- id: autopep8
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
||||||
|
3.5.0 - 2023-10-13
|
||||||
|
==================
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Improve performance of `check-hooks-apply` and `check-useless-excludes`.
|
||||||
|
- #2998 PR by @mxr.
|
||||||
|
- #2935 issue by @mxr.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Use `time.monotonic()` for more accurate hook timing.
|
||||||
|
- #3024 PR by @adamchainz.
|
||||||
|
|
||||||
|
### Migrating
|
||||||
|
- Require npm 6.x+ for `language: node` hooks.
|
||||||
|
- #2996 PR by @RoelAdriaans.
|
||||||
|
- #1983 issue by @henryiii.
|
||||||
|
|
||||||
3.4.0 - 2023-09-02
|
3.4.0 - 2023-09-02
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ import subprocess
|
||||||
import time
|
import time
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Collection
|
from typing import Generator
|
||||||
|
from typing import Iterable
|
||||||
from typing import MutableMapping
|
from typing import MutableMapping
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
@ -57,20 +58,20 @@ def _full_msg(
|
||||||
|
|
||||||
|
|
||||||
def filter_by_include_exclude(
|
def filter_by_include_exclude(
|
||||||
names: Collection[str],
|
names: Iterable[str],
|
||||||
include: str,
|
include: str,
|
||||||
exclude: str,
|
exclude: str,
|
||||||
) -> list[str]:
|
) -> Generator[str, None, None]:
|
||||||
include_re, exclude_re = re.compile(include), re.compile(exclude)
|
include_re, exclude_re = re.compile(include), re.compile(exclude)
|
||||||
return [
|
return (
|
||||||
filename for filename in names
|
filename for filename in names
|
||||||
if include_re.search(filename)
|
if include_re.search(filename)
|
||||||
if not exclude_re.search(filename)
|
if not exclude_re.search(filename)
|
||||||
]
|
)
|
||||||
|
|
||||||
|
|
||||||
class Classifier:
|
class Classifier:
|
||||||
def __init__(self, filenames: Collection[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.lru_cache(maxsize=None)
|
||||||
|
@ -79,15 +80,14 @@ class Classifier:
|
||||||
|
|
||||||
def by_types(
|
def by_types(
|
||||||
self,
|
self,
|
||||||
names: Sequence[str],
|
names: Iterable[str],
|
||||||
types: Collection[str],
|
types: Iterable[str],
|
||||||
types_or: Collection[str],
|
types_or: Iterable[str],
|
||||||
exclude_types: Collection[str],
|
exclude_types: Iterable[str],
|
||||||
) -> list[str]:
|
) -> Generator[str, None, None]:
|
||||||
types = frozenset(types)
|
types = frozenset(types)
|
||||||
types_or = frozenset(types_or)
|
types_or = frozenset(types_or)
|
||||||
exclude_types = frozenset(exclude_types)
|
exclude_types = frozenset(exclude_types)
|
||||||
ret = []
|
|
||||||
for filename in names:
|
for filename in names:
|
||||||
tags = self._types_for_file(filename)
|
tags = self._types_for_file(filename)
|
||||||
if (
|
if (
|
||||||
|
@ -95,24 +95,24 @@ class Classifier:
|
||||||
(not types_or or tags & types_or) and
|
(not types_or or tags & types_or) and
|
||||||
not tags & exclude_types
|
not tags & exclude_types
|
||||||
):
|
):
|
||||||
ret.append(filename)
|
yield filename
|
||||||
return ret
|
|
||||||
|
|
||||||
def filenames_for_hook(self, hook: Hook) -> tuple[str, ...]:
|
def filenames_for_hook(self, hook: Hook) -> Generator[str, None, None]:
|
||||||
names = self.filenames
|
return self.by_types(
|
||||||
names = filter_by_include_exclude(names, hook.files, hook.exclude)
|
filter_by_include_exclude(
|
||||||
names = self.by_types(
|
self.filenames,
|
||||||
names,
|
hook.files,
|
||||||
|
hook.exclude,
|
||||||
|
),
|
||||||
hook.types,
|
hook.types,
|
||||||
hook.types_or,
|
hook.types_or,
|
||||||
hook.exclude_types,
|
hook.exclude_types,
|
||||||
)
|
)
|
||||||
return tuple(names)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(
|
def from_config(
|
||||||
cls,
|
cls,
|
||||||
filenames: Collection[str],
|
filenames: Iterable[str],
|
||||||
include: str,
|
include: str,
|
||||||
exclude: str,
|
exclude: str,
|
||||||
) -> Classifier:
|
) -> Classifier:
|
||||||
|
@ -121,7 +121,7 @@ class Classifier:
|
||||||
# this also makes improperly quoted shell-based hooks work better
|
# this also makes improperly quoted shell-based hooks work better
|
||||||
# see #1173
|
# see #1173
|
||||||
if os.altsep == '/' and os.sep == '\\':
|
if os.altsep == '/' and os.sep == '\\':
|
||||||
filenames = [f.replace(os.sep, os.altsep) for f in filenames]
|
filenames = (f.replace(os.sep, os.altsep) for f in filenames)
|
||||||
filenames = filter_by_include_exclude(filenames, include, exclude)
|
filenames = filter_by_include_exclude(filenames, include, exclude)
|
||||||
return Classifier(filenames)
|
return Classifier(filenames)
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ def _run_single_hook(
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
use_color: bool,
|
use_color: bool,
|
||||||
) -> tuple[bool, bytes]:
|
) -> tuple[bool, bytes]:
|
||||||
filenames = classifier.filenames_for_hook(hook)
|
filenames = tuple(classifier.filenames_for_hook(hook))
|
||||||
|
|
||||||
if hook.id in skips or hook.alias in skips:
|
if hook.id in skips or hook.alias in skips:
|
||||||
output.write(
|
output.write(
|
||||||
|
@ -187,7 +187,7 @@ def _run_single_hook(
|
||||||
|
|
||||||
if not hook.pass_filenames:
|
if not hook.pass_filenames:
|
||||||
filenames = ()
|
filenames = ()
|
||||||
time_before = time.time()
|
time_before = time.monotonic()
|
||||||
language = languages[hook.language]
|
language = languages[hook.language]
|
||||||
with language.in_env(hook.prefix, hook.language_version):
|
with language.in_env(hook.prefix, hook.language_version):
|
||||||
retcode, out = language.run_hook(
|
retcode, out = language.run_hook(
|
||||||
|
@ -199,7 +199,7 @@ def _run_single_hook(
|
||||||
require_serial=hook.require_serial,
|
require_serial=hook.require_serial,
|
||||||
color=use_color,
|
color=use_color,
|
||||||
)
|
)
|
||||||
duration = round(time.time() - time_before, 2) or 0
|
duration = round(time.monotonic() - time_before, 2) or 0
|
||||||
diff_after = _get_diff()
|
diff_after = _get_diff()
|
||||||
|
|
||||||
# if the hook makes changes, fail the commit
|
# if the hook makes changes, fail the commit
|
||||||
|
@ -250,7 +250,7 @@ def _compute_cols(hooks: Sequence[Hook]) -> int:
|
||||||
return max(cols, 80)
|
return max(cols, 80)
|
||||||
|
|
||||||
|
|
||||||
def _all_filenames(args: argparse.Namespace) -> Collection[str]:
|
def _all_filenames(args: argparse.Namespace) -> Iterable[str]:
|
||||||
# these hooks do not operate on files
|
# these hooks do not operate on files
|
||||||
if args.hook_stage in {
|
if args.hook_stage in {
|
||||||
'post-checkout', 'post-commit', 'post-merge', 'post-rewrite',
|
'post-checkout', 'post-commit', 'post-merge', 'post-rewrite',
|
||||||
|
|
|
@ -93,7 +93,7 @@ def install_environment(
|
||||||
# install as if we installed from git
|
# install as if we installed from git
|
||||||
|
|
||||||
local_install_cmd = (
|
local_install_cmd = (
|
||||||
'npm', 'install', '--dev', '--prod',
|
'npm', 'install', '--include=dev', '--include=prod',
|
||||||
'--ignore-prepublish', '--no-progress', '--no-save',
|
'--ignore-prepublish', '--no-progress', '--no-save',
|
||||||
)
|
)
|
||||||
lang_base.setup_cmd(prefix, local_install_cmd)
|
lang_base.setup_cmd(prefix, local_install_cmd)
|
||||||
|
|
|
@ -21,7 +21,7 @@ def check_all_hooks_match_files(config_file: str) -> int:
|
||||||
for hook in all_hooks(config, Store()):
|
for hook in all_hooks(config, Store()):
|
||||||
if hook.always_run or hook.language == 'fail':
|
if hook.always_run or hook.language == 'fail':
|
||||||
continue
|
continue
|
||||||
elif not classifier.filenames_for_hook(hook):
|
elif not any(classifier.filenames_for_hook(hook)):
|
||||||
print(f'{hook.id} does not apply to this repository')
|
print(f'{hook.id} does not apply to this repository')
|
||||||
retv = 1
|
retv = 1
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
|
from typing import Iterable
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from cfgv import apply_defaults
|
from cfgv import apply_defaults
|
||||||
|
@ -14,7 +15,7 @@ from pre_commit.commands.run import Classifier
|
||||||
|
|
||||||
|
|
||||||
def exclude_matches_any(
|
def exclude_matches_any(
|
||||||
filenames: Sequence[str],
|
filenames: Iterable[str],
|
||||||
include: str,
|
include: str,
|
||||||
exclude: str,
|
exclude: str,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
@ -50,11 +51,12 @@ def check_useless_excludes(config_file: str) -> int:
|
||||||
# Not actually a manifest dict, but this more accurately reflects
|
# Not actually a manifest dict, but this more accurately reflects
|
||||||
# the defaults applied during runtime
|
# the defaults applied during runtime
|
||||||
hook = apply_defaults(hook, MANIFEST_HOOK_DICT)
|
hook = apply_defaults(hook, MANIFEST_HOOK_DICT)
|
||||||
names = classifier.filenames
|
names = classifier.by_types(
|
||||||
types = hook['types']
|
classifier.filenames,
|
||||||
types_or = hook['types_or']
|
hook['types'],
|
||||||
exclude_types = hook['exclude_types']
|
hook['types_or'],
|
||||||
names = classifier.by_types(names, types, types_or, exclude_types)
|
hook['exclude_types'],
|
||||||
|
)
|
||||||
include, exclude = hook['files'], hook['exclude']
|
include, exclude = hook['files'], hook['exclude']
|
||||||
if not exclude_matches_any(names, include, exclude):
|
if not exclude_matches_any(names, include, exclude):
|
||||||
print(
|
print(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = pre_commit
|
name = pre_commit
|
||||||
version = 3.4.0
|
version = 3.5.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
|
||||||
|
|
|
@ -293,7 +293,7 @@ def test_verbose_duration(cap_out, store, in_git_dir, t1, t2, expected):
|
||||||
write_config('.', {'repo': 'meta', 'hooks': [{'id': 'identity'}]})
|
write_config('.', {'repo': 'meta', 'hooks': [{'id': 'identity'}]})
|
||||||
cmd_output('git', 'add', '.')
|
cmd_output('git', 'add', '.')
|
||||||
opts = run_opts(verbose=True)
|
opts = run_opts(verbose=True)
|
||||||
with mock.patch.object(time, 'time', side_effect=(t1, t2)):
|
with mock.patch.object(time, 'monotonic', side_effect=(t1, t2)):
|
||||||
ret, printed = _do_run(cap_out, store, str(in_git_dir), opts)
|
ret, printed = _do_run(cap_out, store, str(in_git_dir), opts)
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert expected in printed
|
assert expected in printed
|
||||||
|
@ -1127,8 +1127,8 @@ def test_classifier_empty_types_or(tmpdir):
|
||||||
types_or=[],
|
types_or=[],
|
||||||
exclude_types=[],
|
exclude_types=[],
|
||||||
)
|
)
|
||||||
assert for_symlink == ['foo']
|
assert tuple(for_symlink) == ('foo',)
|
||||||
assert for_file == ['bar']
|
assert tuple(for_file) == ('bar',)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -1142,33 +1142,33 @@ def some_filenames():
|
||||||
|
|
||||||
def test_include_exclude_base_case(some_filenames):
|
def test_include_exclude_base_case(some_filenames):
|
||||||
ret = filter_by_include_exclude(some_filenames, '', '^$')
|
ret = filter_by_include_exclude(some_filenames, '', '^$')
|
||||||
assert ret == [
|
assert tuple(ret) == (
|
||||||
'.pre-commit-hooks.yaml',
|
'.pre-commit-hooks.yaml',
|
||||||
'pre_commit/git.py',
|
'pre_commit/git.py',
|
||||||
'pre_commit/main.py',
|
'pre_commit/main.py',
|
||||||
]
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_matches_broken_symlink(tmpdir):
|
def test_matches_broken_symlink(tmpdir):
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
os.symlink('does-not-exist', 'link')
|
os.symlink('does-not-exist', 'link')
|
||||||
ret = filter_by_include_exclude({'link'}, '', '^$')
|
ret = filter_by_include_exclude({'link'}, '', '^$')
|
||||||
assert ret == ['link']
|
assert tuple(ret) == ('link',)
|
||||||
|
|
||||||
|
|
||||||
def test_include_exclude_total_match(some_filenames):
|
def test_include_exclude_total_match(some_filenames):
|
||||||
ret = filter_by_include_exclude(some_filenames, r'^.*\.py$', '^$')
|
ret = filter_by_include_exclude(some_filenames, r'^.*\.py$', '^$')
|
||||||
assert ret == ['pre_commit/git.py', 'pre_commit/main.py']
|
assert tuple(ret) == ('pre_commit/git.py', 'pre_commit/main.py')
|
||||||
|
|
||||||
|
|
||||||
def test_include_exclude_does_search_instead_of_match(some_filenames):
|
def test_include_exclude_does_search_instead_of_match(some_filenames):
|
||||||
ret = filter_by_include_exclude(some_filenames, r'\.yaml$', '^$')
|
ret = filter_by_include_exclude(some_filenames, r'\.yaml$', '^$')
|
||||||
assert ret == ['.pre-commit-hooks.yaml']
|
assert tuple(ret) == ('.pre-commit-hooks.yaml',)
|
||||||
|
|
||||||
|
|
||||||
def test_include_exclude_exclude_removes_files(some_filenames):
|
def test_include_exclude_exclude_removes_files(some_filenames):
|
||||||
ret = filter_by_include_exclude(some_filenames, '', r'\.py$')
|
ret = filter_by_include_exclude(some_filenames, '', r'\.py$')
|
||||||
assert ret == ['.pre-commit-hooks.yaml']
|
assert tuple(ret) == ('.pre-commit-hooks.yaml',)
|
||||||
|
|
||||||
|
|
||||||
def test_args_hook_only(cap_out, store, repo_with_passing_hook):
|
def test_args_hook_only(cap_out, store, repo_with_passing_hook):
|
||||||
|
|
|
@ -43,7 +43,7 @@ def _run_try_repo(tempdir_factory, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def test_try_repo_repo_only(cap_out, tempdir_factory):
|
def test_try_repo_repo_only(cap_out, tempdir_factory):
|
||||||
with mock.patch.object(time, 'time', return_value=0.0):
|
with mock.patch.object(time, 'monotonic', return_value=0.0):
|
||||||
_run_try_repo(tempdir_factory, verbose=True)
|
_run_try_repo(tempdir_factory, verbose=True)
|
||||||
start, config, rest = _get_out(cap_out)
|
start, config, rest = _get_out(cap_out)
|
||||||
assert start == ''
|
assert start == ''
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import multiprocessing
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
@ -10,6 +9,7 @@ import pytest
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
from pre_commit import lang_base
|
from pre_commit import lang_base
|
||||||
from pre_commit import parse_shebang
|
from pre_commit import parse_shebang
|
||||||
|
from pre_commit import xargs
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import CalledProcessError
|
from pre_commit.util import CalledProcessError
|
||||||
|
|
||||||
|
@ -30,19 +30,6 @@ def homedir_mck():
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def no_sched_getaffinity():
|
|
||||||
# Simulates an OS without os.sched_getaffinity available (mac/windows)
|
|
||||||
# https://docs.python.org/3/library/os.html#interface-to-the-scheduler
|
|
||||||
with mock.patch.object(
|
|
||||||
os,
|
|
||||||
'sched_getaffinity',
|
|
||||||
create=True,
|
|
||||||
side_effect=AttributeError,
|
|
||||||
):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
def test_exe_exists_does_not_exist(find_exe_mck, homedir_mck):
|
def test_exe_exists_does_not_exist(find_exe_mck, homedir_mck):
|
||||||
find_exe_mck.return_value = None
|
find_exe_mck.return_value = None
|
||||||
assert lang_base.exe_exists('ruby') is False
|
assert lang_base.exe_exists('ruby') is False
|
||||||
|
@ -129,40 +116,23 @@ def test_no_env_noop(tmp_path):
|
||||||
assert before == inside == after
|
assert before == inside == after
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_sched_getaffinity(no_sched_getaffinity):
|
@pytest.fixture
|
||||||
with mock.patch.object(
|
def cpu_count_mck():
|
||||||
os,
|
with mock.patch.object(xargs, 'cpu_count', return_value=4):
|
||||||
'sched_getaffinity',
|
yield
|
||||||
return_value=set(range(345)),
|
|
||||||
):
|
|
||||||
with mock.patch.dict(os.environ, clear=True):
|
|
||||||
assert lang_base.target_concurrency() == 345
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_without_sched_getaffinity(no_sched_getaffinity):
|
@pytest.mark.parametrize(
|
||||||
with mock.patch.object(multiprocessing, 'cpu_count', return_value=123):
|
('var', 'expected'),
|
||||||
with mock.patch.dict(os.environ, {}, clear=True):
|
(
|
||||||
assert lang_base.target_concurrency() == 123
|
('PRE_COMMIT_NO_CONCURRENCY', 1),
|
||||||
|
('TRAVIS', 2),
|
||||||
|
(None, 4),
|
||||||
def test_target_concurrency_testing_env_var():
|
),
|
||||||
with mock.patch.dict(
|
)
|
||||||
os.environ, {'PRE_COMMIT_NO_CONCURRENCY': '1'}, clear=True,
|
def test_target_concurrency(cpu_count_mck, var, expected):
|
||||||
):
|
with mock.patch.dict(os.environ, {var: '1'} if var else {}, clear=True):
|
||||||
assert lang_base.target_concurrency() == 1
|
assert lang_base.target_concurrency() == expected
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_on_travis():
|
|
||||||
with mock.patch.dict(os.environ, {'TRAVIS': '1'}, clear=True):
|
|
||||||
assert lang_base.target_concurrency() == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_cpu_count_not_implemented(no_sched_getaffinity):
|
|
||||||
with mock.patch.object(
|
|
||||||
multiprocessing, 'cpu_count', side_effect=NotImplementedError,
|
|
||||||
):
|
|
||||||
with mock.patch.dict(os.environ, {}, clear=True):
|
|
||||||
assert lang_base.target_concurrency() == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_shuffled_is_deterministic():
|
def test_shuffled_is_deterministic():
|
||||||
|
|
|
@ -111,11 +111,11 @@ def test_golang_versioned(tmp_path):
|
||||||
tmp_path,
|
tmp_path,
|
||||||
golang,
|
golang,
|
||||||
'go version',
|
'go version',
|
||||||
version='1.18.4',
|
version='1.21.1',
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert out.startswith(b'go version go1.18.4')
|
assert out.startswith(b'go version go1.21.1')
|
||||||
|
|
||||||
|
|
||||||
def test_local_golang_additional_deps(tmp_path):
|
def test_local_golang_additional_deps(tmp_path):
|
||||||
|
|
|
@ -139,7 +139,7 @@ def test_node_with_user_config_set(tmp_path):
|
||||||
test_node_hook_system(tmp_path)
|
test_node_hook_system(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('version', (C.DEFAULT, '18.13.0'))
|
@pytest.mark.parametrize('version', (C.DEFAULT, '18.14.0'))
|
||||||
def test_node_hook_versions(tmp_path, version):
|
def test_node_hook_versions(tmp_path, version):
|
||||||
_make_hello_world(tmp_path)
|
_make_hello_world(tmp_path)
|
||||||
ret = run_language(tmp_path, node, 'node-hello', version=version)
|
ret = run_language(tmp_path, node, 'node-hello', version=version)
|
||||||
|
|
|
@ -133,17 +133,17 @@ def test_normalize_cmd_PATH():
|
||||||
|
|
||||||
|
|
||||||
def test_normalize_cmd_shebang(in_tmpdir):
|
def test_normalize_cmd_shebang(in_tmpdir):
|
||||||
echo = _echo_exe().replace(os.sep, '/')
|
us = sys.executable.replace(os.sep, '/')
|
||||||
path = write_executable(echo)
|
path = write_executable(us)
|
||||||
assert parse_shebang.normalize_cmd((path,)) == (echo, path)
|
assert parse_shebang.normalize_cmd((path,)) == (us, path)
|
||||||
|
|
||||||
|
|
||||||
def test_normalize_cmd_PATH_shebang_full_path(in_tmpdir):
|
def test_normalize_cmd_PATH_shebang_full_path(in_tmpdir):
|
||||||
echo = _echo_exe().replace(os.sep, '/')
|
us = sys.executable.replace(os.sep, '/')
|
||||||
path = write_executable(echo)
|
path = write_executable(us)
|
||||||
with bin_on_path():
|
with bin_on_path():
|
||||||
ret = parse_shebang.normalize_cmd(('run',))
|
ret = parse_shebang.normalize_cmd(('run',))
|
||||||
assert ret == (echo, os.path.abspath(path))
|
assert ret == (us, os.path.abspath(path))
|
||||||
|
|
||||||
|
|
||||||
def test_normalize_cmd_PATH_shebang_PATH(in_tmpdir):
|
def test_normalize_cmd_PATH_shebang_PATH(in_tmpdir):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
@ -12,6 +13,40 @@ from pre_commit import parse_shebang
|
||||||
from pre_commit import xargs
|
from pre_commit import xargs
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpu_count_sched_getaffinity_exists():
|
||||||
|
with mock.patch.object(
|
||||||
|
os, 'sched_getaffinity', create=True, return_value=set(range(345)),
|
||||||
|
):
|
||||||
|
assert xargs.cpu_count() == 345
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def no_sched_getaffinity():
|
||||||
|
# Simulates an OS without os.sched_getaffinity available (mac/windows)
|
||||||
|
# https://docs.python.org/3/library/os.html#interface-to-the-scheduler
|
||||||
|
with mock.patch.object(
|
||||||
|
os,
|
||||||
|
'sched_getaffinity',
|
||||||
|
create=True,
|
||||||
|
side_effect=AttributeError,
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpu_count_multiprocessing_cpu_count_implemented(no_sched_getaffinity):
|
||||||
|
with mock.patch.object(multiprocessing, 'cpu_count', return_value=123):
|
||||||
|
assert xargs.cpu_count() == 123
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpu_count_multiprocessing_cpu_count_not_implemented(
|
||||||
|
no_sched_getaffinity,
|
||||||
|
):
|
||||||
|
with mock.patch.object(
|
||||||
|
multiprocessing, 'cpu_count', side_effect=NotImplementedError,
|
||||||
|
):
|
||||||
|
assert xargs.cpu_count() == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('env', 'expected'),
|
('env', 'expected'),
|
||||||
(
|
(
|
||||||
|
|
Loading…
Add table
Reference in a new issue