4.1 什么是自动修复
对于很多问题,机器能明确写出"正确答案"——比如多余的 import、"foo" % bar 应该改成 f-string、List[int] 应写成 list[int]。Ruff 把这类规则标记为 fixable,加上 --fix 就会直接改文件。
$ ruff check --fix .
# Found 127 errors (103 fixed, 24 remaining).
输出会告诉你:发现了 127 个问题,自动修掉了 103 个,剩余 24 个需要人工处理。
4.2 safe 修复 vs unsafe 修复
Ruff 把自动修复分成两级:
- Safe fix(安全修复)
- 几乎保证语义不变——删未使用的 import、排序 import、
== None→is None。默认--fix只跑这一级。 - Unsafe fix(不安全修复)
- 机器"有很高把握"是正确的,但在极端情况下可能改变语义或破坏代码。例如删除未使用的变量(该变量的 构造 可能有副作用)、把
typing.List替换为list(若某些上游代码依赖 typing 对象的 identity 会出问题)。需要显式加--unsafe-fixes才会执行。
典型 unsafe 修复的反例
规则 F841(未使用变量)的修复是"删除赋值":
result = compute_and_log() # compute_and_log() 里写了日志!
直接删会让副作用(日志)一起消失,所以它被标记为 unsafe。
4.3 预览修改:--diff 与 --show-fixes
不想盲改文件?先看 diff:
$ ruff check --fix --diff .
# 打印将被改动的 diff,但不写入文件
$ ruff check --fix --show-fixes .
# 改完后,每条被修的问题都单独列出"改了什么"
--diff 输出是标准 unified diff,可直接 | less 或 > fix.patch 存盘。
4.4 混合使用 safe + unsafe
工程实践上推荐:
- 第一步:
ruff check --fix .(只跑 safe) - 第二步:
ruff check --fix --unsafe-fixes --diff .(预览 unsafe) - 第三步:逐处确认后再执行
ruff check --fix --unsafe-fixes . - 第四步:跑单元测试确认没破坏行为
4.5 限定规则再修复
一次性把 800 条规则的修复都应用,diff 会巨大、code review 难看。更务实的做法是"分族分批"修:
# 第一批:只修 import 问题
$ ruff check --select I,F401 --fix .
# 第二批:pyupgrade 把旧语法升级
$ ruff check --select UP --fix .
# 第三批:bugbear 反模式
$ ruff check --select B --fix --unsafe-fixes .
每一批都能形成一个独立的 commit,方便 review 和回滚。
4.6 禁止某条规则被 --fix
想开启某规则的检查,但不允许 Ruff 自动改它?用 unfixable:
[tool.ruff.lint]
select = ["ALL"]
unfixable = [
"F841", # 未使用变量——让人工判断副作用
"B008", # 函数默认值中调用
]
4.7 实战:将老项目一键升级语法
假设你接手了一个 Python 3.8 时代的项目,现在环境升级到 3.12,想让全仓用上现代语法:
# 1. 更新 target-version
$ echo '[tool.ruff]\ntarget-version = "py312"' >> pyproject.toml
# 2. 先预览
$ ruff check --select UP --fix --diff . | head -50
# 3. 执行 safe 修复
$ ruff check --select UP --fix .
# 4. 看 unsafe 部分
$ ruff check --select UP --fix --unsafe-fixes --diff .
# 5. 跑测试确认
$ pytest
这一套下来,通常能自动完成 90%+ 的语法现代化——相比手写迁移节省几天时间。
4.8 修复无限循环?—— --fix-only 和迭代
某些情况下一条修复会触发另一条新的错误(例如删 import 后出现 E501 变短的行),Ruff 会自动做多轮修复直到稳定。若只想做一轮:
$ ruff check --fix --no-fix-loop .
--fix-only 则只跑修复不打印剩余错误(便于脚本里静默运行):
$ ruff check --fix-only . # 静默修复
4.9 与 git 配合的"安全修复"工作流
# 确保工作区干净
$ git status --porcelain | grep . && echo "存在未提交变更,终止" && exit 1
# 每族独立 commit
$ ruff check --select I --fix . && git commit -am "style(ruff): sort imports (I)"
$ ruff check --select F --fix . && git commit -am "fix(ruff): remove unused (F)"
$ ruff check --select UP --fix . && git commit -am "refactor(ruff): modern syntax (UP)"
任何一步出错都可以 git reset --hard HEAD~1 回退单族。
4.10 小结
- Ruff 把可修复规则分为 safe / unsafe,
--fix默认只跑 safe。 - 上生产前用
--diff/--show-fixes预览。 - 大仓迁移按族分批修,每族独立 commit。
- 某些规则可设
unfixable,只检查不动刀。