Files

56 lines
2.4 KiB
Python

"""静态检查(无第三方 linter 时的替代):未使用导入、裸 except、过长函数。
用法:``python dev/lint_scan.py``
`from __future__ import annotations` 会被误判(它确实没有显式引用),已忽略。
"""
import ast
import os
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TARGETS = [os.path.join(ROOT, "recorder"), ROOT]
IGNORE = {"annotations"}
MAX_FUNC_LINES = 130
issues: list[str] = []
for target in TARGETS:
files = ([os.path.join(target, f) for f in sorted(os.listdir(target))
if f.endswith(".py")]
if os.path.isdir(target) else [target])
for path in files:
rel = os.path.relpath(path, ROOT)
with open(path, encoding="utf-8") as fh:
src = fh.read()
lines = src.splitlines()
tree = ast.parse(src, path)
def has_noqa(lineno: int) -> bool:
return 0 < lineno <= len(lines) and "noqa" in lines[lineno - 1]
imported: dict[str, int] = {}
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for a in node.names:
imported[(a.asname or a.name).split(".")[0]] = node.lineno
elif isinstance(node, ast.ImportFrom):
for a in node.names:
if a.name != "*":
imported[a.asname or a.name] = node.lineno
used = {n.id for n in ast.walk(tree) if isinstance(n, ast.Name)}
for name, line in sorted(imported.items(), key=lambda kv: kv[1]):
if name in used or name in IGNORE or has_noqa(line):
continue
if f'"{name}"' in src or f"'{name}'" in src:
continue
issues.append(f"{rel}:{line} 未使用的导入 {name}")
for node in ast.walk(tree):
if isinstance(node, ast.ExceptHandler) and node.type is None \
and not has_noqa(node.lineno):
issues.append(f"{rel}:{node.lineno} 裸 except")
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
length = (node.end_lineno or node.lineno) - node.lineno
if length > MAX_FUNC_LINES and not has_noqa(node.lineno):
issues.append(f"{rel}:{node.lineno} 函数 {node.name} 过长({length} 行)")
print("\n".join(issues) if issues else "未发现明显问题")
print(f"\n共 {len(issues)} 条提示")