6.1 四级忽略粒度
| 粒度 | 写法 | 作用域 | 推荐场景 |
|---|---|---|---|
| 行级 | # noqa: E501 | 当前行 | 极个别无可奈何的情况 |
| 文件级 | # ruff: noqa: E501 | 整个文件 | 生成代码、marshmallow schema 等 |
| 路径级 | per-file-ignores | 匹配路径的所有文件 | tests/、__init__.py、migrations/ |
| 全局 | ignore = [...] | 整个项目 | 确实不需要的规则 |
从上到下作用域越来越大,原则是 能用粒度小的就不用大的——避免"为了跳过一个 bug 把整族关掉"。
6.2 行级 noqa
import os # noqa: F401 —— 跳过 F401 但其他规则仍生效
x = very_long_expression_cannot_be_broken_up(...) # noqa: E501
# 跳过多条
eval(user_input) # noqa: S307, S102
# 裸 # noqa(不带规则号)会忽略该行所有规则 —— 不推荐
dangerous() # noqa
裸 noqa 是反模式
不带规则号的 # noqa 把这行所有规则都屏蔽了,包括未来新增的规则。Ruff 会用 PGH004(blanket-noqa)专门报告这种写法——总是写 noqa: XXX。
6.3 文件级 noqa
# ruff: noqa: F401, F403
# 这是整文件的指令,放在文件顶部(通常前 5 行内)
from .models import * # 这个文件的 F401/F403 全部放过
注意前缀是 # ruff: noqa(有冒号),与行级 # noqa 区分。
6.4 per-file-ignores:按路径放宽
比起在 20 个 __init__.py 都写 # noqa: F401,更干净的是 pyproject.toml 里一条规则:
[tool.ruff.lint.per-file-ignores]
# __init__.py 常用 from .foo import X 重新导出,允许未使用 import
"__init__.py" = ["F401", "F403"]
# 测试文件放宽 assert、魔法常量、注解
"tests/**/*.py" = ["S101", "ANN", "D", "PLR2004"]
# Django migrations 机器生成,不管风格
"**/migrations/*.py" = ["E501", "N806"]
# Jupyter notebook 允许 print 与未用变量
"notebooks/**/*.ipynb" = ["T20", "F841"]
6.5 exclude:完全跳过文件
有些目录我们希望 Ruff 根本不去读——自动生成的代码、三方 vendor、老版本备份:
[tool.ruff]
# 覆盖默认的 exclude(.git/.venv 等已默认排除)
exclude = [".git", ".venv", "dist", "build"]
# 追加不覆盖默认
extend-exclude = [
"generated/",
"vendor/",
"**/_pb2.py", # protobuf 生成文件
"**/*_pb2_grpc.py",
]
exclude vs per-file-ignores
前者是"这些文件根本不扫",后者是"扫,但对这些文件放宽某些规则"。生成代码通常用 exclude,测试文件用 per-file-ignores(仍希望发现真 bug)。
6.6 检测无效的 noqa
时间一久,项目里会累积一堆 # noqa: XXX,但原来触发的问题已经修掉了——留着就是噪音。Ruff 有专门规则 RUF100 报告"无效 noqa":
[tool.ruff.lint]
extend-select = ["RUF100"]
$ ruff check .
demo.py:12:20: RUF100 [*] Unused `noqa` directive (unused: `F401`)
$ ruff check --fix . # 自动清理
6.7 强制 noqa 必须带理由(可选)
团队约定每条 # noqa 后面要写原因注释。可通过 --add-noqa 配合 git review 达成:
import typing # noqa: F401 — re-export for public API
eval(trusted_expr) # noqa: S307 — expr 来自受控白名单,见 #1234
这不是 Ruff 内置机制,但团队可以在 code review / 预提交脚本里 grep 未带注释的 noqa 来执行这一约定。
6.8 Ruff 如何反向"加"noqa?
大仓一次性开启新规则时,会冒出成千上万条报错。与其一条条修,不如先全部标记 noqa、立即通过 CI,之后再慢慢清理:
$ ruff check --add-noqa .
# 自动在每个违规行添加对应的 # noqa: CODE 注释
# 先 CI 过了,再由后续 PR 逐个真修复
这是"先划红线、再清历史债"的常见工程策略。
6.9 小结
- 四级粒度:行级 < 文件级 < 路径级 < 全局——能用小的就不用大的。
- 永远带规则号写
# noqa: XXX,裸 noqa 反而会被 PGH004 报告。 - 启用
RUF100让 Ruff 自己清理已失效的 noqa。 - 大迁移用
ruff check --add-noqa一键静默,后续慢慢清。