Skip to content

Module isort.hooks

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)

View Source
"""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

Functions

get_lines

def get_lines(
    command: List[str]
) -> List[str]

Run a command and return lines of output

Parameters:

Name Type Description Default
command str the command to run None

Returns:

Type Description
None list of whitespace-stripped lines output by command
View Source
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()]

get_output

def get_output(
    command: List[str]
) -> str

Run a command and return raw output

Parameters:

Name Type Description Default
command str the command to run None

Returns:

Type Description
None the stdout output of the command
View Source
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()

git_hook

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.

View Source
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