#!/usr/bin/env python3 import argparse import os import re import sys import colorama from helpers import ( filter_changed, get_err, get_output, git_ls_files, print_error_for_file, styled, ) curfile = None def print_error(file, lineno, msg): global curfile # noqa: PLW0603 if curfile != file: print_error_for_file(file, None) curfile = file if lineno is not None: print(f"{styled(colorama.Style.BRIGHT, f'{file}:{lineno}:')} {msg}") else: print(f"{styled(colorama.Style.BRIGHT, f'{file}:')} {msg}") def split_args_platform_compatible(args): if os.name == "posix": return [args] char_length = 0 argsets = [] for index, arg in enumerate(args): # Windows is techincally 8191, but we need to leave some room for the command itself if char_length + len(arg) > 8000: argsets.append(args[:index]) args = args[index:] char_length = 0 char_length += len(arg) return argsets def main(): colorama.init() parser = argparse.ArgumentParser() parser.add_argument( "files", nargs="*", default=[], help="files to be processed (regex on path)" ) parser.add_argument( "-c", "--changed", action="store_true", help="Only run on changed files" ) parser.add_argument( "-a", "--apply", action="store_true", help="Apply changes to files where possible", ) args = parser.parse_args() files = [] for path in git_ls_files(): filetypes = (".py",) ext = os.path.splitext(path)[1] if ext in filetypes and path.startswith("esphome"): path = os.path.relpath(path, os.getcwd()) files.append(path) # Match against re file_name_re = re.compile("|".join(args.files)) files = [p for p in files if file_name_re.search(p)] if args.changed: files = filter_changed(files) files.sort() if not files: sys.exit(0) errors = 0 # Needed to get around command-line string limits in Windows. filesets = split_args_platform_compatible(files) print("Running ruff...") print() for fileset in filesets: cmd = ["ruff", "format"] + ([] if args.apply else ["--check"]) + fileset log = get_err(*cmd) for line in log.splitlines(): WOULD_REFORMAT = "would reformat" if line.startswith(WOULD_REFORMAT): file_ = line[len(WOULD_REFORMAT) + 1 :] print_error( file_, None, "Please format this file with the ruff formatter" ) errors += 1 print() print("Running flake8...") print() for files in filesets: cmd = ["flake8"] + files log = get_output(*cmd) for line in log.splitlines(): line = line.split(":", 4) if len(line) < 4: continue file_ = line[0] linno = line[1] msg = (":".join(line[3:])).strip() print_error(file_, linno, msg) errors += 1 print() print("Running pylint...") print() for files in filesets: cmd = ["pylint", "-f", "parseable", "--persistent=n"] + files log = get_output(*cmd) for line in log.splitlines(): line = line.split(":", 3) if len(line) < 3: continue file_ = line[0] linno = line[1] msg = (":".join(line[2:])).strip() print_error(file_, linno, msg) errors += 1 print() print("Running pyupgrade...") print() PYUPGRADE_TARGET = "--py39-plus" for files in filesets: cmd = ["pyupgrade", PYUPGRADE_TARGET] + files log = get_err(*cmd) for line in log.splitlines(): REWRITING = "Rewriting" if line.startswith(REWRITING): file_ = line[len(REWRITING) + 1 :] print_error( file_, None, f"Please run pyupgrade {PYUPGRADE_TARGET} on this file" ) errors += 1 sys.exit(errors) if __name__ == "__main__": main()