"""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 subprocess # nosec - Needed for hook from typing import List from isort import SortImports 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) -> 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. :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"] files_modified = get_lines(diff_cmd) errors = 0 for filename in files_modified: if filename.endswith(".py"): # Get the staged contents of the file staged_cmd = ["git", "show", ":%s" % filename] staged_contents = get_output(staged_cmd) sort = SortImports(file_path=filename, file_contents=staged_contents, check=True) if sort.incorrectly_sorted: errors += 1 if modify: SortImports(file_path=filename, file_contents=staged_contents, check=False) return errors if strict else 0