-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…mlc#4977) * add lint workflow * add line number * address comments * update package versions * remove autopep8 config
- Loading branch information
Showing
6 changed files
with
587 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
name: Lint | ||
|
||
on: [pull_request] | ||
|
||
jobs: | ||
lintrunner: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Pull DGL | ||
uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Checkout master and HEAD | ||
run: | | ||
git checkout -t origin/master | ||
git checkout ${{ github.event.pull_request.head.sha }} | ||
- name: Setup Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.8' | ||
|
||
- name: Install requirements | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install lintrunner --user | ||
- name: Initialize lint dependencies | ||
run: lintrunner init | ||
|
||
- name: Run lintrunner on all changed files | ||
run: | | ||
set +e | ||
if ! lintrunner --force-color -m master --tee-json=lint.json; then | ||
echo "" | ||
echo -e "\e[1m\e[36mYou can reproduce these results locally by using \`lintrunner\`.\e[0m" | ||
echo -e "\e[1m\e[36mSee https://github.com/pytorch/pytorch/wiki/lintrunner for setup instructions.\e[0m" | ||
exit 1 | ||
fi | ||
- name: Store annotations | ||
if: always() && github.event_name == 'pull_request' | ||
# Don't show this as an error; the above step will have already failed. | ||
continue-on-error: true | ||
run: | | ||
# Use jq to massage the JSON lint output into GitHub Actions workflow commands. | ||
jq --raw-output \ | ||
'"::\(if .severity == "advice" or .severity == "disabled" then "warning" else .severity end) file=\(.path),line=\(.line),col=\(.char),title=\(.code) \(.name)::" + (.description | gsub("\\n"; "%0A"))' \ | ||
lint.json | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.event_name == 'workflow_dispatch' }} | ||
cancel-in-progress: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Black + usort | ||
[[linter]] | ||
code = 'UFMT' | ||
include_patterns = [ | ||
'**/*.py', | ||
] | ||
command = [ | ||
'python3', | ||
'tests/lint/ufmt_linter.py', | ||
'--', | ||
'@{{PATHSFILE}}' | ||
] | ||
exclude_patterns = [ | ||
'.github/*', | ||
'build/*', | ||
'cmake/*', | ||
'conda/*', | ||
'docker/*', | ||
'third_party/*', | ||
] | ||
init_command = [ | ||
'python3', | ||
'tests/lint/pip_init.py', | ||
'--dry-run={{DRYRUN}}', | ||
'black==22.10.0', | ||
'ufmt==2.0.1', | ||
'usort==1.0.5', | ||
] | ||
is_formatter = true | ||
|
||
[[linter]] | ||
code = 'CLANGFORMAT' | ||
include_patterns = [ | ||
'**/*.h', | ||
'**/*.c', | ||
'**/*.cc', | ||
'**/*.cpp', | ||
'**/*.cuh', | ||
'**/*.cu', | ||
] | ||
exclude_patterns = [ | ||
] | ||
init_command = [ | ||
'python3', | ||
'tests/lint/pip_init.py', | ||
'--dry-run={{DRYRUN}}', | ||
'clang-format==15.0.4', | ||
] | ||
command = [ | ||
'python3', | ||
'tests/lint/clangformat_linter.py', | ||
'--binary=clang-format', | ||
'--', | ||
'@{{PATHSFILE}}' | ||
] | ||
is_formatter = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,3 @@ | ||
[tool.black] | ||
|
||
line-length = 80 | ||
|
||
|
||
[tool.autopep8] | ||
|
||
max_line_length = 80 | ||
in-place = true | ||
aggressive = 3 | ||
# Add the path to here if you want to exclude them from autopep8 auto reformat. | ||
# When a directory or multiple files are passed to autopep8, it will ignore the | ||
# following directory and files. It is not recommended to pass a directory to | ||
# autopep8. | ||
exclude = ''' | ||
.github/*, | ||
build/*, | ||
cmake/*, | ||
conda/*, | ||
docker/*, | ||
third_party/*, | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
"""Borrowed from github.com/pytorch/pytorch/tools/linter/adapters/clangformat_linter.py""" | ||
import argparse | ||
import concurrent.futures | ||
import json | ||
import logging | ||
import os | ||
import subprocess | ||
import sys | ||
import time | ||
from enum import Enum | ||
from pathlib import Path | ||
from typing import Any, List, NamedTuple, Optional | ||
|
||
|
||
IS_WINDOWS: bool = os.name == "nt" | ||
|
||
|
||
def eprint(*args: Any, **kwargs: Any) -> None: | ||
print(*args, file=sys.stderr, flush=True, **kwargs) | ||
|
||
|
||
class LintSeverity(str, Enum): | ||
ERROR = "error" | ||
WARNING = "warning" | ||
ADVICE = "advice" | ||
DISABLED = "disabled" | ||
|
||
|
||
class LintMessage(NamedTuple): | ||
path: Optional[str] | ||
line: Optional[int] | ||
char: Optional[int] | ||
code: str | ||
severity: LintSeverity | ||
name: str | ||
original: Optional[str] | ||
replacement: Optional[str] | ||
description: Optional[str] | ||
|
||
|
||
def as_posix(name: str) -> str: | ||
return name.replace("\\", "/") if IS_WINDOWS else name | ||
|
||
|
||
def _run_command( | ||
args: List[str], | ||
*, | ||
timeout: int, | ||
) -> "subprocess.CompletedProcess[bytes]": | ||
logging.debug("$ %s", " ".join(args)) | ||
start_time = time.monotonic() | ||
try: | ||
return subprocess.run( | ||
args, | ||
stdout=subprocess.PIPE, | ||
stderr=subprocess.PIPE, | ||
shell=IS_WINDOWS, # So batch scripts are found. | ||
timeout=timeout, | ||
check=True, | ||
) | ||
finally: | ||
end_time = time.monotonic() | ||
logging.debug("took %dms", (end_time - start_time) * 1000) | ||
|
||
|
||
def run_command( | ||
args: List[str], | ||
*, | ||
retries: int, | ||
timeout: int, | ||
) -> "subprocess.CompletedProcess[bytes]": | ||
remaining_retries = retries | ||
while True: | ||
try: | ||
return _run_command(args, timeout=timeout) | ||
except subprocess.TimeoutExpired as err: | ||
if remaining_retries == 0: | ||
raise err | ||
remaining_retries -= 1 | ||
logging.warning( | ||
"(%s/%s) Retrying because command failed with: %r", | ||
retries - remaining_retries, | ||
retries, | ||
err, | ||
) | ||
time.sleep(1) | ||
|
||
|
||
def check_file( | ||
filename: str, | ||
binary: str, | ||
retries: int, | ||
timeout: int, | ||
) -> List[LintMessage]: | ||
try: | ||
with open(filename, "rb") as f: | ||
original = f.read() | ||
proc = run_command( | ||
[binary, filename], | ||
retries=retries, | ||
timeout=timeout, | ||
) | ||
except subprocess.TimeoutExpired: | ||
return [ | ||
LintMessage( | ||
path=filename, | ||
line=None, | ||
char=None, | ||
code="CLANGFORMAT", | ||
severity=LintSeverity.ERROR, | ||
name="timeout", | ||
original=None, | ||
replacement=None, | ||
description=( | ||
"clang-format timed out while trying to process a file. " | ||
"Please report an issue in pytorch/pytorch with the " | ||
"label 'module: lint'" | ||
), | ||
) | ||
] | ||
except (OSError, subprocess.CalledProcessError) as err: | ||
return [ | ||
LintMessage( | ||
path=filename, | ||
line=None, | ||
char=None, | ||
code="CLANGFORMAT", | ||
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 ( | ||
"COMMAND (exit code {returncode})\n" | ||
"{command}\n\n" | ||
"STDERR\n{stderr}\n\n" | ||
"STDOUT\n{stdout}" | ||
).format( | ||
returncode=err.returncode, | ||
command=" ".join(as_posix(x) for x in err.cmd), | ||
stderr=err.stderr.decode("utf-8").strip() or "(empty)", | ||
stdout=err.stdout.decode("utf-8").strip() or "(empty)", | ||
) | ||
), | ||
) | ||
] | ||
|
||
replacement = proc.stdout | ||
if original == replacement: | ||
return [] | ||
|
||
line = 0 | ||
original = original.decode("utf-8") | ||
replacement = replacement.decode("utf-8") | ||
for line, (i, j) in enumerate( | ||
zip(original.split("\n"), replacement.split("\n")) | ||
): | ||
if i != j: | ||
break | ||
|
||
return [ | ||
LintMessage( | ||
path=filename, | ||
line=line, | ||
char=None, | ||
code="CLANGFORMAT", | ||
severity=LintSeverity.WARNING, | ||
name="format", | ||
original=original, | ||
replacement=replacement, | ||
description="See https://clang.llvm.org/docs/ClangFormat.html.\nRun `lintrunner -a` to apply this patch.", | ||
) | ||
] | ||
|
||
|
||
def main() -> None: | ||
parser = argparse.ArgumentParser( | ||
description="Format files with clang-format.", | ||
fromfile_prefix_chars="@", | ||
) | ||
parser.add_argument( | ||
"--binary", | ||
required=True, | ||
help="clang-format binary path", | ||
) | ||
parser.add_argument( | ||
"--retries", | ||
default=3, | ||
type=int, | ||
help="times to retry timed out clang-format", | ||
) | ||
parser.add_argument( | ||
"--timeout", | ||
default=90, | ||
type=int, | ||
help="seconds to wait for clang-format", | ||
) | ||
parser.add_argument( | ||
"--verbose", | ||
action="store_true", | ||
help="verbose logging", | ||
) | ||
parser.add_argument( | ||
"filenames", | ||
nargs="+", | ||
help="paths to lint", | ||
) | ||
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.binary, 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(): | ||
print(json.dumps(lint_message._asdict()), flush=True) | ||
except Exception: | ||
logging.critical('Failed at "%s".', futures[future]) | ||
raise | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.