Python技术迷

又一 Python 神奇库 -- Ruff !

一眼看过去没报错,提交一跑,CI 红了。 不是代码不能跑,是 import 顺序乱了,没用到的包挂着,== None 这种小别扭也混进去了。更烦的是,团队里有人跑 black,有人跑 isort,再加一层 flake8,一套活下来,光等工具转圈都够呛。

这种地方我现在一般不跟人讲大道理,先把工具链砍掉一半。 Ruff 就是干这个的。

Ruff 现在不只是个 lint 工具,它自己就能做检查和格式化,目标就是拿一个统一入口,把 Flake8、isort、pyupgrade、autoflake、Black 这类活尽量收进来,而且官方文档明确把它定位成“超快”的 linter 和 formatter;格式化这块也就是 ruff format,并且对 Black 的输出保持高度兼容。

先装上再说:

pip install ruff
# 或者
uv add --dev ruff

Ruff 官方也给了这两种常见装法,另外还能直接跑 uvx ruff check、uvx ruff format。

我更建议项目里直接落一个 pyproject.toml,别每个人本地靠感觉配:

[tool.ruff]
line-length = 100
target-version = "py311"
exclude = ["build", "dist", ".venv"]

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

这里有几个点挺实用。

select 里这几个规则别贪多。E、F 先把最常见的格式和语法问题兜住,I 处理 import 排序,UP 顺手把一些老写法升级掉,B 和 SIM 则专门抓那种“代码能跑,但写得别扭,后面容易出坑”的东西。Ruff 官方也给过一套很接近这个思路的推荐组合。

比如这段业务代码,平时 review 很容易漏:

from pathlib import Path
import os
import json

defload_user_profile(file_path=None):
if file_path == None:
        file_path = "./data/user_profile.json"

if os.path.exists(file_path):
        f = open(file_path, "r", encoding="utf-8")
        data = json.load(f)
        f.close()
return data
else:
return {}

这段代码能跑,但 Ruff 一扫,基本会把几个毛病都点出来:

  • import 顺序不对
  • file_path == None 写法别扭
  • 打开文件没用上下文管理器
  • 有些写法可以更现代一点

顺手执行:

ruff check . --fix
ruff format .

第一条负责修能自动修的,第二条统一格式。官方文档里这两个入口就是这么分工的,ruff check 负责 lint,ruff format 负责格式化,--check 模式还能直接挂到 CI 里。

修完大概会变成这样:

import json
from pathlib import Path

defload_user_profile(file_path: str | None = None) -> dict:
    path = Path(file_path or"./data/user_profile.json")
ifnot path.exists():
return {}

with path.open("r", encoding="utf-8") as f:
return json.load(f)

这就顺眼多了。 不是说 Ruff 帮你“写出好代码”,它干的是另一件更值钱的事:把那些低价值、重复、机械、每次 review 都要骂一遍的问题,先在提交前清掉。

还有个地方我挺喜欢:它的配置入口够统一。 Ruff 可以用 pyproject.toml、ruff.toml 或 .ruff.toml,而且不是那种层层自动合并配置的路子,默认取最近的配置文件,真要继承再显式 extend。多人项目里这点很省事,不容易出现“我本地和你本地怎么扫出来不一样”的破事。

再给你一个我在线上项目里常用的 pre-commit 配置,效果很直接:代码还没进仓库,先过 Ruff。

repos:
-repo:https://github.com/astral-sh/ruff-pre-commit
rev:v0.15.6
hooks:
-id:ruff-check
args:[--fix]
-id:ruff-format

这个集成方式是 Ruff 官方文档直接给出来的。

你会发现,团队里以前那种:

  • isort 没跑
  • black 版本不一致
  • flake8 插件缺了一个
  • 老语法没人改
  • PR 里一堆无意义格式噪音

这些事,基本都能压下去。

当然,Ruff 也不是“装了就结束”。 我一般不建议一上来就 select = ["ALL"]。规则太猛,老项目第一次接入,告警能把人看麻。正确姿势是先收一套常用规则,把自动修复能吃掉的先吃掉,再一点点放开。尤其是存量项目,别把代码治理做成一次事故。

还有一个细节很多人会忽略:Ruff 的 formatter 虽然目标是和 Black 高度兼容,但官方也明确说了,不是要让你长期 Black、Ruff 混着跑,它们在边角行为上还是会有差异。项目里定一个就行,别双修。

所以这玩意神奇在哪?

不在“它是新工具”。 在它终于把 Python 这堆零零碎碎的小工具,往一个入口上收了。命令少了,配置少了,等待少了,PR 里的废话也少了。你真接到团队里,感受最明显的不是“代码更优雅”,而是那些本来不该靠人眼盯的脏活,终于有人替你干了。

这就够了。