diff --git a/.github/workflows/lint_mypy.yml b/.github/workflows/lint_mypy.yml index f36c9e901..5ac64a062 100644 --- a/.github/workflows/lint_mypy.yml +++ b/.github/workflows/lint_mypy.yml @@ -23,6 +23,15 @@ jobs: - run: python -m pip install pylint==2.4.4 - run: pylint mesonbuild + custom_lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.x' + - run: python ./run_custom_lint.py + mypy: runs-on: ubuntu-latest steps: diff --git a/run_custom_lint.py b/run_custom_lint.py new file mode 100755 index 000000000..89de9506e --- /dev/null +++ b/run_custom_lint.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import typing as T + +root = Path(__file__).absolute().parent +mesonbuild = root / 'mesonbuild' + +whitelist = ['mesonbuild/', 'run_', 'ci/', 'tools/', 'docs/'] + +def check_missing_encoding(lines: T.List[str], path: str) -> int: + errors = 0 + functions = ['read_text', 'write_text', 'open'] + for num, line in enumerate(lines): + for func in functions: + l = line + + # Skip ignored lines + if '[ignore encoding]' in l: + continue + + # Do we have a match? + loc = l.find(func + '(') + if loc < 0: + continue + if loc > 0 and ord(l[loc-1].lower()) in [*range(ord('a'), ord('z')), *range(ord('0'), ord('9')), '_']: + continue + loc += len(func) + 1 + # Some preprocessign to make parsing easier + l = l[loc:] + l = l.replace(' ', '') + l = l.replace('\t', '') + l = l.replace('\n', '') + l = l.replace('\'', '"') + + # Parameter begin + args = '' + b_open = 1 + while l: + c = l[0] + l = l[1:] + if c == ')': + b_open -= 1 + if b_open == 0: + break + elif b_open == 1: + args += c + if c == '(': + b_open += 1 + + binary_modes = ['rb', 'br', 'r+b', 'wb', 'bw', 'ab', 'ba'] + is_binary = any([f'"{x}"' in args for x in binary_modes]) + if 'encoding=' not in args and not (func == 'open' and is_binary): + location = f'\x1b[33;1m[\x1b[0;1m{path}:{num+1}\x1b[33m]\x1b[0m' + #print(f'{location:<64}: \x1b[31;1mERROR:\x1b[0m Missing `encoding=` parameter in "{line.strip()}"') + print(f'{location:<72}: \x1b[31;1mERROR:\x1b[0m Missing `encoding=` parameter in `{func}` call') + errors += 1 + return errors + +def main() -> int: + print('Scanning mesonbuild...') + errors = 0 + for i in sorted(root.glob('**/*.py')): + raw = i.read_text(encoding='utf-8') + lines = raw.splitlines() + filename = i.relative_to(root).as_posix() + + if not any([filename.startswith(x) for x in whitelist]): + continue + + errors += check_missing_encoding(lines, filename) + print(f'Found {errors} errors while scanning mesonbuild') + return 0 if errors == 0 else 1 + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/run_mypy.py b/run_mypy.py index 58305d4a8..511db33ed 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -41,7 +41,7 @@ modules = [ 'mesonbuild/optinterpreter.py', 'mesonbuild/programs.py', - 'run_encoding_tests.py', + 'run_custom_lint.py', 'run_mypy.py', 'run_single_test.py', 'tools'