核心目标:全面掌握 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 等数十个工具的功能。
#mermaid-svg-KG2K9qeZjZMHZvMQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KG2K9qeZjZMHZvMQ .error-icon{fill:#552222;}#mermaid-svg-KG2K9qeZjZMHZvMQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KG2K9qeZjZMHZvMQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .marker.cross{stroke:#333333;}#mermaid-svg-KG2K9qeZjZMHZvMQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KG2K9qeZjZMHZvMQ p{margin:0;}#mermaid-svg-KG2K9qeZjZMHZvMQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .cluster-label text{fill:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .cluster-label span{color:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .cluster-label span p{background-color:transparent;}#mermaid-svg-KG2K9qeZjZMHZvMQ .label text,#mermaid-svg-KG2K9qeZjZMHZvMQ span{fill:#333;color:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .node rect,#mermaid-svg-KG2K9qeZjZMHZvMQ .node circle,#mermaid-svg-KG2K9qeZjZMHZvMQ .node ellipse,#mermaid-svg-KG2K9qeZjZMHZvMQ .node polygon,#mermaid-svg-KG2K9qeZjZMHZvMQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .rough-node .label text,#mermaid-svg-KG2K9qeZjZMHZvMQ .node .label text,#mermaid-svg-KG2K9qeZjZMHZvMQ .image-shape .label,#mermaid-svg-KG2K9qeZjZMHZvMQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-KG2K9qeZjZMHZvMQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .rough-node .label,#mermaid-svg-KG2K9qeZjZMHZvMQ .node .label,#mermaid-svg-KG2K9qeZjZMHZvMQ .image-shape .label,#mermaid-svg-KG2K9qeZjZMHZvMQ .icon-shape .label{text-align:center;}#mermaid-svg-KG2K9qeZjZMHZvMQ .node.clickable{cursor:pointer;}#mermaid-svg-KG2K9qeZjZMHZvMQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .arrowheadPath{fill:#333333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KG2K9qeZjZMHZvMQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KG2K9qeZjZMHZvMQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KG2K9qeZjZMHZvMQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KG2K9qeZjZMHZvMQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .cluster text{fill:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ .cluster span{color:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KG2K9qeZjZMHZvMQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KG2K9qeZjZMHZvMQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-KG2K9qeZjZMHZvMQ .icon-shape,#mermaid-svg-KG2K9qeZjZMHZvMQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KG2K9qeZjZMHZvMQ .icon-shape p,#mermaid-svg-KG2K9qeZjZMHZvMQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KG2K9qeZjZMHZvMQ .icon-shape .label rect,#mermaid-svg-KG2K9qeZjZMHZvMQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KG2K9qeZjZMHZvMQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KG2K9qeZjZMHZvMQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KG2K9qeZjZMHZvMQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 整合
🦀 Ruff (一个工具搞定)
Linter
700+ 规则
Formatter
Black 兼容
Import 排序
isort 兼容
Auto-fix
安全自动修复
传统工具链 (多个独立工具)
Flake8
pyflakes + pycodestyle
isort
import 排序
Black
代码格式化
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 |
bash
# 同一个项目的实际测试:
# 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 安装方式
bash
# 推荐: pip 安装
pip install ruff
# 或使用 pipx (全局隔离)
pipx install ruff
# 验证安装
ruff --version
# ruff 0.6.x
1.2.2 第一个 lint 命令
python
# 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)) # ❌ 逗号后缺空格
bash
# 直接运行 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 自动修复
bash
# 一键修复所有可自动修复的问题
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 的规则按前缀分类,每个前缀对应一个工具或检查领域:
#mermaid-svg-a2ksZcFBGbfDHQe4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a2ksZcFBGbfDHQe4 .error-icon{fill:#552222;}#mermaid-svg-a2ksZcFBGbfDHQe4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a2ksZcFBGbfDHQe4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .marker.cross{stroke:#333333;}#mermaid-svg-a2ksZcFBGbfDHQe4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a2ksZcFBGbfDHQe4 p{margin:0;}#mermaid-svg-a2ksZcFBGbfDHQe4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .cluster-label text{fill:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .cluster-label span{color:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .cluster-label span p{background-color:transparent;}#mermaid-svg-a2ksZcFBGbfDHQe4 .label text,#mermaid-svg-a2ksZcFBGbfDHQe4 span{fill:#333;color:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .node rect,#mermaid-svg-a2ksZcFBGbfDHQe4 .node circle,#mermaid-svg-a2ksZcFBGbfDHQe4 .node ellipse,#mermaid-svg-a2ksZcFBGbfDHQe4 .node polygon,#mermaid-svg-a2ksZcFBGbfDHQe4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .rough-node .label text,#mermaid-svg-a2ksZcFBGbfDHQe4 .node .label text,#mermaid-svg-a2ksZcFBGbfDHQe4 .image-shape .label,#mermaid-svg-a2ksZcFBGbfDHQe4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-a2ksZcFBGbfDHQe4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .rough-node .label,#mermaid-svg-a2ksZcFBGbfDHQe4 .node .label,#mermaid-svg-a2ksZcFBGbfDHQe4 .image-shape .label,#mermaid-svg-a2ksZcFBGbfDHQe4 .icon-shape .label{text-align:center;}#mermaid-svg-a2ksZcFBGbfDHQe4 .node.clickable{cursor:pointer;}#mermaid-svg-a2ksZcFBGbfDHQe4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .arrowheadPath{fill:#333333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a2ksZcFBGbfDHQe4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a2ksZcFBGbfDHQe4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a2ksZcFBGbfDHQe4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-a2ksZcFBGbfDHQe4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .cluster text{fill:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 .cluster span{color:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a2ksZcFBGbfDHQe4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a2ksZcFBGbfDHQe4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-a2ksZcFBGbfDHQe4 .icon-shape,#mermaid-svg-a2ksZcFBGbfDHQe4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a2ksZcFBGbfDHQe4 .icon-shape p,#mermaid-svg-a2ksZcFBGbfDHQe4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-a2ksZcFBGbfDHQe4 .icon-shape .label rect,#mermaid-svg-a2ksZcFBGbfDHQe4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a2ksZcFBGbfDHQe4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a2ksZcFBGbfDHQe4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a2ksZcFBGbfDHQe4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Ruff 规则体系 (700+)
F: Pyflakes
未使用的 import
未使用的变量
重复定义
E/W: pycodestyle
行太长
行尾空格
I: isort
import 顺序错误
D: pydocstyle
缺少模块文档
首句命令式
UP: pyupgrade
用 list 替代 typing.List
用 dict\[\] 替代 typing.Dict\[\]
B: flake8-bugbear
可变默认参数
raise 链保留
更多...
简化 (flake8-simplify)
类型检查导入
Pylint 规则
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 查看所有可用规则
bash
# 列出所有规则
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 完整配置模板
toml
# 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 按场景选择规则集
toml
# ── 场景 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 配置优先级
#mermaid-svg-tylODKyca5llGbpI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tylODKyca5llGbpI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tylODKyca5llGbpI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tylODKyca5llGbpI .error-icon{fill:#552222;}#mermaid-svg-tylODKyca5llGbpI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tylODKyca5llGbpI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tylODKyca5llGbpI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tylODKyca5llGbpI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tylODKyca5llGbpI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tylODKyca5llGbpI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tylODKyca5llGbpI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tylODKyca5llGbpI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tylODKyca5llGbpI .marker.cross{stroke:#333333;}#mermaid-svg-tylODKyca5llGbpI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tylODKyca5llGbpI p{margin:0;}#mermaid-svg-tylODKyca5llGbpI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tylODKyca5llGbpI .cluster-label text{fill:#333;}#mermaid-svg-tylODKyca5llGbpI .cluster-label span{color:#333;}#mermaid-svg-tylODKyca5llGbpI .cluster-label span p{background-color:transparent;}#mermaid-svg-tylODKyca5llGbpI .label text,#mermaid-svg-tylODKyca5llGbpI span{fill:#333;color:#333;}#mermaid-svg-tylODKyca5llGbpI .node rect,#mermaid-svg-tylODKyca5llGbpI .node circle,#mermaid-svg-tylODKyca5llGbpI .node ellipse,#mermaid-svg-tylODKyca5llGbpI .node polygon,#mermaid-svg-tylODKyca5llGbpI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tylODKyca5llGbpI .rough-node .label text,#mermaid-svg-tylODKyca5llGbpI .node .label text,#mermaid-svg-tylODKyca5llGbpI .image-shape .label,#mermaid-svg-tylODKyca5llGbpI .icon-shape .label{text-anchor:middle;}#mermaid-svg-tylODKyca5llGbpI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tylODKyca5llGbpI .rough-node .label,#mermaid-svg-tylODKyca5llGbpI .node .label,#mermaid-svg-tylODKyca5llGbpI .image-shape .label,#mermaid-svg-tylODKyca5llGbpI .icon-shape .label{text-align:center;}#mermaid-svg-tylODKyca5llGbpI .node.clickable{cursor:pointer;}#mermaid-svg-tylODKyca5llGbpI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tylODKyca5llGbpI .arrowheadPath{fill:#333333;}#mermaid-svg-tylODKyca5llGbpI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tylODKyca5llGbpI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tylODKyca5llGbpI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tylODKyca5llGbpI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tylODKyca5llGbpI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tylODKyca5llGbpI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tylODKyca5llGbpI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tylODKyca5llGbpI .cluster text{fill:#333;}#mermaid-svg-tylODKyca5llGbpI .cluster span{color:#333;}#mermaid-svg-tylODKyca5llGbpI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tylODKyca5llGbpI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tylODKyca5llGbpI rect.text{fill:none;stroke-width:0;}#mermaid-svg-tylODKyca5llGbpI .icon-shape,#mermaid-svg-tylODKyca5llGbpI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tylODKyca5llGbpI .icon-shape p,#mermaid-svg-tylODKyca5llGbpI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tylODKyca5llGbpI .icon-shape .label rect,#mermaid-svg-tylODKyca5llGbpI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tylODKyca5llGbpI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tylODKyca5llGbpI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tylODKyca5llGbpI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 最高优先级
项目级
项目级 (备选)
最低优先级
覆盖
覆盖
CLI 参数
ruff check --select F401
最终配置
pyproject.toml
tool.ruff
ruff.toml
或 .ruff.toml
Ruff 内置默认值
2.3 Formatter------Black 兼容的格式化器
2.3.1 基本使用
bash
# 格式化当前目录
ruff format .
# 检查格式化差异 (不修改文件)
ruff format --check .
# 仅格式化指定文件
ruff format src/ tests/
# 显示差异 (类似 git diff)
ruff format --diff .
2.3.2 Ruff Formatter vs Black
python
# ── 原始代码 ──
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 安装扩展
bash
# 1. 安装 Ruff VS Code 扩展
# 在 VS Code 扩展市场搜索 "Ruff" (作者: Astral Software)
# 或命令行:
code --install-extension charliermarsh.ruff
3.1.2 settings.json 配置
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 工作流效果
#mermaid-svg-NcYvjQkzWEgSiQ8y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NcYvjQkzWEgSiQ8y .error-icon{fill:#552222;}#mermaid-svg-NcYvjQkzWEgSiQ8y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NcYvjQkzWEgSiQ8y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .marker.cross{stroke:#333333;}#mermaid-svg-NcYvjQkzWEgSiQ8y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NcYvjQkzWEgSiQ8y p{margin:0;}#mermaid-svg-NcYvjQkzWEgSiQ8y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .cluster-label text{fill:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .cluster-label span{color:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .cluster-label span p{background-color:transparent;}#mermaid-svg-NcYvjQkzWEgSiQ8y .label text,#mermaid-svg-NcYvjQkzWEgSiQ8y span{fill:#333;color:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .node rect,#mermaid-svg-NcYvjQkzWEgSiQ8y .node circle,#mermaid-svg-NcYvjQkzWEgSiQ8y .node ellipse,#mermaid-svg-NcYvjQkzWEgSiQ8y .node polygon,#mermaid-svg-NcYvjQkzWEgSiQ8y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .rough-node .label text,#mermaid-svg-NcYvjQkzWEgSiQ8y .node .label text,#mermaid-svg-NcYvjQkzWEgSiQ8y .image-shape .label,#mermaid-svg-NcYvjQkzWEgSiQ8y .icon-shape .label{text-anchor:middle;}#mermaid-svg-NcYvjQkzWEgSiQ8y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .rough-node .label,#mermaid-svg-NcYvjQkzWEgSiQ8y .node .label,#mermaid-svg-NcYvjQkzWEgSiQ8y .image-shape .label,#mermaid-svg-NcYvjQkzWEgSiQ8y .icon-shape .label{text-align:center;}#mermaid-svg-NcYvjQkzWEgSiQ8y .node.clickable{cursor:pointer;}#mermaid-svg-NcYvjQkzWEgSiQ8y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .arrowheadPath{fill:#333333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NcYvjQkzWEgSiQ8y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NcYvjQkzWEgSiQ8y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NcYvjQkzWEgSiQ8y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NcYvjQkzWEgSiQ8y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .cluster text{fill:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y .cluster span{color:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NcYvjQkzWEgSiQ8y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NcYvjQkzWEgSiQ8y rect.text{fill:none;stroke-width:0;}#mermaid-svg-NcYvjQkzWEgSiQ8y .icon-shape,#mermaid-svg-NcYvjQkzWEgSiQ8y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NcYvjQkzWEgSiQ8y .icon-shape p,#mermaid-svg-NcYvjQkzWEgSiQ8y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NcYvjQkzWEgSiQ8y .icon-shape .label rect,#mermaid-svg-NcYvjQkzWEgSiQ8y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NcYvjQkzWEgSiQ8y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NcYvjQkzWEgSiQ8y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NcYvjQkzWEgSiQ8y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Ctrl+S 保存文件
Ruff Lint
实时诊断
Ruff Fix
自动修复
Ruff Format
格式化代码
Ruff 整理
import 排序
编辑器内显示
波浪线 + 快速修复
代码自动清理
格式统一
import 有序
3.2 Pre-commit 集成------提交前自动检查
3.2.1 配置
yaml
# .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]
bash
# 安装 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 进阶:仅在变更文件上运行
yaml
# .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
yaml
# .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
yaml
# .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
bash
# 在 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 迁移
bash
# 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 迁移
bash
# 移除旧工具
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 迁移对照
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"]
toml
# ── 新配置 (Ruff) ──
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint.isort]
known-first-party = ["myproject"]
5.1 进阶技巧
5.1.1 忽略特定行
python
# 忽略单行
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 仅对特定文件启用/禁用规则
toml
[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 自定义规则忽略模式
toml
[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 导出规则配置
bash
# 将当前配置导出为配置文件
ruff check --show-settings
# 生成建议的 pyproject.toml
ruff config --format pyproject
# 查看某个文件上应用了哪些规则
ruff check --show-files file.py
5.2 性能调优
5.2.1 多层级缓存
bash
# 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 模式
bash
# --preview 启用尚未稳定的新规则
ruff check --preview .
ruff format --preview .
# pyproject.toml 中设置
[tool.ruff.lint]
preview = true
5.3 实战:新项目 Ruff 配置方案
toml
# ── 最小生产级配置 ──
[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 问题排查流程
#mermaid-svg-1g24HvsXVnzfIJNL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1g24HvsXVnzfIJNL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1g24HvsXVnzfIJNL .error-icon{fill:#552222;}#mermaid-svg-1g24HvsXVnzfIJNL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1g24HvsXVnzfIJNL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1g24HvsXVnzfIJNL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1g24HvsXVnzfIJNL .marker.cross{stroke:#333333;}#mermaid-svg-1g24HvsXVnzfIJNL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1g24HvsXVnzfIJNL p{margin:0;}#mermaid-svg-1g24HvsXVnzfIJNL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1g24HvsXVnzfIJNL .cluster-label text{fill:#333;}#mermaid-svg-1g24HvsXVnzfIJNL .cluster-label span{color:#333;}#mermaid-svg-1g24HvsXVnzfIJNL .cluster-label span p{background-color:transparent;}#mermaid-svg-1g24HvsXVnzfIJNL .label text,#mermaid-svg-1g24HvsXVnzfIJNL span{fill:#333;color:#333;}#mermaid-svg-1g24HvsXVnzfIJNL .node rect,#mermaid-svg-1g24HvsXVnzfIJNL .node circle,#mermaid-svg-1g24HvsXVnzfIJNL .node ellipse,#mermaid-svg-1g24HvsXVnzfIJNL .node polygon,#mermaid-svg-1g24HvsXVnzfIJNL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1g24HvsXVnzfIJNL .rough-node .label text,#mermaid-svg-1g24HvsXVnzfIJNL .node .label text,#mermaid-svg-1g24HvsXVnzfIJNL .image-shape .label,#mermaid-svg-1g24HvsXVnzfIJNL .icon-shape .label{text-anchor:middle;}#mermaid-svg-1g24HvsXVnzfIJNL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1g24HvsXVnzfIJNL .rough-node .label,#mermaid-svg-1g24HvsXVnzfIJNL .node .label,#mermaid-svg-1g24HvsXVnzfIJNL .image-shape .label,#mermaid-svg-1g24HvsXVnzfIJNL .icon-shape .label{text-align:center;}#mermaid-svg-1g24HvsXVnzfIJNL .node.clickable{cursor:pointer;}#mermaid-svg-1g24HvsXVnzfIJNL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1g24HvsXVnzfIJNL .arrowheadPath{fill:#333333;}#mermaid-svg-1g24HvsXVnzfIJNL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1g24HvsXVnzfIJNL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1g24HvsXVnzfIJNL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1g24HvsXVnzfIJNL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1g24HvsXVnzfIJNL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1g24HvsXVnzfIJNL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1g24HvsXVnzfIJNL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1g24HvsXVnzfIJNL .cluster text{fill:#333;}#mermaid-svg-1g24HvsXVnzfIJNL .cluster span{color:#333;}#mermaid-svg-1g24HvsXVnzfIJNL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1g24HvsXVnzfIJNL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1g24HvsXVnzfIJNL rect.text{fill:none;stroke-width:0;}#mermaid-svg-1g24HvsXVnzfIJNL .icon-shape,#mermaid-svg-1g24HvsXVnzfIJNL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1g24HvsXVnzfIJNL .icon-shape p,#mermaid-svg-1g24HvsXVnzfIJNL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1g24HvsXVnzfIJNL .icon-shape .label rect,#mermaid-svg-1g24HvsXVnzfIJNL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1g24HvsXVnzfIJNL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1g24HvsXVnzfIJNL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1g24HvsXVnzfIJNL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 确实没有错误
规则未启用
文件被 exclude
极少数情况
配置差异
版本不一致
缓存问题
ruff check 没有输出?
✅ 代码质量好!
检查 pyproject.toml 的 select 字段
检查 exclude 和 extend-exclude
ruff format 和 Black 输出不同?
升级到最新版 Ruff
对比 line-length / quote-style 设置
CI 中 ruff 报错但本地不报?
锁定 ruff 版本: pip install ruff==0.6.x
ruff clean && ruff check .
5.4.2 常见误报与处理
python
# 误报 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