Update old Python code
authorKirill Isakov <bootctl@gmail.com>
Sun, 3 Apr 2022 11:14:55 +0000 (17:14 +0600)
committerKirill Isakov <bootctl@gmail.com>
Sun, 10 Apr 2022 08:42:59 +0000 (14:42 +0600)
- reformat old Python scripts with black
- fix pylint warnings
- fix mypy warnings
- wrap all linters in lint.py
- replace reformat.py with lint.py --fix
- add new linting command: `ninja -C build lint`

.astylerc
lint.py [new file with mode: 0755]
meson.build
reformat.py [deleted file]
version.py

index 8d50422..31b2482 100644 (file)
--- a/.astylerc
+++ b/.astylerc
@@ -2,13 +2,13 @@
 --convert-tabs
 --exclude=subprojects
 --exclude=build
--i
--j
--f
--A2
--U
--p
--xg
--k3
--w
+--ignore-exclude-errors
+--add-braces
+--break-blocks
+--style=attach
+--unpad-paren
+--pad-oper
+--pad-return-type
+--align-pointer=name
+--indent-preproc-define
 --formatted
diff --git a/lint.py b/lint.py
new file mode 100755 (executable)
index 0000000..076b3e7
--- /dev/null
+++ b/lint.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+
+"""Run linters on project code. Add --fix to autofix files with linters that support it."""
+
+import sys
+import subprocess as subp
+from glob import glob
+from os import path, environ, chdir
+
+DRY = "--fix" not in sys.argv or environ.get("CI")
+HEADER = "#" * 24
+
+if DRY:
+    MSG = """
+You're running linters in non-destructive readonly mode.
+Some of them support automated fixes (like reformatting code).
+To apply them, run `lint.py --fix` or `ninja -C build reformat`.
+"""
+    print(MSG, file=sys.stderr)
+
+source_root = path.dirname(path.realpath(__file__))
+source_root = environ.get("MESON_SOURCE_ROOT", source_root)
+chdir(source_root)
+
+# It's best not to use globs that cover everything in the project — if integration
+# tests are run with a large --repeat value, test working directory can reach
+# enormous sizes, and linters either get very slow, or start crashing.
+linters = (
+    [
+        "astyle",
+        "--recursive",
+        "--options=.astylerc",
+        "--dry-run" if DRY else "--formatted",
+        "./*.c",
+        "./*.h",
+    ],
+    ["shfmt", "-d" if DRY else "-w", "-i", "2", "-s", "."],
+    ["black", "--check" if DRY else ".", "."],
+    ["pylint", "."],
+    ["mypy", "--exclude", "build", "."],
+    ["shellcheck", "-x", *glob(".ci/**/*.sh", recursive=True)],
+)
+
+failed: bool = False
+
+for cmd in linters:
+    exe = cmd[0]
+    print(f"{HEADER} Running linter '{exe}' {HEADER}")
+
+    try:
+        res = subp.run(
+            cmd,
+            check=False,
+            stdout=subp.PIPE,
+            encoding="utf-8",
+        )
+        failed = (
+            failed
+            or bool(res.returncode)
+            or (exe == "astyle" and "Formatted  " in res.stdout)
+        )
+        print(res.stdout)
+    except FileNotFoundError as e:
+        print(f"Warning: linter {exe} is missing", file=sys.stderr)
+
+sys.exit(int(failed))
index 488230e..85d1612 100644 (file)
@@ -139,6 +139,12 @@ if os_name == 'linux' and not opt_systemd.disabled()
 endif
 
 run_target('reformat', command: [
-  find_program('python3'),
-  '@SOURCE_ROOT@/reformat.py',
+  python,
+  '@SOURCE_ROOT@/lint.py',
+  '--fix',
+])
+
+run_target('lint', command: [
+  python,
+  '@SOURCE_ROOT@/lint.py',
 ])
diff --git a/reformat.py b/reformat.py
deleted file mode 100755 (executable)
index 256e482..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python3
-
-from os import path, environ
-from sys import stderr
-import subprocess as subp
-import glob
-
-source_root = path.dirname(path.realpath(__file__))
-source_root = environ.get("MESON_SOURCE_ROOT", source_root)
-
-astyle_cmd = [
-    "astyle",
-    "--options=.astylerc",
-    "--recursive",
-    "*.c",
-    "*.h",
-]
-
-shfmt_cmd = [
-    "shfmt",
-    "-i", "2",
-    "-s",
-    "-w",
-]
-
-for path in "**/*.sh", "**/*.test", ".ci/**/*.sh":
-    shfmt_cmd.extend(glob.glob(path, root_dir=source_root, recursive=True))
-
-for cmd in astyle_cmd, shfmt_cmd:
-    try:
-        result = subp.run(cmd, cwd=source_root, check=True)
-    except FileNotFoundError as e:
-        print("Warning: missing", cmd[0], file=stderr)
index b4fb38a..3213e32 100755 (executable)
@@ -1,30 +1,39 @@
 #!/usr/bin/env python3
 
+"""Print current tinc version for using in build scripts.
+
+First try to determine the latest version using git tags. If this fails (because
+the .git directory is missing, git is not installed, or for some other reason),
+fall back to using the VERSION file. If it is not present or could not be read,
+use 'unknown'.
+"""
+
 from os import path, environ
 from sys import argv, stderr
 import subprocess as subp
+import typing as T
 
-prefix = "release-"
-source_root = path.dirname(path.realpath(__file__))
-source_root = environ.get("MESON_SOURCE_ROOT", source_root)
+PREFIX = "release-"
+SOURCE_ROOT = path.dirname(path.realpath(__file__))
+SOURCE_ROOT = environ.get("MESON_SOURCE_ROOT", SOURCE_ROOT)
 
 cmd = [
     "git",
     "--git-dir",
-    path.join(source_root, ".git"),
+    path.join(SOURCE_ROOT, ".git"),
     "describe",
     "--always",
     "--tags",
-    "--match=" + prefix + "*",
+    "--match=" + PREFIX + "*",
 ]
 
 if "short" in argv:
     cmd.append("--abbrev=0")
 
-version = None
+version: T.Optional[str] = None
 
 try:
-    result = subp.run(cmd, stdout=subp.PIPE, encoding="utf-8")
+    result = subp.run(cmd, stdout=subp.PIPE, encoding="utf-8", check=False)
     if not result.returncode:
         version = result.stdout
 except FileNotFoundError:
@@ -32,11 +41,11 @@ except FileNotFoundError:
 
 if not version:
     try:
-        with open(path.join(source_root, "VERSION"), "r") as f:
+        with open(path.join(SOURCE_ROOT, "VERSION"), "r", encoding="utf-8") as f:
             version = f.read().strip()
     except OSError as e:
         print("could not read version from file", e, file=stderr)
-elif version.startswith(prefix):
-    version = version[len(prefix):].strip()
+elif version.startswith(PREFIX):
+    version = version[len(PREFIX) :].strip()
 
 print(version if version else "unknown", end="")