如何让Cron仅在Python包执行路径相关代码变更时触发执行
解决Python包夜间Cron任务仅在执行路径变更时运行的方案
一、先明确__main__入口的实际执行依赖
要精准判断变更是否影响执行,首先得搞清楚入口运行时到底用到了哪些文件和函数——动态追踪是最靠谱的方式,能避免静态分析的误判:
- 写个简单的追踪脚本,运行入口并记录所有被执行到的自有包文件/函数:
替换import sys executed_files = set() executed_funcs = set() def trace_execution(frame, event, arg): if event == 'call': file_path = frame.f_code.co_filename # 只追踪自己包内的代码,过滤标准库和第三方依赖 if 'your_package' in file_path and file_path.endswith('.py'): executed_files.add(file_path) func_name = frame.f_code.co_name executed_funcs.add(f"{file_path}:{func_name}") return trace_execution sys.settrace(trace_execution) # 运行你的包入口(根据实际入口逻辑调整) from your_package.__main__ import main main() # 保存结果到文件,供后续对比用 with open('executed_files.txt', 'w') as f: f.write('\n'.join(executed_files)) with open('executed_funcs.txt', 'w') as f: f.write('\n'.join(executed_funcs))your_package为你的包名,运行一次这个脚本就能拿到执行路径的基准数据。
二、用Git对比两次提交的变更
Cron任务每次运行时,先读取上一次执行时的Git哈希(存在last_commit.txt里),再对比当前HEAD的哈希,提取变更内容:
- 用Shell脚本实现对比逻辑:
# 读取上次运行的提交哈希,首次运行时用初始提交哈希 LAST_COMMIT=$(cat last_commit.txt 2>/dev/null || echo "初始提交哈希") CURRENT_COMMIT=$(git rev-parse HEAD) # 获取两次提交之间变更的自有包Python文件 CHANGED_FILES=$(git diff --name-only $LAST_COMMIT $CURRENT_COMMIT | grep "your_package.*\.py$") # 如果需要细粒度到函数,获取变更涉及的函数 CHANGED_FUNCS=$(git diff -p $LAST_COMMIT $CURRENT_COMMIT | awk ' /^diff --git/ {file=$3} /^def / {print file ":" substr($2, 1, index($2, "(")-1)} ' | grep "your_package")
三、判断是否需要执行包
根据对比结果,判断变更是否落在执行路径内:
文件级判断(简单场景)
如果变更的文件在executed_files.txt里,就执行包:
NEED_RUN=0 # 遍历变更文件,检查是否在执行路径中 for file in $CHANGED_FILES; do ABS_FILE=$(realpath "$file") if grep -q "^$ABS_FILE$" executed_files.txt; then NEED_RUN=1 break fi done
函数级判断(精准场景)
如果要排除“文件变更但仅修改未调用函数”的情况,就检查变更函数是否在executed_funcs.txt里:
NEED_RUN=0 for func in $CHANGED_FUNCS; do if grep -q "^$func$" executed_funcs.txt; then NEED_RUN=1 break fi done # 额外处理新增文件的情况(新增文件如果被入口调用,也需要执行) for file in $CHANGED_FILES; do if ! grep -q "^$(realpath "$file")$" executed_files.txt; then # 检查新增文件是否被入口依赖(可以用静态分析或临时运行验证) python -c "import your_package.__main__; import $(echo $file | sed 's/\.py$//' | tr '/' '.')" && NEED_RUN=1 break fi done
四、执行逻辑与Cron整合
最后在脚本里加入执行逻辑,并配置Cron:
if [ $NEED_RUN -eq 1 ]; then echo "Detected changes in execution path, running package..." python -m your_package.__main__ # 更新上次运行的提交哈希 echo $CURRENT_COMMIT > last_commit.txt else echo "No relevant changes, skipping run." fi
把脚本保存为run_on_change.sh,然后添加Cron任务:
crontab -e # 每天凌晨2点执行,输出日志到指定文件 0 2 * * * /bin/bash /path/to/run_on_change.sh >> /path/to/cron_log.txt 2>&1
内容的提问来源于stack exchange,提问作者Harry Stuart
相关产品推荐
相关产品推荐

