最近把一个老项目迁移到Ruff做代码规范治理,配置规则的时候想看看命名相关规则到底有哪些。我的第一反应是敲出这样一条命令:ruff list --select N。结果命令是跑通了,但我盯着终端愣了几秒——这条命令的语法到底该怎么解析?N是什么玩意?--select和list结合起来为什么就能把规则筛出来了?查官方文档,措辞很克制,没有把匹配逻辑讲透;翻社区帖子,也大多是零散的使用片段。这篇文章就专门来拆解这条命令:语法结构、参数取值逻辑、输出格式、shell转义陷阱,以及我实际配置项目规则时怎么用它走通全流程。
如果你正在给Python项目配置Ruff、从flake8迁移规则,或者看别人配置里写了select = ["N", "E"]却不知道这些字母怎么来的,这篇应该能帮你把链路彻底打通。
1. 先搞清楚“list”和“select”在Ruff里各管什么
1.1 我的第一反应:这到底是SQL的SELECT,还是Shell的select?
第一次看到ruff list --select N,我很自然地想起了SQL里的SELECT语句、Python的select模块、甚至MySQL的select ... from ...。Ruff明明是个代码检查工具,怎么还带个select参数?
其实这里的--select跟SQL半毛钱关系都没有。Ruff把内置的所有lint规则组织成一个规则库,每条规则都有一个唯一代码,比如E501、F401、N801。--select在Ruff里的语义是“从规则库中筛选出你关心的规则子集”,本质上就是一个过滤器。它可以用在ruff check里(真正执行检查时只跑这些规则),也可以用在这里讨论的ruff list里(只罗列出这些规则,不做任何代码分析)。
所以第一步先在心里把概念掰正:这里的select是“规则选择器”,不是数据查询。
1.2 ruff list的定位:一份可筛选的规则清单
ruff list是Ruff提供的一个子命令,作用很单纯:把当前Ruff版本支持的所有lint规则列出来。默认情况下,不带任何参数直接运行ruff list,会输出一张长长的表,里面包含规则代码、规则名、规则说明、所属分类等信息,内容非常多,多到人眼根本看不过来。
这时候--select就派上用场了。它可以在列表阶段就把范围缩小。比如我只关心“命名规范”相关的规则,那就可以用ruff list --select N,让输出里只剩下N开头的那一批。为什么不直接看文档?因为Ruff的规则数量随着版本在涨,新版本会加preview规则,文档不一定跟得上,而且本地命令输出的是当前安装版本的真实规则集合,比查网页靠谱。
1.3 N这个字母不是随口写的:规则代码的编码逻辑
要理解--select N,必须理解Ruff规则代码的编码体系。Ruff把不同来源的规则用前缀字母区分,每个前缀代表一类规则来源或一个插件:
| 前缀 | 规则来源 | 大致方向 |
|---|---|---|
| E | pycodestyle errors | 代码风格错误,如行过长 |
| W | pycodestyle warnings | 代码风格警告,如多余空行 |
| F | Pyflakes | 未使用导入、未定义变量等 |
| N | flake8-naming | 命名规范(PEP 8命名) |
| B | flake8-bugbear | 易错点和坏味道 |
| C | mccabe | 圈复杂度 |
| I | isort | 导入排序 |
| UP | pyupgrade | 现代化语法升级 |
| SIM | flake8-simplify | 简化代码 |
| RUF | Ruff自定义规则 | Ruff自己实现的特殊规则 |
N代表的就是flake8-naming这套命名规范规则,比如类名应该用CapWords、函数名应该用snake_case等等。所以你写--select N,潜台词就是“我只想筛选出命名规范这一大类”。
每个规则代码的结构是“前缀字母+数字编号”,比如N801、N802。--select在设计上支持前缀匹配:你给一个N,它就把所有以N开头的规则全部挑出来;你给一个N801,它就只挑这一条。理解了这一点,后面的各种组合语法就顺理成章了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 语法拆解:--select的取值规则是全文核心
2.1 前缀匹配:一个N会框住N开头的全部规则
--select N并不是精确匹配一个叫“N”的规则,而是所有以“N”开头的规则的集合。这是Ruff规则选择器的核心机制。
你可以把规则代码想象成文件路径的前缀目录:N就像顶层目录,N801是里面的一个文件。--select N等于把整个目录都选进来,--select N801只选了目录里的某个文件。这种设计的好处是,你不需要记住每条规则的具体编号,只需知道大类前缀就能快速圈定范围。
举几个具体的例子:
bash复制# 筛选所有命名规范规则
ruff list --select N
# 筛选所有风格错误和警告
ruff list --select E,W
# 只筛选一条规则
ruff list --select N801
在实际的ruff check里,同样的规则选择器语义也在生效。ruff check --select N src/就是用所有命名规范规则去检查src/目录。所以你在list阶段验证过的选择器,可以直接搬到check里用,语法完全一致。
2.2 多选、通配符和精确代码:怎么组合
--select不止能接单个值,它接受逗号分隔的多个规则选择器。这是非常实用的特性,因为大多数项目的规则配置都不可能只有一个大类。
bash复制# 同时筛选命名、错误、未使用导入三大类
ruff list --select N,E,F
# 混合精确代码和前綴:先看所有N规则,再看E501这一条
ruff list --select N,E501
除了逗号组合,--select还支持glob通配符。比如--select "N*"会选择所有N开头的规则,--select "UP0*"会选择所有UP0开头的规则。通配符的好处是能更精细地控制范围:同样是N,N8*可能只会匹配N80x到N81x之间的命名规则,这比直接写N粒度更细。
不过这里有个关键坑:通配符在shell里会被展开。如果你在bash或zsh里直接写ruff list --select N*而不加引号,N*会被shell当成文件名匹配模式。如果当前目录下恰好有个文件叫N801.py,shell会把命令变成ruff list --select N801.py,Ruff会直接报unknown rule selector,让人摸不着头脑。
我的习惯是:所有包含通配符的规则选择器一律加双引号,避免shell干扰。保险起见,纯字母的N、E、F也可以加,加了不亏。
2.3 引号与shell:一个真实翻车现场
这个坑我确实踩过。当时在项目目录下执行:
bash复制ruff list --select UP*
结果Ruff报错error: invalid value 'xxx.py' for '--select <RULE>': unknown rule selector: xxx.py。我愣了两秒才反应过来,目录下有几个UP*.py测试文件,shell把UP*展开成了一堆文件名,全被塞给了--select参数。这不是Ruff的bug,是shell通配符展开的正常行为。
解决方案很简单:
bash复制# 加双引号,让shell把它当成一个参数传给Ruff
ruff list --select "UP*"
如果你在脚本或CI里使用这条命令,建议养成一个习惯:规则选择器统一加双引号。因为脚本运行目录是不确定的,一旦某个目录下有匹配的文件,脚本就会莫名其妙挂掉,排查起来很费劲。
2.4 输出格式:默认表格、json和filter
ruff list的默认输出是文本表格,字符画一样排列,人眼看着还算舒服。但如果你想在脚本里处理规则清单,或者把规则列表导出成文档,文本表格就不好用了。
ruff list --output-format json可以输出JSON格式。每条规则会变成一个对象,包含代码、名称、说明、标签、是否preview等字段。配合jq就能做很多事:
bash复制# 只取所有规则代码
ruff list --select N --output-format json | jq -r '.[].code'
# 统计N类规则数量
ruff list --select N --output-format json | jq 'length'
另外,ruff list在新版本里支持--filter参数,用来按规则状态过滤,常见取值包括stable(稳定规则)、preview(预览规则)、deprecated(废弃规则)、all(全部规则)。比如想看看正在预览的命名规则有哪些,可以组合:
bash复制ruff list --select N --filter preview
不同Ruff版本对--filter的参数支持有差异,最稳妥的办法是用ruff list --help确认当前版本的可用参数。这个习惯放到所有命令行工具上都成立,别嫌麻烦。
3. 实操演示:用N类规则完整走一遍
3.1 先看一眼N类规则的全貌
理论知识讲完,我实际操作一遍给大家看。假设当前环境里Ruff版本足够新,运行:
bash复制ruff list --select N
输出会是一张包含多列的表格,列字段大致有规则代码、规则名、说明、标签等。以flake8-naming迁移过来的规则为例,你会看到类似这些内容:
N801:类名应使用CapWords风格N802:函数名应使用snake_case风格N803:参数名应使用snake_case风格N806:变量名应使用snake_case风格N807:dunder方法名应使用前后各两个下划线
注意,不同Ruff版本列出的N类规则条目会有差异,因为Ruff团队会持续同步上游flake8-naming的更新,也可能加入自己的扩展规则。所以不要拿网上别人贴的清单当永久真理,要以本地命令输出为准。
这一步的意义在于:让我在动手配置之前,先对“命名规范”这个大类到底管哪些事有个直观认知。否则我直接写select = ["N"],根本不知道它会带来多少条约束、会不会跟团队现有风格冲突。
3.2 写进pyproject.toml:新旧配置写法差异
看完规则清单,接下来就是把筛选结果落到项目配置里。以pyproject.toml为例,在Ruff较新的版本里,lint规则配置应该放在[tool.ruff.lint]节点下:
toml复制[tool.ruff.lint]
select = ["N", "E", "F", "W"]
ignore = ["N806"]
这里的select数组和命令行里的--select用的是同一套语法,每个元素都可以是前缀或精确代码。ignore则是例外列表,用于从已选中的范围里排除个别规则。
我之前用过旧版Ruff,当时配置写在[tool.ruff]下,直接写select和ignore。新版本对这种写法仍然兼容,但会给出deprecation警告,建议迁移到[tool.ruff.lint]。如果项目是新建的,直接用新写法,别走弯路。
还有一个细节:如果select里写了前缀N,而团队对个别规则有异议,比如觉得N806(局部变量名必须snake_case)太严格,因为有些循环变量就是习惯用单个字母,可以在ignore里单独排除。这种“大类开启、局部豁免”的策略,比把规则全部列出来再删要省事得多。
3.3 检查跑起来之后:报错、rule详情、noqa豁免
配置写完,执行检查:
bash复制ruff check src/
如果命中了N类规则,就会看到对应诊断信息。比如某处函数名用了驼峰,就会提示N802 function name should be lowercase。
如果你想了解某条规则的完整说明、示例和可配置参数,可以用ruff rule命令:
bash复制ruff rule N802
它会输出这条规则的详细文档,包括推荐的正确写法、可以配置的选项、甚至对应的flake8-naming原始规则编号。这比上网查资料更高效。
如果某一行代码确实需要豁免,比如第三方的API返回了一堆不符合PEP 8命名的字段名,你又不能改它,可以在代码末尾加注释:
python复制# noqa: N802
或者用命令行参数--add-noqa自动给所有当前告警加上忽略注释:
bash复制ruff check src/ --select N --add-noqa
这条命令会把所有N类告警自动标记noqa,适合刚开启某类规则但临时无法全量修复的场景。不过提醒一句:别滥用noqa,否则规则就形同虚设了。
4. 这些细节不注意,迟早被Ruff的select绊一跤
4.1 前缀与精确规则混用时的语义边界
--select支持前缀和精确代码混用,但边界在哪里,值得说清楚。
比如--select E会选中所有E开头的规则,包括E101、E501、E731等等。--select E5则只选E5xx这一组规则,比如E501、E502。--select E501则只选这一条。如果你写的是--select E50,它选中的是E501、E502等所有以E50开头的规则。
这种逐级前缀的特性非常灵活,但也容易出问题。我见过有人写--select E4, W时多打了个空格,变成--select "E4, W",结果Ruff解析时把" W"当作未知规则报错。CLI参数里逗号后面不要加空格,这是最常犯的低级错误。
另外要注意:规则代码是大小写敏感的。--select N没问题,但如果你手滑写成--select n,Ruff会直接报unknown rule selector。字母E、W、F这些前缀都是大写,没有例外。
4.2 版本升级带来的命令行为漂移
Ruff迭代速度很快,ruff list这个子命令本身也不是从一开始就有的。早期版本主要通过ruff check --select配合查看文档来管理规则,后来才加入了ruff list这样独立的规则浏览命令。不同版本的ruff list支持的参数有细微差别:
- 某些旧版本不支持
--filter --output-format可能在不同版本里有不同的可用值- 规则清单本身会随版本增加新规则,甚至标记旧规则为废弃
所以如果发现自己的ruff list --select N输出和网上教程对不上,先检查Ruff版本:
bash复制ruff --version
然后看帮助:
bash复制ruff list --help
我的建议是:项目里尽量固定Ruff版本,用pip或uv锁定版本号,避免团队成员各自升级导致规则清单不一致。CI环境里也要用锁定的版本,不然今天跑过检查明天可能因为新规则直接挂掉。
4.3 让规则筛选成为团队协作的一部分
最后说点实战心得。ruff list --select N这种命令,单看很不起眼,但它能成为团队规则治理的一个很好的入口。
我们团队现在做规则评审时,会先跑一遍ruff list --select "UP*" --output-format json,把preview状态的新规则导出来,贴到文档里,逐条讨论要不要开启。以前是从网页上扒规则说明,不仅费劲,还经常跟本地版本对不上。现在直接在终端导出json,再合并成Markdown表格,方便很多。
另外一个实用技巧是:如果你正在从flake8迁移到Ruff,可以先看看每个前缀对应哪些规则。ruff list会把规则的来源标签也显示出来,你可以在列表里看到哪些规则来自flake8-bugbear、哪些来自flake8-simplify,这样迁移的时候能对照旧配置文件逐个核对,而不是闭着眼睛全量开启。
我自己在配规则时的习惯是先宽后严:先只开E、F、I这几个最基础的,跑通检查;再逐步加上N、B、UP、SIM等,每次加一类,用ruff list --select <前缀>先看清单,和团队讨论后再落地。这样如果出了什么问题,也很容易定位是加哪一类规则引起的。
回到开头那串命令:ruff list --select N,语法说破天也就是“列出规则库中所有以N开头的规则”。但把它放进完整的Ruff工作流里,它其实是理解规则体系、配置规则集、维持长期代码规范的一个起点。下次在终端里敲这条命令之前,想想你想让哪一类规则进入项目,这个字母背后代表的是一整套工程决策。
