fix: auto-fix code issues (cron)
- 修复重复导入/字段 - 修复异常处理 - 修复PEP8格式问题 - 添加类型注解 - 修复缺失的urllib.parse导入
This commit is contained in:
169
code_reviewer.py
169
code_reviewer.py
@@ -9,7 +9,14 @@ from pathlib import Path
|
||||
|
||||
|
||||
class CodeIssue:
|
||||
def __init__(self, file_path: str, line_no: int, issue_type: str, message: str, severity: str = "info"):
|
||||
def __init__(
|
||||
self,
|
||||
file_path: str,
|
||||
line_no: int,
|
||||
issue_type: str,
|
||||
message: str,
|
||||
severity: str = "info",
|
||||
):
|
||||
self.file_path = file_path
|
||||
self.line_no = line_no
|
||||
self.issue_type = issue_type
|
||||
@@ -74,17 +81,29 @@ class CodeReviewer:
|
||||
# 9. 检查敏感信息
|
||||
self._check_sensitive_info(content, lines, rel_path)
|
||||
|
||||
def _check_bare_exceptions(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_bare_exceptions(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查裸异常捕获"""
|
||||
for i, line in enumerate(lines, 1):
|
||||
if re.search(r"except\s*:\s*$", line.strip()) or re.search(r"except\s+Exception\s*:\s*$", line.strip()):
|
||||
if re.search(r"except\s*:\s*$", line.strip()) or re.search(
|
||||
r"except\s+Exception\s*:\s*$", line.strip()
|
||||
):
|
||||
# 跳过有注释说明的情况
|
||||
if "# noqa" in line or "# intentional" in line.lower():
|
||||
continue
|
||||
issue = CodeIssue(file_path, i, "bare_exception", "裸异常捕获,应该使用具体异常类型", "warning")
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
i,
|
||||
"bare_exception",
|
||||
"裸异常捕获,应该使用具体异常类型",
|
||||
"warning",
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
def _check_duplicate_imports(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_duplicate_imports(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查重复导入"""
|
||||
imports = {}
|
||||
for i, line in enumerate(lines, 1):
|
||||
@@ -96,30 +115,50 @@ class CodeReviewer:
|
||||
name = name.strip().split()[0] # 处理 'as' 别名
|
||||
key = f"{module}.{name}" if module else name
|
||||
if key in imports:
|
||||
issue = CodeIssue(file_path, i, "duplicate_import", f"重复导入: {key}", "warning")
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
i,
|
||||
"duplicate_import",
|
||||
f"重复导入: {key}",
|
||||
"warning",
|
||||
)
|
||||
self.issues.append(issue)
|
||||
imports[key] = i
|
||||
|
||||
def _check_pep8_issues(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_pep8_issues(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查 PEP8 问题"""
|
||||
for i, line in enumerate(lines, 1):
|
||||
# 行长度超过 120
|
||||
if len(line) > 120:
|
||||
issue = CodeIssue(file_path, i, "line_too_long", f"行长度 {len(line)} 超过 120 字符", "info")
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
i,
|
||||
"line_too_long",
|
||||
f"行长度 {len(line)} 超过 120 字符",
|
||||
"info",
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
# 行尾空格
|
||||
if line.rstrip() != line:
|
||||
issue = CodeIssue(file_path, i, "trailing_whitespace", "行尾有空格", "info")
|
||||
issue = CodeIssue(
|
||||
file_path, i, "trailing_whitespace", "行尾有空格", "info"
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
# 多余的空行
|
||||
if i > 1 and line.strip() == "" and lines[i - 2].strip() == "":
|
||||
if i < len(lines) and lines[i].strip() == "":
|
||||
issue = CodeIssue(file_path, i, "extra_blank_line", "多余的空行", "info")
|
||||
issue = CodeIssue(
|
||||
file_path, i, "extra_blank_line", "多余的空行", "info"
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
def _check_unused_imports(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_unused_imports(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查未使用的导入"""
|
||||
try:
|
||||
tree = ast.parse(content)
|
||||
@@ -147,10 +186,14 @@ class CodeReviewer:
|
||||
# 排除一些常见例外
|
||||
if name in ["annotations", "TYPE_CHECKING"]:
|
||||
continue
|
||||
issue = CodeIssue(file_path, lineno, "unused_import", f"未使用的导入: {name}", "info")
|
||||
issue = CodeIssue(
|
||||
file_path, lineno, "unused_import", f"未使用的导入: {name}", "info"
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
def _check_string_formatting(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_string_formatting(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查混合字符串格式化"""
|
||||
has_fstring = False
|
||||
has_percent = False
|
||||
@@ -165,10 +208,18 @@ class CodeReviewer:
|
||||
has_format = True
|
||||
|
||||
if has_fstring and (has_percent or has_format):
|
||||
issue = CodeIssue(file_path, 0, "mixed_formatting", "文件混合使用多种字符串格式化方式,建议统一为 f-string", "info")
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
0,
|
||||
"mixed_formatting",
|
||||
"文件混合使用多种字符串格式化方式,建议统一为 f-string",
|
||||
"info",
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
def _check_magic_numbers(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_magic_numbers(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查魔法数字"""
|
||||
# 常见的魔法数字模式
|
||||
magic_patterns = [
|
||||
@@ -190,36 +241,88 @@ class CodeReviewer:
|
||||
match = re.search(r"(\d{3,})", code_part)
|
||||
if match:
|
||||
num = int(match.group(1))
|
||||
if num in [200, 404, 500, 401, 403, 429, 1000, 1024, 2048, 4096, 8080, 3000, 8000]:
|
||||
if num in [
|
||||
200,
|
||||
404,
|
||||
500,
|
||||
401,
|
||||
403,
|
||||
429,
|
||||
1000,
|
||||
1024,
|
||||
2048,
|
||||
4096,
|
||||
8080,
|
||||
3000,
|
||||
8000,
|
||||
]:
|
||||
continue
|
||||
issue = CodeIssue(file_path, i, "magic_number", f"{msg}: {num}", "info")
|
||||
issue = CodeIssue(
|
||||
file_path, i, "magic_number", f"{msg}: {num}", "info"
|
||||
)
|
||||
self.issues.append(issue)
|
||||
|
||||
def _check_sql_injection(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_sql_injection(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查 SQL 注入风险"""
|
||||
for i, line in enumerate(lines, 1):
|
||||
# 检查字符串拼接的 SQL
|
||||
if re.search(r'execute\s*\(\s*["\'].*%s', line) or re.search(r'execute\s*\(\s*f["\']', line):
|
||||
if re.search(r'execute\s*\(\s*["\'].*%s', line) or re.search(
|
||||
r'execute\s*\(\s*f["\']', line
|
||||
):
|
||||
if "?" not in line and "%s" in line:
|
||||
issue = CodeIssue(file_path, i, "sql_injection_risk", "可能的 SQL 注入风险 - 需要人工确认", "error")
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
i,
|
||||
"sql_injection_risk",
|
||||
"可能的 SQL 注入风险 - 需要人工确认",
|
||||
"error",
|
||||
)
|
||||
self.manual_review_issues.append(issue)
|
||||
|
||||
def _check_cors_config(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_cors_config(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查 CORS 配置"""
|
||||
for i, line in enumerate(lines, 1):
|
||||
if "allow_origins" in line and '["*"]' in line:
|
||||
issue = CodeIssue(file_path, i, "cors_wildcard", "CORS 允许所有来源 - 需要人工确认", "warning")
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
i,
|
||||
"cors_wildcard",
|
||||
"CORS 允许所有来源 - 需要人工确认",
|
||||
"warning",
|
||||
)
|
||||
self.manual_review_issues.append(issue)
|
||||
|
||||
def _check_sensitive_info(self, content: str, lines: list[str], file_path: str) -> None:
|
||||
def _check_sensitive_info(
|
||||
self, content: str, lines: list[str], file_path: str
|
||||
) -> None:
|
||||
"""检查敏感信息"""
|
||||
for i, line in enumerate(lines, 1):
|
||||
# 检查硬编码密钥
|
||||
if re.search(r'(password|secret|key|token)\s*=\s*["\'][^"\']+["\']', line, re.IGNORECASE):
|
||||
if "os.getenv" not in line and "environ" not in line and "getenv" not in line:
|
||||
if re.search(
|
||||
r'(password|secret|key|token)\s*=\s*["\'][^"\']+["\']',
|
||||
line,
|
||||
re.IGNORECASE,
|
||||
):
|
||||
if (
|
||||
"os.getenv" not in line
|
||||
and "environ" not in line
|
||||
and "getenv" not in line
|
||||
):
|
||||
# 排除一些常见假阳性
|
||||
if not re.search(r'["\']\*+["\']', line) and not re.search(r'["\']<[^"\']*>["\']', line):
|
||||
issue = CodeIssue(file_path, i, "hardcoded_secret", "可能的硬编码敏感信息 - 需要人工确认", "error")
|
||||
if not re.search(r'["\']\*+["\']', line) and not re.search(
|
||||
r'["\']<[^"\']*>["\']', line
|
||||
):
|
||||
issue = CodeIssue(
|
||||
file_path,
|
||||
i,
|
||||
"hardcoded_secret",
|
||||
"可能的硬编码敏感信息 - 需要人工确认",
|
||||
"error",
|
||||
)
|
||||
self.manual_review_issues.append(issue)
|
||||
|
||||
def auto_fix(self) -> None:
|
||||
@@ -289,7 +392,9 @@ class CodeReviewer:
|
||||
if self.fixed_issues:
|
||||
report.append(f"共修复 {len(self.fixed_issues)} 个问题:\n")
|
||||
for issue in self.fixed_issues:
|
||||
report.append(f"- ✅ {issue.file_path}:{issue.line_no} - {issue.issue_type}: {issue.message}")
|
||||
report.append(
|
||||
f"- ✅ {issue.file_path}:{issue.line_no} - {issue.issue_type}: {issue.message}"
|
||||
)
|
||||
else:
|
||||
report.append("无")
|
||||
|
||||
@@ -297,7 +402,9 @@ class CodeReviewer:
|
||||
if self.manual_review_issues:
|
||||
report.append(f"共发现 {len(self.manual_review_issues)} 个问题:\n")
|
||||
for issue in self.manual_review_issues:
|
||||
report.append(f"- ⚠️ {issue.file_path}:{issue.line_no} - {issue.issue_type}: {issue.message}")
|
||||
report.append(
|
||||
f"- ⚠️ {issue.file_path}:{issue.line_no} - {issue.issue_type}: {issue.message}"
|
||||
)
|
||||
else:
|
||||
report.append("无")
|
||||
|
||||
@@ -305,7 +412,9 @@ class CodeReviewer:
|
||||
if self.issues:
|
||||
report.append(f"共发现 {len(self.issues)} 个问题:\n")
|
||||
for issue in self.issues:
|
||||
report.append(f"- 📝 {issue.file_path}:{issue.line_no} - {issue.issue_type}: {issue.message}")
|
||||
report.append(
|
||||
f"- 📝 {issue.file_path}:{issue.line_no} - {issue.issue_type}: {issue.message}"
|
||||
)
|
||||
else:
|
||||
report.append("无")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user