mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-26 17:05:31 +01:00
88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
|
"""Defines a git hook to allow pre-commit warnings and errors about import order.
|
||
|
|
||
|
usage:
|
||
|
exit_code = git_hook(strict=True|False, modify=True|False)
|
||
|
"""
|
||
|
import os
|
||
|
import subprocess # nosec - Needed for hook
|
||
|
from pathlib import Path
|
||
|
from typing import List
|
||
|
|
||
|
from isort import Config, api, exceptions
|
||
|
|
||
|
|
||
|
def get_output(command: List[str]) -> str:
|
||
|
"""Run a command and return raw output
|
||
|
|
||
|
:param str command: the command to run
|
||
|
:returns: the stdout output of the command
|
||
|
"""
|
||
|
result = subprocess.run(command, stdout=subprocess.PIPE, check=True) # nosec - trusted input
|
||
|
return result.stdout.decode()
|
||
|
|
||
|
|
||
|
def get_lines(command: List[str]) -> List[str]:
|
||
|
"""Run a command and return lines of output
|
||
|
|
||
|
:param str command: the command to run
|
||
|
:returns: list of whitespace-stripped lines output by command
|
||
|
"""
|
||
|
stdout = get_output(command)
|
||
|
return [line.strip() for line in stdout.splitlines()]
|
||
|
|
||
|
|
||
|
def git_hook(
|
||
|
strict: bool = False, modify: bool = False, lazy: bool = False, settings_file: str = ""
|
||
|
) -> int:
|
||
|
"""Git pre-commit hook to check staged files for isort errors
|
||
|
|
||
|
:param bool strict - if True, return number of errors on exit,
|
||
|
causing the hook to fail. If False, return zero so it will
|
||
|
just act as a warning.
|
||
|
:param bool modify - if True, fix the sources if they are not
|
||
|
sorted properly. If False, only report result without
|
||
|
modifying anything.
|
||
|
:param bool lazy - if True, also check/fix unstaged files.
|
||
|
This is useful if you frequently use ``git commit -a`` for example.
|
||
|
If False, ony check/fix the staged files for isort errors.
|
||
|
:param str settings_file - A path to a file to be used as
|
||
|
the configuration file for this run.
|
||
|
When settings_file is the empty string, the configuration file
|
||
|
will be searched starting at the directory containing the first
|
||
|
staged file, if any, and going upward in the directory structure.
|
||
|
|
||
|
:return number of errors if in strict mode, 0 otherwise.
|
||
|
"""
|
||
|
|
||
|
# Get list of files modified and staged
|
||
|
diff_cmd = ["git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD"]
|
||
|
if lazy:
|
||
|
diff_cmd.remove("--cached")
|
||
|
|
||
|
files_modified = get_lines(diff_cmd)
|
||
|
if not files_modified:
|
||
|
return 0
|
||
|
|
||
|
errors = 0
|
||
|
config = Config(
|
||
|
settings_file=settings_file,
|
||
|
settings_path=os.path.dirname(os.path.abspath(files_modified[0])),
|
||
|
)
|
||
|
for filename in files_modified:
|
||
|
if filename.endswith(".py"):
|
||
|
# Get the staged contents of the file
|
||
|
staged_cmd = ["git", "show", f":{filename}"]
|
||
|
staged_contents = get_output(staged_cmd)
|
||
|
|
||
|
try:
|
||
|
if not api.check_code_string(
|
||
|
staged_contents, file_path=Path(filename), config=config
|
||
|
):
|
||
|
errors += 1
|
||
|
if modify:
|
||
|
api.sort_file(filename, config=config)
|
||
|
except exceptions.FileSkipped: # pragma: no cover
|
||
|
pass
|
||
|
|
||
|
return errors if strict else 0
|