xref: /aosp_15_r20/external/pytorch/tools/linter/adapters/exec_linter.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1"""
2EXEC: Ensure that source files are not executable.
3"""
4
5from __future__ import annotations
6
7import argparse
8import json
9import logging
10import os
11import sys
12from enum import Enum
13from typing import NamedTuple
14
15
16LINTER_CODE = "EXEC"
17
18
19class LintSeverity(str, Enum):
20    ERROR = "error"
21    WARNING = "warning"
22    ADVICE = "advice"
23    DISABLED = "disabled"
24
25
26class LintMessage(NamedTuple):
27    path: str | None
28    line: int | None
29    char: int | None
30    code: str
31    severity: LintSeverity
32    name: str
33    original: str | None
34    replacement: str | None
35    description: str | None
36
37
38def check_file(filename: str) -> LintMessage | None:
39    is_executable = os.access(filename, os.X_OK)
40    if is_executable:
41        return LintMessage(
42            path=filename,
43            line=None,
44            char=None,
45            code=LINTER_CODE,
46            severity=LintSeverity.ERROR,
47            name="executable-permissions",
48            original=None,
49            replacement=None,
50            description="This file has executable permission; please remove it by using `chmod -x`.",
51        )
52    return None
53
54
55if __name__ == "__main__":
56    parser = argparse.ArgumentParser(
57        description="exec linter",
58        fromfile_prefix_chars="@",
59    )
60    parser.add_argument(
61        "--verbose",
62        action="store_true",
63    )
64    parser.add_argument(
65        "filenames",
66        nargs="+",
67        help="paths to lint",
68    )
69
70    args = parser.parse_args()
71
72    logging.basicConfig(
73        format="<%(threadName)s:%(levelname)s> %(message)s",
74        level=logging.NOTSET
75        if args.verbose
76        else logging.DEBUG
77        if len(args.filenames) < 1000
78        else logging.INFO,
79        stream=sys.stderr,
80    )
81
82    lint_messages = []
83    for filename in args.filenames:
84        lint_message = check_file(filename)
85        if lint_message is not None:
86            lint_messages.append(lint_message)
87
88    for lint_message in lint_messages:
89        print(json.dumps(lint_message._asdict()), flush=True)
90