Skip to content

Commit

Permalink
Merge pull request #2981 from Holzhaus/pre-commit-python-fixes
Browse files Browse the repository at this point in the history
tools: Refactor python clang-format wrapping
  • Loading branch information
daschuer authored Aug 5, 2020
2 parents 42fcb20 + eae9aba commit 35a3bc5
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 327 deletions.
16 changes: 3 additions & 13 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ repos:
hooks:
- id: clang-format
name: clang-format
entry: tools/clang_format_wrapper.py
description: "Run clang-format in two passes (reformat, then break long lines)"
entry: tools/clang_format.py
require_serial: true
stages:
- commit
- manual
language: python
files: \.(c|cc|cxx|cpp|frag|glsl|h|hpp|hxx|ih|ispc|ipp|java|js|m|mm|proto|vert)$
files: \.(c|cc|cxx|cpp|frag|glsl|h|hpp|hxx|ih|ispc|ipp|java|m|mm|proto|vert)$
additional_dependencies:
- clang-format
- repo: https://github.com/psf/black
Expand Down Expand Up @@ -114,14 +115,3 @@ repos:
stages:
- commit
- manual
- repo: local
hooks:
- id: line-length
name: line-length
description: Check for lines longer 100 and brakes them before 80.
entry: ./tools/line_length.py
stages:
- commit
- manual
language: python
files: \.(c|cpp|h)$
137 changes: 137 additions & 0 deletions tools/clang_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import logging
import os
import re
import subprocess
import sys
import tempfile
import typing

import githelper


# We recommend a maximum line length of 80, but do allow up to 100 characters
# if deemed necessary by the developer. Lines that exceed that limit will
# be wrapped after 80 characters automatically.
LINE_LENGTH_THRESHOLD = 100
BREAK_BEFORE = 80


def get_clang_format_config_with_columnlimit(rootdir, limit):
"""Create a temporary config with ColumnLimit set to 80."""
cpp_file = os.path.join(rootdir, "src/mixxx.cpp")
proc = subprocess.run(
["clang-format", "--dump-config", cpp_file],
capture_output=True,
text=True,
)
proc.check_returncode()
return re.sub(
r"(ColumnLimit:\s*)\d+", r"\g<1>{}".format(BREAK_BEFORE), proc.stdout
)


def run_clang_format_on_lines(rootdir, file_to_format, stylepath=None):
logger = logging.getLogger(__name__)

line_arguments = [
"--lines={}:{}".format(start, end)
for start, end in file_to_format.lines
]
assert line_arguments

logger.info("Reformatting %s...", file_to_format.filename)
filename = os.path.join(rootdir, file_to_format.filename)
cmd = [
"clang-format",
"--style=file",
# The --assume-filename argument sets the path for the .clang-format
# config file implcitly by assuming a different location of the file to
# format
"--assume-filename={}".format(
os.path.join(
stylepath if stylepath else rootdir, file_to_format.filename
)
),
*line_arguments,
]

with open(filename) as fp:
logger.debug("Executing: %r", cmd)
proc = subprocess.run(cmd, stdin=fp, capture_output=True, text=True)
try:
proc.check_returncode()
except subprocess.CalledProcessError:
logger.error(
"Error while executing command %s: %s", cmd, proc.stderr,
)
raise

if proc.stderr:
logger.error(proc.stderr)
with open(filename, mode="w") as fp:
fp.write(proc.stdout)


def main(argv: typing.Optional[typing.List[str]] = None) -> int:
logging.basicConfig(
format="[%(levelname)s] %(message)s", level=logging.INFO
)

logger = logging.getLogger(__name__)

parser = argparse.ArgumentParser()
parser.add_argument("--from-ref", help="use changes changes since commit")
parser.add_argument("--to-ref", help="use changes until commit")
parser.add_argument("files", nargs="*", help="only check these files")
args = parser.parse_args(argv)

if not args.from_ref:
args.from_ref = os.getenv("PRE_COMMIT_FROM_REF") or os.getenv(
"PRE_COMMIT_SOURCE"
)

if not args.to_ref:
args.to_ref = os.getenv("PRE_COMMIT_TO_REF") or os.getenv(
"PRE_COMMIT_ORIGIN"
)

# Filter filenames
rootdir = githelper.get_toplevel_path()

# First pass: Format added/changed lines using clang-format
logger.info("First pass: Reformatting added/changed lines...")
files_with_added_lines = githelper.get_changed_lines_grouped(
from_ref=args.from_ref,
to_ref=args.to_ref,
filter_lines=lambda line: line.added,
include_files=args.files,
)
for changed_file in files_with_added_lines:
run_clang_format_on_lines(rootdir, changed_file)

# Second pass: Wrap long added/changed lines using clang-format
logger.info("Second pass: Breaking long added/changed lines...")
files_with_long_added_lines = githelper.get_changed_lines_grouped(
from_ref=args.from_ref,
to_ref=args.to_ref,
filter_lines=lambda line: line.added
and len(line.text) > LINE_LENGTH_THRESHOLD,
include_files=args.files,
)
config = get_clang_format_config_with_columnlimit(rootdir, BREAK_BEFORE)
with tempfile.TemporaryDirectory(prefix="clang-format") as tempdir:
# Create temporary config with ColumnLimit enabled
configfile = os.path.join(tempdir, ".clang-format")
with open(configfile, mode="w") as configfp:
configfp.write(config)

for changed_file in files_with_long_added_lines:
run_clang_format_on_lines(rootdir, changed_file, stylepath=tempdir)
return 0


if __name__ == "__main__":
sys.exit(main())
130 changes: 0 additions & 130 deletions tools/clang_format_wrapper.py

This file was deleted.

Loading

0 comments on commit 35a3bc5

Please sign in to comment.