Ruff 完全指南:下一代 Python Linter 与 Formatter
·
核心目标:全面掌握 Ruff 的安装配置、规则体系、自动修复、IDE 集成和生产级 CI/CD 工作流,实现 Python 代码质量检查从"跑完喝杯咖啡"到"瞬间完成"的升级。
前置知识:Python 3.8+ 基础、了解
pip/pyproject.toml,了解 Flake8 / Black / isort 等传统工具更佳。
1.1 什么是 Ruff
Ruff 是一个用 Rust 编写的极速 Python linter 和 formatter,由 Astral 团队开发。它在单个工具中整合了 Flake8、isort、pyupgrade、pylint、pydocstyle 等数十个工具的功能。
1.1.1 速度对比——为什么用 Ruff
| 工具 | Lint 10 万行 | Lint 100 万行 | 底层语言 |
|---|---|---|---|
| Ruff | 0.05s | 0.3s | Rust |
| Flake8 | 3s | 35s | Python |
| Pylint | 12s | 140s | Python |
| Black | 2.5s | 28s | Python |
# 同一个项目的实际测试:
# cpython 源码 (~200 万行 Python)
$ time ruff check . # 0.3s real
$ time flake8 . # 53s real ← Ruff 快 170 倍!
$ time pylint *.py # 3min+ ← Ruff 快 600 倍!
Ruff 快到什么程度?你保存文件后还没松开手,lint 结果已经出来了。
1.2 安装与第一个命令
1.2.1 安装方式
# 推荐: pip 安装
pip install ruff
# 或使用 pipx (全局隔离)
pipx install ruff
# 验证安装
ruff --version
# ruff 0.6.x
1.2.2 第一个 lint 命令
# bad_code.py —— 一个有问题的文件
import os, sys, json # ❌ 多模块一行
import collections # ❌ 未使用的 import
unused_var = "hello" # ❌ 未使用的变量
def my_function( x,y ): # ❌ 括号内多余空格
result=x+y # ❌ 操作符两边缺空格
return result
print(my_function(1,2)) # ❌ 逗号后缺空格
# 直接运行 ruff check
ruff check bad_code.py
Ruff 输出:
bad_code.py:1:1: F401 [*] `collections` imported but unused
bad_code.py:1:10: F401 [*] `os` imported but unused
bad_code.py:1:14: F401 [*] `sys` imported but unused
bad_code.py:1:19: F401 [*] `json` imported but unused
bad_code.py:3:1: F841 [*] Local variable `unused_var` is assigned to but never used
bad_code.py:5:17: E201 Whitespace after '('
bad_code.py:5:19: E231 Missing whitespace after ','
bad_code.py:5:21: E202 Whitespace before ')'
bad_code.py:6:10: E225 Missing whitespace around operator
Found 9 errors.
[*] 9 fixable with the `--fix` option.
1.2.3 自动修复
# 一键修复所有可自动修复的问题
ruff check --fix bad_code.py
# 修复后的代码:
import collections # 只移除了未使用的
# os, sys, json 已被删除
def my_function(x, y): # ✅ 空格正确
result = x + y # ✅ 操作符空格正确
return result
print(my_function(1, 2)) # ✅ 逗号后空格正确
# unused_var 被删除
2.1 Linter——700+ 规则体系
2.1.1 规则分类
Ruff 的规则按前缀分类,每个前缀对应一个工具或检查领域:
2.1.2 常用规则速查表
| 规则 | 序号 | 级别 | 说明 | 可修复 |
|---|---|---|---|---|
F401 |
— | Error | 未使用的 import | ✅ |
F841 |
— | Error | 未使用的变量 | ✅ |
E501 |
79 | Error | 行过长 | ❌ |
E711 |
— | Error | == None 应用 is None |
✅ |
I001 |
— | Error | import 未排序 | ✅ |
UP006 |
— | Error | 使用旧式 typing.List |
✅ |
B006 |
— | Error | 可变默认参数 | ❌ |
B904 |
— | Error | raise 未保留异常链 | ✅ |
SIM108 |
— | Error | 可用三元表达式简化 | ✅ |
SIM201 |
— | Error | not a in b → a not in b |
✅ |
D100 |
— | Error | 缺少模块级 docstring | ❌ |
TCH001 |
— | Error | 类型检查专用 import 应移入 TYPE_CHECKING |
✅ |
RUF100 |
— | Warn | # noqa 注释指向不存在的规则 |
❌ |
2.1.3 查看所有可用规则
# 列出所有规则
ruff rule --all
# 按前缀过滤
ruff rule --all | grep "^F" # Pyflakes 规则
ruff rule --all | grep "^UP" # pyupgrade 规则
# 查看某条规则的详情
ruff rule F401
# 输出: 规则描述、示例、出处、自动修复能力
2.2 配置——pyproject.toml 一站搞定
2.2.1 完整配置模板
# pyproject.toml
[tool.ruff]
# ── 目标 Python 版本 ──
target-version = "py311" # 3.11 语法特性
# ── 行宽 ──
line-length = 100 # 超长警告阈值
# ── 排除目录 ──
exclude = [
".git",
".venv",
"__pycache__",
"build",
"dist",
"migrations", # Django migrations
]
# ── Linting 规则选择 ──
[tool.ruff.lint]
select = [
# Pyflakes (基础错误)
"F",
# pycodestyle (代码风格)
"E", "W",
# isort (import 排序)
"I",
# pyupgrade (语法现代化)
"UP",
# flake8-bugbear (常见 bug)
"B",
# flake8-simplify (简化建议)
"SIM",
# 类型检查 import
"TCH",
# Pylint 规则 (部分)
"PL",
# Ruff 专有规则
"RUF",
]
ignore = [
# 允许行尾空白 (比如 Markdown 的换行)
"W291",
# 不强制文档字符串
"D100", "D104",
]
# 修正特定规则配置
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # __init__.py 允许未使用 import
"tests/**/*.py" = ["PLR2004"] # 测试文件中允许魔数
"scripts/*.py" = ["T201"] # 脚本中允许 print
[tool.ruff.lint.isort]
known-first-party = ["myproject"] # 第一方包前缀
force-single-line = true # 强制单行导入
[tool.ruff.lint.pylint]
max-args = 6 # 最大函数参数数
# ── Formatter 配置 ──
[tool.ruff.format]
quote-style = "double" # 双引号 (Black 默认)
indent-style = "space" # 空格缩进
skip-magic-trailing-comma = false # 保留尾随逗号魔法
docstring-code-format = true # 格式化 docstring 中的代码示例
2.2.2 按场景选择规则集
# ── 场景 1: 最小规则 (宽松, 只抓硬伤) ──
[tool.ruff.lint]
select = ["F", "E", "B"] # Pyflakes + pycodestyle + bugbear
# ── 场景 2: 推荐规则 (日常开发) ──
[tool.ruff.lint]
select = ["F", "E", "W", "I", "UP", "B", "SIM", "TCH", "RUF"]
# ── 场景 3: 严格规则 (开源项目/团队规范) ──
[tool.ruff.lint]
select = [
"F", "E", "W", "I", "D", # + pydocstyle
"UP", "B", "SIM", "TCH", "PL", # + Pylint 子集
"C4", "RUF", "T20", "PT" # + 其他
]
2.2.3 配置优先级
2.3 Formatter——Black 兼容的格式化器
2.3.1 基本使用
# 格式化当前目录
ruff format .
# 检查格式化差异 (不修改文件)
ruff format --check .
# 仅格式化指定文件
ruff format src/ tests/
# 显示差异 (类似 git diff)
ruff format --diff .
2.3.2 Ruff Formatter vs Black
# ── 原始代码 ──
def process_data(items: list[int], config: dict[str, any] = None) -> dict[str, list[int]]:
result = {}
for item in items:
if item > 0 and item % 2 == 0 and config and isinstance(config.get('mode'), str):
result.setdefault(config['mode'], []).append(item * 2 + 1)
return result
# ── Black 格式化 (line-length=88) ──
def process_data(
items: list[int], config: dict[str, any] = None
) -> dict[str, list[int]]:
result = {}
for item in items:
if (
item > 0
and item % 2 == 0
and config
and isinstance(config.get("mode"), str)
):
result.setdefault(config["mode"], []).append(item * 2 + 1)
return result
# ── Ruff format (line-length=88) ── 99.9% 相同!
# 差异仅在极端边缘情况下,Ruff 有意保持与 Black 高度兼容
| 特性 | Black | Ruff Formatter |
|---|---|---|
| 格式化质量 | ✅ 成熟 | ✅ 兼容 Black |
| 速度 | 2.5s (10万行) | 0.03s (80x) |
| 配置项 | 极少 (哲学) | 同样极少 |
--check / --diff |
✅ | ✅ |
| Jupyter Notebook | ✅ | ✅ |
| 预览样式 | ✅ (--preview) |
✅ (--preview) |
Ruff formatter 默认兼容 Black 99.9% 的输出。如果发现不一致,请提交 issue,Ruff 团队视之为 bug。
3.1 VS Code 集成——实时反馈
3.1.1 安装扩展
# 1. 安装 Ruff VS Code 扩展
# 在 VS Code 扩展市场搜索 "Ruff" (作者: Astral Software)
# 或命令行:
code --install-extension charliermarsh.ruff
3.1.2 settings.json 配置
{
// ── 关闭其他 linter, 只用 Ruff ──
"python.linting.enabled": false,
"python.linting.flake8Enabled": false,
"python.linting.pylintEnabled": false,
// ── Ruff 配置 ──
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true, // 保存时自动格式化
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit", // 保存时自动修复
"source.organizeImports.ruff": "explicit" // 保存时整理 import
}
},
"ruff.enable": true,
"ruff.lint.enable": true,
"ruff.format.enable": true,
// 可选: 将格式化方式存入 import
// 其他选项: "ruff.lineWidth": 100
}
3.1.3 工作流效果
3.2 Pre-commit 集成——提交前自动检查
3.2.1 配置
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.0 # 使用最新版本
hooks:
# Linter: 检查并修复
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
types_or: [python, pyi]
# Formatter: 格式化
- id: ruff-format
types_or: [python, pyi, jupyter]
# 安装 pre-commit hooks
pip install pre-commit
pre-commit install
# 手动对所有文件运行
pre-commit run --all-files
# 之后每次 git commit 会自动触发:
# > ruff.....................................................................Passed
# > ruff-format............................................................Passed
3.2.2 进阶:仅在变更文件上运行
# .pre-commit-config.yaml (高性能版)
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.0
hooks:
- id: ruff
args: [--fix]
# 不传文件参数,ruff 自动处理暂存区文件
- id: ruff-format
4.1 CI/CD 集成——门禁检查
4.1.1 GitHub Actions
# .github/workflows/lint.yml
name: Lint
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Ruff
run: pip install ruff
- name: Lint (不修复, 只检查)
run: ruff check --output-format=github .
- name: Format check (检查格式差异)
run: ruff format --check --diff .
# --output-format=github 使结果以 GitHub Annotation 显示
4.1.2 GitLab CI
# .gitlab-ci.yml
ruff:
image: python:3.11
script:
- pip install ruff
- ruff check --output-format=gitlab .
- ruff format --check --diff .
only:
- merge_requests
- main
4.1.3 仅检查变更文件——加速 CI
# 在 CI 中仅 lint 变更的文件 (相比 PR base)
git diff --name-only --diff-filter=ACMR origin/main...HEAD \
-- '*.py' '*.pyi' \
| xargs ruff check
4.2 迁移指南——从传统工具到 Ruff
4.2.1 从 Flake8 迁移
# Step 1: 生成 Ruff 配置 (读取现有 .flake8 配置)
ruff config
# Step 2: 对比 Ruff 和 Flake8 的检查结果
ruff check . > ruff-report.txt
flake8 . > flake8-report.txt
diff ruff-report.txt flake8-report.txt
# Step 3: 移除 Flake8 及其插件
pip uninstall flake8 flake8-bugbear flake8-simplify \
flake8-comprehensions flake8-print pep8-naming
# Step 4: 安装 Ruff
pip install ruff
# Step 5: 更新 pyproject.toml
4.2.2 规则映射表
| Flake8 插件 | Ruff 前缀 | 覆盖率 |
|---|---|---|
| pyflakes | F |
100% |
| pycodestyle | E, W |
99% |
| flake8-bugbear | B |
95% |
| flake8-simplify | SIM |
99% |
| flake8-comprehensions | C4 |
99% |
| flake8-print | T20 |
100% |
| pep8-naming | N |
97% |
| flake8-annotations | ANN |
90% |
| flake8-docstrings | D |
90% |
| flake8-pytest-style | PT |
98% |
| pylint | PL(部分) |
~60% |
4.2.3 从 isort + Black 迁移
# 移除旧工具
pip uninstall black isort
# Ruff format 替代 Black
# 之前: black --check --diff .
# 之后: ruff format --check --diff .
# Ruff lint 替代 isort
# 之前: isort --check-only --diff .
# 之后: ruff check --select I --diff .
4.2.4 pyproject.toml 迁移对照
# ── 旧配置 ──
[tool.black]
line-length = 100
target-version = ["py311"]
[tool.isort]
profile = "black"
line_length = 100
known_first_party = ["myproject"]
[tool.flake8]
max-line-length = 100
extend-ignore = ["E203"]
# ── 新配置 (Ruff) ──
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint.isort]
known-first-party = ["myproject"]
5.1 进阶技巧
5.1.1 忽略特定行
# 忽略单行
import unused_module # noqa: F401
# 忽略特定规则
x = "very long string that exceeds the line length limit..." # noqa: E501
# 忽略整段代码
# ruff: noqa: F841, E501
def experimental():
x = 1 # 不会报告 F841
y = very_long_function_name(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) # 不会报告 E501
# ruff: noqa: F841, E501
5.1.2 仅对特定文件启用/禁用规则
[tool.ruff.lint.per-file-ignores]
# CLI 脚本中允许 print
"cli.py" = ["T201"]
"manage.py" = ["T201"]
# 测试文件更宽松
"tests/**/*.py" = [
"PLR2004", # 魔数
"S101", # assert (测试中正常)
"ARG001", # 未使用函数参数 (fixture 中用)
]
# Django migrations 不使用 lint
"**/migrations/*.py" = ["ALL"]
# Notebook 文件中允许 print
"*.ipynb" = ["T201"]
5.1.3 自定义规则忽略模式
[tool.ruff.lint]
# 允许某些变量名的未使用导入 (如 typing 的 TYPE_CHECKING)
ignore-init-module-imports = true
# 不检查赋值表达式中未使用的变量
# a = [x for x in range(10) if x > 5] ← x 被正常使用
dummy-variable-rgx = "^_+|^(_$|unused_)" # _开头的变量不报告
[tool.ruff.lint.flake8-type-checking]
# 强制将 typing-only import 移入 TYPE_CHECKING
strict = true
5.1.4 Ruff 导出规则配置
# 将当前配置导出为配置文件
ruff check --show-settings
# 生成建议的 pyproject.toml
ruff config --format pyproject
# 查看某个文件上应用了哪些规则
ruff check --show-files file.py
5.2 性能调优
5.2.1 多层级缓存
# Ruff 自动缓存结果到 ~/.cache/ruff/
# 第二次运行几乎瞬时完成
# 查看缓存位置
ruff check --show-files . | head
# 清除缓存
ruff clean
# CI 中使用缓存 (GitHub Actions)
# .github/workflows/lint.yml
- uses: actions/cache@v4
with:
path: ~/.cache/ruff
key: ruff-${{ hashFiles('pyproject.toml') }}-${{ github.sha }}
restore-keys: ruff-${{ hashFiles('pyproject.toml') }}-
5.2.2 使用 --preview 模式
# --preview 启用尚未稳定的新规则
ruff check --preview .
ruff format --preview .
# pyproject.toml 中设置
[tool.ruff.lint]
preview = true
5.3 实战:新项目 Ruff 配置方案
# ── 最小生产级配置 ──
[tool.ruff]
target-version = "py311"
line-length = 100
extend-exclude = [
".git",
".venv",
"build",
"dist",
"migrations",
"node_modules",
"__pycache__",
]
[tool.ruff.lint]
select = [
"F", # Pyflakes: 基础错误检测
"E", "W", # pycodestyle: 代码风格
"I", # isort: import 排序
"UP", # pyupgrade: 语法现代化
"B", # bugbear: 常见陷阱
"SIM", # flake8-simplify: 简化代码
"TCH", # 类型检查导入
"T20", # flake8-print: 防止遗留 print
"RUF", # Ruff 专有规则
"C4", # comprehensions: 推导式优化
"N", # pep8-naming: 命名规范
]
ignore = []
fixable = ["ALL"]
unfixable = []
[tool.ruff.lint.isort]
known-first-party = ["myapp"]
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "parents"
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true
5.4 常见问题与排障
5.4.1 问题排查流程
5.4.2 常见误报与处理
# 误报 1: TYPE_CHECKING 导入
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from myapp.models import User # Ruff: F401 unused import
# 解决: 启用 TCH 规则, Ruff 会自动理解 TYPE_CHECKING
# 误报 2: @override 装饰器
from typing import override
class Child(Parent):
@override
def method(self): ... # 如果工具不能识别 override → F811
# 解决: target-version >= "py312" 时自动识别
# 误报 3: dataclass 的 __init__
from dataclasses import dataclass
@dataclass
class Config:
name: str = "default" # Ruff: B008 mutable default in function
# 解决: Ruff 识别 dataclass, 不会误报 (0.3.x+)
5.5 小结
| 知识点 | 掌握程度 | 核心要点 |
|---|---|---|
| Ruff 定位 | 掌握 | Rust 编写的极速 Python linter + formatter,替代 Flake8/Black/isort |
| 安装使用 | 熟练 | pip install ruff,ruff check --fix |
| 规则体系 | 掌握 | 700+ 规则,按前缀分类 (F/E/I/UP/B/SIM/PL…) |
| pyproject.toml 配置 | 熟练 | select/ignore/per-file-ignores 三段式管理 |
| Formatter | 掌握 | ruff format --check --diff .,兼容 Black 99.9% |
| VS Code 集成 | 掌握 | 保存时自动 lint + fix + format + import 整理 |
| Pre-commit | 掌握 | 提交前自动检查,零等待 |
| CI/CD | 掌握 | GitHub Actions / GitLab CI 一行安装即可 |
| 迁移 | 掌握 | Flake8→Ruff 规则映射表,配置一键转换 |
| 进阶技巧 | 理解 | 行级忽略、per-file-ignores、缓存优化 |
推荐资源
- Ruff 官方文档 —— 最权威的配置和规则参考
- Ruff Rules —— 所有 700+ 规则的完整列表
- Ruff VS Code Extension
- Ruff Pre-commit Hooks
- Why Ruff
更多推荐


所有评论(0)