pre-commit 无法直接调用 cProfile 或 py-spy 等运行时性能工具,因其需执行环境而 pre-commit 阶段代码未运行;可行方案是用 pylint 自定义检查器拦截低效模式(如循环内 len()),或用 pygrep 文本扫描禁止 timeit/cProfile 等调试代码入仓。

pre-commit 怎么调用 Python 性能检查工具?
pre-commit 本身不直接做性能分析,它只负责在 git commit 前运行你指定的命令。真正干活的是第三方工具(比如 py-spy、line_profiler 或静态分析器如 vulture),但它们大多**不支持“自动扫描未提交代码并报告耗时问题”**——因为性能检查需要运行时数据,而 pre-commit 阶段代码还没执行。
所以实际可行路径是:用 pre-commit 触发轻量级、可静态/快速验证的性能守门员,例如:
-
pylint+ 自定义规则检测明显低效写法(如循环内重复调用len()、str.upper()等) -
pycodestyle或flake8检查是否误用print()/logging.debug()在热路径 -
vulture找出无用函数/变量(间接减少冗余执行) -
bandit检测已知高开销且危险的调用(如eval()、exec())
为什么不能直接用 cProfile 或 py-spy?
因为 cProfile 和 py-spy 都需要运行目标代码才能采集性能数据,而 pre-commit 是在 Git 钩子里跑的,没有上下文来决定“运行哪段代码”“传什么参数”“是否带 mock”。强行塞进去只会报错或永远卡住:
ERROR: No module named 'myapp.main' ... or ... py-spy: error: no process found with name 'python'
常见错误现象包括:
-
ModuleNotFoundError:钩子环境没装项目依赖 - 空输出或超时:没指定
--pid或--duration,py-spy默认等 1 秒却找不到 Python 进程 - 误把测试代码当主程序运行,导致钩子阻塞提交流程
如何配置一个真正可用的 pre-commit 性能守门员?
推荐组合:pylint + 自定义 checker,专注拦截典型反模式。例如禁止在 for 循环里反复计算 len(my_list):
repos:
- repo: https://github.com/pycqa/pylint
rev: v3.2.5
hooks:
- id: pylint
args: [--load-plugins=pylint_performance_checker]
对应 pylint_performance_checker.py(放项目根目录或 PYTHONPATH 可达处):
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
from astroid import MANAGER
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
<p>class LenInLoopChecker(BaseChecker):
<strong>implements</strong> = IAstroidChecker
name = "len-in-loop"
msgs = {
"W9901": ("Avoid len() in loop condition, cache it", "len-in-loop", ""),
}</p><pre class="brush:python;toolbar:false;">def visit_for(self, node):
if hasattr(node, "iter") and "len(" in node.iter.as_string():
self.add_message("len-in-loop", node=node)
def register(linter): linter.register_checker(LenInLoopChecker(linter))
关键点:
- 必须用
--load-plugins显式加载,否则 pylint 忽略 - 自定义 checker 要继承
BaseChecker,且register()函数名不能拼错 - 不要试图在 hook 里启动服务或等待响应——pre-commit 要求所有 hook 在几秒内完成
有没有更轻量但有效的替代方案?
有。与其硬上运行时分析,不如用 codespell + pygrep 做文本级守门:
- repo: local
hooks:
- id: detect-timeit-in-prod
name: forbid timeit in production code
entry: grep -n "timeit\|cProfile\|line_profiler" --include="*.py"
language: system
types: [python]
pass_filenames: false
这种方案的优势:
- 零依赖,不装额外包
- 100% 确定性:只要文件里出现关键词就失败
- 规避了“该不该允许性能调试代码进仓库”的权限争议——直接禁止
容易被忽略的是:很多团队把 timeit.timeit(...) 留在开发分支忘了删,结果上线后悄悄拖慢接口。这类文本扫描比任何运行时检查都管用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










