1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- """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, only 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
|