diff --git a/example-lint b/example-lint index 39413da..0d4f4f7 100755 --- a/example-lint +++ b/example-lint @@ -21,11 +21,11 @@ def run() -> None: # eg: file_types = ['py', 'html', 'css', 'js'] file_types = ["py"] - EXCLUDED_FILES: List[str] = [ + excluded_files: List[str] = [ # No linters will be run on files in this list. # eg: 'path/to/file.py' ] - by_lang = linter_config.list_files(file_types, exclude=EXCLUDED_FILES) + by_lang = linter_config.list_files(file_types, exclude=excluded_files) linter_config.external_linter( "mypy", diff --git a/pyproject.toml b/pyproject.toml index d1cbaa3..2b4ad36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,53 @@ profile = "black" [tool.ruff] # See https://github.com/charliermarsh/ruff#rules for error code definitions. select = [ + "ANN", # annotations + "B", # bugbear + "BLE", # blind except + "C4", # comprehensions + "COM", # trailing comma + "DTZ", # naive datetime + "E", # style errors + "EXE", # shebang + "F", # flakes + "FLY", # string formatting + "FURB", # refurb + "G", # logging format "I", # import sorting + "ICN", # import conventions + "INT", # gettext + "ISC", # string concatenation + "LOG", # logging + "N", # naming + "PERF", # performance + "PGH", # pygrep-hooks + "PIE", # miscellaneous + "PL", # pylint + "PYI", # typing stubs + "Q", # quotes + "RET", # return + "RSE", # raise + "RUF", # Ruff + "S", # security + "SLF", # self + "SLOT", # slots + "SIM", # simplify + "T10", # debugger + "TID", # tidy imports + "TRY", # try + "UP", # upgrade + "W", # style warnings + "YTT", # sys.version +] +extend-ignore = [ + "ANN101", # Missing type annotation for `self` in method + "COM812", # Trailing comma missing + "E501", # Line too long + "PLR0911", # Too many return statements + "PLR0913", # Too many arguments in function definition + "S101", # Use of `assert` detected + "S603", # `subprocess` call: check for execution of untrusted input + "S607", # Starting a process with a partial executable path ] src = ["."] target-version = "py38" diff --git a/tools/run-mypy b/tools/run-mypy index b092349..af568ed 100755 --- a/tools/run-mypy +++ b/tools/run-mypy @@ -15,7 +15,7 @@ os.chdir(os.path.dirname(TOOLS_DIR)) sys.path.append(os.path.dirname(TOOLS_DIR)) -parser = argparse.ArgumentParser(description="Run mypy on files tracked" " by git.") +parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.") parser.add_argument( "targets", diff --git a/zulint/command.py b/zulint/command.py index e117d3c..50112e9 100644 --- a/zulint/command.py +++ b/zulint/command.py @@ -64,7 +64,7 @@ def add_default_linter_arguments(parser: argparse.ArgumentParser) -> None: def split_arg_into_list(arg: str) -> List[str]: - return [linter for linter in arg.split(",")] + return arg.split(",") run_parallel_functions: "weakref.WeakValueDictionary[int, Callable[[], int]]" = ( @@ -75,11 +75,11 @@ def split_arg_into_list(arg: str) -> List[str]: def run_parallel_worker(item: Tuple[str, int]) -> Tuple[str, int]: name, func_id = item func = run_parallel_functions[func_id] - logging.info("start {}".format(name)) + logging.info("start %s", name) time_start = time.perf_counter() result = func() time_end = time.perf_counter() - logging.info("finish {}; elapsed time: {}".format(name, time_end - time_start)) + logging.info("finish %s; elapsed time: %g", name, time_end - time_start) sys.stdout.flush() sys.stderr.flush() return name, result @@ -108,14 +108,13 @@ def run_parallel( class LinterConfig: - lint_functions: Dict[str, Callable[[], int]] = {} - lint_descriptions: Dict[str, str] = {} - fixable_linters: Set[str] = set() - def __init__(self, args: argparse.Namespace) -> None: self.args = args self.by_lang: Dict[str, List[str]] = {} self.groups: Mapping[str, Sequence[str]] = {} + self.lint_functions: Dict[str, Callable[[], int]] = {} + self.lint_descriptions: Dict[str, str] = {} + self.fixable_linters: Set[str] = set() def list_files( self, @@ -222,7 +221,7 @@ def do_lint(self) -> NoReturn: if self.args.list: print("{}{:<15} {} {}".format(BOLDRED, "Linter", "Description", ENDC)) for linter, desc in self.lint_descriptions.items(): - print("{}{:<15} {}{}{}".format(BLUE, linter, GREEN, desc, ENDC)) + print(f"{BLUE}{linter:<15} {GREEN}{desc}{ENDC}") sys.exit() if self.args.list_groups: print("{}{:<15} {} {}".format(BOLDRED, "Linter Group", "File types", ENDC)) @@ -238,7 +237,7 @@ def do_lint(self) -> NoReturn: jobs = self.args.jobs if self.args.fix: # Do not run multiple fixers in parallel, since they might - # race with each other and corrupt each other’s output. + # race with each other and corrupt each other's output. jobs = 1 failed_linters = run_parallel(self.lint_functions, jobs) diff --git a/zulint/custom_rules.py b/zulint/custom_rules.py index 3f37aa0..d9b5af7 100644 --- a/zulint/custom_rules.py +++ b/zulint/custom_rules.py @@ -6,20 +6,16 @@ from zulint.printer import BLUE, ENDC, GREEN, MAGENTA, YELLOW, colors, print_err -Rule = TypedDict( - "Rule", - { - "bad_lines": Sequence[str], - "description": str, - "exclude": AbstractSet[str], - "exclude_line": AbstractSet[Tuple[str, str]], - "exclude_pattern": str, - "good_lines": Sequence[str], - "include_only": AbstractSet[str], - "pattern": str, - }, - total=False, -) + +class Rule(TypedDict, total=False): + bad_lines: Sequence[str] + description: str + exclude: AbstractSet[str] + exclude_line: AbstractSet[Tuple[str, str]] + exclude_pattern: str + good_lines: Sequence[str] + include_only: AbstractSet[str] + pattern: str class RuleList: @@ -96,17 +92,15 @@ def check_file_for_pattern( if line_fully_stripped in exclude_lines: unmatched_exclude_lines.discard(line_fully_stripped) continue - if rule.get("exclude_pattern"): - if re.search(rule["exclude_pattern"], line_fully_stripped): - continue + if rule.get("exclude_pattern") and re.search( + rule["exclude_pattern"], line_fully_stripped + ): + continue self.print_error(rule, line, identifier, color, fn, i + 1) ok = False if unmatched_exclude_lines: - print( - "Please remove exclusions for file %s: %s" - % (fn, unmatched_exclude_lines) - ) + print("Please remove exclusions for file {fn}: {unmatched_exclude_lines}") return ok diff --git a/zulint/linters.py b/zulint/linters.py index 1cf1b07..ca75dec 100644 --- a/zulint/linters.py +++ b/zulint/linters.py @@ -26,10 +26,8 @@ def run_command( try: signal_name = signal.Signals(-p.returncode).name except (AttributeError, ValueError): - signal_name = "signal {}".format(-p.returncode) - print_err( - name, color, "{} terminated by {}".format(command[0], signal_name) - ) + signal_name = f"signal {-p.returncode}" + print_err(name, color, f"{command[0]} terminated by {signal_name}") return p.returncode diff --git a/zulint/lister.py b/zulint/lister.py index 7fc2646..31e8a95 100755 --- a/zulint/lister.py +++ b/zulint/lister.py @@ -15,32 +15,31 @@ def get_ftype(fpath: str, use_shebang: bool) -> str: ext = os.path.splitext(fpath)[1] if ext: return ext[1:] - elif use_shebang: + + if use_shebang: # opening a file may throw an OSError with open(fpath, encoding="utf8") as f: first_line = f.readline() if re.search(r"^#!.*\bpython", first_line): return "py" - elif re.search(r"^#!.*sh", first_line): + if re.search(r"^#!.*sh", first_line): return "sh" - elif re.search(r"^#!.*\bperl", first_line): + if re.search(r"^#!.*\bperl", first_line): return "pl" - elif re.search(r"^#!.*\bnode", first_line): + if re.search(r"^#!.*\bnode", first_line): return "js" - elif re.search(r"^#!.*\bruby", first_line): + if re.search(r"^#!.*\bruby", first_line): return "rb" - elif re.search(r"^#!.*\btail", first_line): + if re.search(r"^#!.*\btail", first_line): return "" # do not lint these scripts. - elif re.search(r"^#!", first_line): + if re.search(r"^#!", first_line): print( - 'Error: Unknown shebang in file "%s":\n%s' % (fpath, first_line), + f'Error: Unknown shebang in file "{path}":\n{first_line}', file=sys.stderr, ) return "" - else: - return "" - else: - return "" + + return "" @overload @@ -144,7 +143,7 @@ def list_files( except (OSError, UnicodeDecodeError) as e: etype = e.__class__.__name__ print( - 'Error: %s while determining type of file "%s":' % (etype, fpath), + f'Error: {etype} while determining type of file "{fpath}":', file=sys.stderr, ) print(e, file=sys.stderr) @@ -159,8 +158,7 @@ def list_files( if group_by_ftype: return result_dict - else: - return result_list + return result_list if __name__ == "__main__":