Skip to content

Commit

Permalink
feat: Add toml-sort linter (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
q0w authored Jun 4, 2023
1 parent c634956 commit cb49c15
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 0 deletions.
21 changes: 21 additions & 0 deletions examples/adapters/toml_sort_linter/.lintrunner.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[[linter]]
code = 'TOML-SORT'
include_patterns = ['**/*.toml']
exclude_patterns = []
command = [
'python',
'-m',
'lintrunner_adapters',
'run',
'toml_sort_linter',
'@{{PATHSFILE}}'
]
init_command = [
'python',
'-m',
'lintrunner_adapters',
'run',
'pip_init',
'--dry-run={{DRYRUN}}',
'toml-sort==0.23.1',
]
143 changes: 143 additions & 0 deletions lintrunner_adapters/adapters/toml_sort_linter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"""Adapter for https://github.com/pappasam/toml-sort."""

from __future__ import annotations

import argparse
import concurrent.futures
import logging
import os
import subprocess
import sys

from lintrunner_adapters import (
LintMessage,
LintSeverity,
add_default_options,
as_posix,
run_command,
)

LINTER_CODE = "TOML-SORT"


def check_file(
filename: str,
retries: int,
timeout: int,
) -> list[LintMessage]:
try:
with open(filename, "rb") as f:
original = f.read()
with open(filename, "rb") as f:
proc = run_command(
["toml-sort", "-"],
stdin=f,
retries=retries,
timeout=timeout,
check=True,
)
except subprocess.TimeoutExpired:
return [
LintMessage(
path=filename,
line=None,
char=None,
code=LINTER_CODE,
severity=LintSeverity.ERROR,
name="timeout",
original=None,
replacement=None,
description="toml-sort timed out while trying to process a file.",
)
]
except (OSError, subprocess.CalledProcessError) as err:
return [
LintMessage(
path=filename,
line=None,
char=None,
code=LINTER_CODE,
severity=LintSeverity.ADVICE,
name="command-failed",
original=None,
replacement=None,
description=(
f"Failed due to {err.__class__.__name__}:\n{err}"
if not isinstance(err, subprocess.CalledProcessError)
else (
f"COMMAND (exit code {err.returncode})\n"
f"{' '.join(as_posix(x) for x in err.cmd)}\n\n"
f"STDERR\n{err.stderr.decode('utf-8').strip() or '(empty)'}\n\n"
f"STDOUT\n{err.stdout.decode('utf-8').strip() or '(empty)'}"
)
),
)
]

replacement = proc.stdout
if original == replacement:
return []

return [
LintMessage(
path=filename,
line=None,
char=None,
code=LINTER_CODE,
severity=LintSeverity.WARNING,
name="format",
original=original.decode("utf-8"),
replacement=replacement.decode("utf-8"),
description="Run `lintrunner -a` to apply this patch.",
)
]


def main() -> None:
parser = argparse.ArgumentParser(
description=f"toml-sort wrapper linter. Linter code: {LINTER_CODE}",
fromfile_prefix_chars="@",
)
parser.add_argument(
"--timeout",
default=90,
type=int,
help="seconds to wait for toml-sort",
)
add_default_options(parser)
args = parser.parse_args()

logging.basicConfig(
format="<%(threadName)s:%(levelname)s> %(message)s",
level=logging.NOTSET
if args.verbose
else logging.DEBUG
if len(args.filenames) < 1000
else logging.INFO,
stream=sys.stderr,
)

with concurrent.futures.ThreadPoolExecutor(
max_workers=os.cpu_count(),
thread_name_prefix="Thread",
) as executor:
futures = {
executor.submit(
check_file,
x,
args.retries,
args.timeout,
): x
for x in args.filenames
}
for future in concurrent.futures.as_completed(futures):
try:
for lint_message in future.result():
lint_message.display()
except Exception:
logging.critical('Failed at "%s".', futures[future])
raise


if __name__ == "__main__":
main()

0 comments on commit cb49c15

Please sign in to comment.