16 KiB
代码质量标准
版本: 3.6.0 最后更新: 2026-02-18 状态: ✅ 生产就绪
概述
Skill Seekers 通过自动化 lint 检查、全面的测试和持续集成来保持高代码质量。本文档概述了用于确保可靠性和可维护性的质量标准、工具和流程。
质量支柱:
- Lint 检查 - 使用 Ruff 进行自动化代码风格和错误检测
- 测试 - 全面的测试覆盖(1,880+ 测试)
- 类型安全 - 类型提示与验证
- 安全 - 使用 Bandit 进行安全扫描
- CI/CD - 每次提交的自动化验证
使用 Ruff 进行 Lint 检查
什么是 Ruff?
Ruff 是一个用 Rust 编写的极速 Python linter,集成了多个工具的功能:
- Flake8(风格检查)
- isort(导入排序)
- Black(代码格式化)
- pyupgrade(Python 版本升级)
- 以及 100+ 其他 lint 规则
为什么选择 Ruff:
- ⚡ 比传统 linter 快 10-100 倍
- 🔧 大多数问题可自动修复
- 📦 一个工具替代 10+ 个传统工具
- 🎯 全面的规则覆盖
安装
# Using uv (recommended)
uv pip install ruff
# Using pip
pip install ruff
# Development installation
pip install -e ".[dev]" # Includes ruff
运行 Ruff
检查问题
# Check all Python files
ruff check .
# Check specific directory
ruff check src/
# Check specific file
ruff check src/skill_seekers/cli/doc_scraper.py
# Check with auto-fix
ruff check --fix .
格式化代码
# Check formatting (dry run)
ruff format --check .
# Apply formatting
ruff format .
# Format specific file
ruff format src/skill_seekers/cli/doc_scraper.py
配置
Ruff 配置位于 pyproject.toml:
[tool.ruff]
line-length = 100
target-version = "py310"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"SIM", # flake8-simplify
"UP", # pyupgrade
]
ignore = [
"E501", # Line too long (handled by formatter)
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = [
"S101", # Allow assert in tests
]
常见 Ruff 规则
SIM102:简化嵌套 If 语句
修改前:
if condition1:
if condition2:
do_something()
修改后:
if condition1 and condition2:
do_something()
原因: 提高可读性,减少嵌套层级。
SIM117:合并多个 With 语句
修改前:
with open('file1.txt') as f1:
with open('file2.txt') as f2:
process(f1, f2)
修改后:
with open('file1.txt') as f1, open('file2.txt') as f2:
process(f1, f2)
原因: 语法更简洁,资源管理更好。
B904:正确的异常链
修改前:
try:
risky_operation()
except Exception:
raise CustomError("Failed")
修改后:
try:
risky_operation()
except Exception as e:
raise CustomError("Failed") from e
原因: 保留错误上下文,便于调试。
SIM113:移除未使用的 Enumerate 计数器
修改前:
for i, item in enumerate(items):
process(item) # i is never used
修改后:
for item in items:
process(item)
原因: 意图更清晰,移除未使用的变量。
B007:未使用的循环变量
修改前:
for item in items:
total += 1 # item is never used
修改后:
for _ in items:
total += 1
原因: 明确表示循环变量是有意不使用的。
ARG002:未使用的方法参数
修改前:
def process(self, data, unused_arg):
return data.transform() # unused_arg never used
修改后:
def process(self, data):
return data.transform()
原因: 移除死代码,使函数签名更清晰。
近期代码质量改进
v3.6.0 修复(2026 年 1 月 18 日)
修复了代码库中全部 21 个 ruff lint 错误:
| 规则 | 数量 | 受影响文件 | 影响 |
|---|---|---|---|
| SIM102 | 7 | config_extractor.py、pattern_recognizer.py(3 处) | 合并嵌套 if 语句 |
| SIM117 | 9 | test_example_extractor.py(3 处)、unified_skill_builder.py | 合并 with 语句 |
| B904 | 1 | pdf_scraper.py | 添加异常链 |
| SIM113 | 1 | config_validator.py | 移除未使用的 enumerate 计数器 |
| B007 | 1 | doc_scraper.py | 将未使用的循环变量改为 _ |
| ARG002 | 1 | 测试 fixture | 移除未使用的测试参数 |
| 总计 | 21 | 12 个文件 | 零 lint 错误 |
结果: 代码库干净无 lint 错误,可维护性提升。
已更新的文件
- src/skill_seekers/cli/config_extractor.py(SIM102 修复)
- src/skill_seekers/cli/config_validator.py(SIM113 修复)
- src/skill_seekers/cli/doc_scraper.py(B007 修复)
- src/skill_seekers/cli/pattern_recognizer.py(3 处 SIM102 修复)
- src/skill_seekers/cli/test_example_extractor.py(3 处 SIM117 修复)
- src/skill_seekers/cli/unified_skill_builder.py(SIM117 修复)
- src/skill_seekers/cli/pdf_scraper.py(B904 修复)
- 6 个测试文件(各类修复)
测试要求
测试覆盖标准
关键路径: 要求 100% 覆盖
- 核心抓取逻辑
- 平台适配器
- MCP 工具实现
- 配置验证
整体项目: 覆盖率目标 >80%
当前状态:
- ✅ 1,880+ 测试通过
- ✅ 代码覆盖率 >85%
- ✅ 所有关键路径已覆盖
- ✅ 已集成 CI/CD
运行测试
全部测试
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=src/skill_seekers --cov-report=term --cov-report=html
# View HTML coverage report
open htmlcov/index.html
特定测试类别
# Unit tests only
pytest tests/test_*.py -v
# Integration tests
pytest tests/test_*_integration.py -v
# E2E tests
pytest tests/test_*_e2e.py -v
# MCP tests
pytest tests/test_mcp*.py -v
测试标记
# Slow tests (skip by default)
pytest tests/ -m "not slow"
# Run slow tests
pytest tests/ -m slow
# Async tests
pytest tests/ -m asyncio
测试类别
-
单元测试(800+ 测试)
- 单个函数测试
- 隔离的组件测试
- 模拟外部依赖
-
集成测试(300+ 测试)
- 多组件工作流
- 端到端功能测试
- 真实文件系统操作
-
E2E 测试(100+ 测试)
- 完整用户工作流
- CLI 命令测试
- 平台集成测试
-
MCP 测试(63 个测试)
- 全部 40 个 MCP 工具
- 传输模式测试(stdio、HTTP)
- 错误处理验证
提交前的测试要求
根据 ~/.claude/CLAUDE.md 中的用户指令:
"never skip any test. always make sure all test pass"
这意味着:
- ✅ 提交前全部 1,880+ 测试必须通过
- ✅ 不跳过任何测试,即使它们很慢
- ✅ 为新功能添加测试
- ✅ 立即修复失败的测试
- ✅ 保持或提升覆盖率
CI/CD 集成
GitHub Actions 工作流
Skill Seekers 使用 GitHub Actions 对每次提交和 PR 进行自动化质量检查。
工作流配置
# .github/workflows/ci.yml (excerpt)
name: CI
on:
push:
branches: [main, development]
pull_request:
branches: [main, development]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: pip install ruff
- name: Run Ruff Check
run: ruff check .
- name: Run Ruff Format Check
run: ruff format --check .
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install package
run: pip install -e ".[all-llms,dev]"
- name: Run tests
run: pytest tests/ --cov=src/skill_seekers --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
CI 检查
每次提交和 PR 必须通过:
- Ruff Lint 检查 - 零 lint 错误
- Ruff 格式检查 - 一致的代码风格
- Pytest - 全部 1,880+ 测试通过
- 覆盖率 - 代码覆盖率 >80%
- 多平台 - Ubuntu + macOS
- 多版本 - Python 3.10-3.13
状态: ✅ 所有检查通过
Pre-commit 钩子
设置
# Install pre-commit
pip install pre-commit
# Install hooks
pre-commit install
配置
创建 .pre-commit-config.yaml:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.0
hooks:
# Run ruff linter
- id: ruff
args: [--fix]
# Run ruff formatter
- id: ruff-format
- repo: local
hooks:
# Run tests before commit
- id: pytest
name: pytest
entry: pytest
language: system
pass_filenames: false
always_run: true
args: [tests/, -v]
使用
# Pre-commit hooks run automatically on git commit
git add .
git commit -m "Your message"
# → Runs ruff check, ruff format, pytest
# Run manually on all files
pre-commit run --all-files
# Skip hooks (emergency only!)
git commit -m "Emergency fix" --no-verify
最佳实践
代码组织
导入顺序
# 1. Standard library imports
import os
import sys
from pathlib import Path
# 2. Third-party imports
import anthropic
import requests
from fastapi import FastAPI
# 3. Local application imports
from skill_seekers.cli.doc_scraper import scrape_all
from skill_seekers.cli.adaptors import get_adaptor
工具: Ruff 通过 I 规则自动排序导入。
命名约定
# Constants: UPPER_SNAKE_CASE
MAX_PAGES = 500
DEFAULT_TIMEOUT = 30
# Classes: PascalCase
class DocumentationScraper:
pass
# Functions/variables: snake_case
def scrape_all(base_url, config):
pages_count = 0
return pages_count
# Private: leading underscore
def _internal_helper():
pass
文档
文档字符串
def scrape_all(base_url: str, config: dict) -> list[dict]:
"""Scrape documentation from a website using BFS traversal.
Args:
base_url: The root URL to start scraping from
config: Configuration dict with selectors and patterns
Returns:
List of page dictionaries containing title, content, URL
Raises:
NetworkError: If connection fails
InvalidConfigError: If config is malformed
Example:
>>> pages = scrape_all('https://docs.example.com', config)
>>> len(pages)
42
"""
pass
类型提示
from typing import Optional, Union, Literal
def package_skill(
skill_dir: str | Path,
target: Literal['claude', 'gemini', 'openai', 'markdown'],
output_path: Optional[str] = None
) -> str:
"""Package skill for target platform."""
pass
错误处理
异常模式
# Good: Specific exceptions with context
try:
result = risky_operation()
except NetworkError as e:
raise ScrapingError(f"Failed to fetch {url}") from e
# Bad: Bare except
try:
result = risky_operation()
except: # ❌ Too broad, loses error info
pass
日志记录
import logging
logger = logging.getLogger(__name__)
# Log at appropriate levels
logger.debug("Processing page: %s", url)
logger.info("Scraped %d pages", len(pages))
logger.warning("Rate limit approaching: %d requests", count)
logger.error("Failed to parse: %s", url, exc_info=True)
安全扫描
Bandit
Bandit 扫描 Python 代码中的安全漏洞。
安装
pip install bandit
运行 Bandit
# Scan all Python files
bandit -r src/
# Scan with config
bandit -r src/ -c pyproject.toml
# Generate JSON report
bandit -r src/ -f json -o bandit-report.json
常见安全问题
B404:导入 subprocess 模块
# Review: Ensure safe usage of subprocess
import subprocess
# ✅ Safe: Using subprocess with shell=False and list arguments
subprocess.run(['ls', '-l'], shell=False)
# ❌ UNSAFE: Using shell=True with user input (NEVER DO THIS)
# This is an example of what NOT to do - security vulnerability!
# subprocess.run(f'ls {user_input}', shell=True)
B605:通过 shell 启动进程
# ❌ UNSAFE: Shell injection risk (NEVER DO THIS)
# Example of security anti-pattern:
# import os
# os.system(f'rm {filename}')
# ✅ Safe: Use subprocess with list arguments
import subprocess
subprocess.run(['rm', filename], shell=False)
安全最佳实践:
- 切勿对用户输入使用
shell=True - 始终验证和清理用户输入
- 使用带列表参数的 subprocess 而不是 shell 命令
- 避免动态构造命令
开发工作流
1. 开始工作前
# Pull latest changes
git checkout development
git pull origin development
# Create feature branch
git checkout -b feature/your-feature
# Install dependencies
pip install -e ".[all-llms,dev]"
2. 开发过程中
# Run linter frequently
ruff check src/skill_seekers/cli/your_file.py --fix
# Run relevant tests
pytest tests/test_your_feature.py -v
# Check formatting
ruff format src/skill_seekers/cli/your_file.py
3. 提交前
# Run all linting checks
ruff check .
ruff format --check .
# Run full test suite (REQUIRED)
pytest tests/ -v
# Check coverage
pytest tests/ --cov=src/skill_seekers --cov-report=term
# Verify all tests pass ✅
4. 提交更改
# Stage changes
git add .
# Commit (pre-commit hooks will run)
git commit -m "feat: Add your feature
- Detailed change 1
- Detailed change 2
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
# Push to remote
git push origin feature/your-feature
5. 创建 Pull Request
# Create PR via GitHub CLI
gh pr create --title "Add your feature" --body "Description..."
# CI checks will run automatically:
# ✅ Ruff linting
# ✅ Ruff formatting
# ✅ Pytest (1,880+ tests)
# ✅ Coverage report
# ✅ Multi-platform (Ubuntu + macOS)
# ✅ Multi-version (Python 3.10-3.13)
质量指标
当前状态(v3.6.0)
| 指标 | 数值 | 目标 | 状态 |
|---|---|---|---|
| Lint 错误 | 0 | 0 | ✅ |
| 测试数量 | 1200+ | 1000+ | ✅ |
| 测试通过率 | 100% | 100% | ✅ |
| 代码覆盖率 | >85% | >80% | ✅ |
| CI 通过率 | 100% | >95% | ✅ |
| Python 版本 | 3.10-3.13 | 3.10+ | ✅ |
| 平台 | Ubuntu、macOS | 2+ | ✅ |
历史改进
| 版本 | Lint 错误 | 测试 | 覆盖率 |
|---|---|---|---|
| v2.5.0 | 38 | 602 | 75% |
| v2.6.0 | 21 | 700+ | 80% |
| v2.7.0 | 0 | 1200+ | 85%+ |
进展: 所有质量指标持续改进。
故障排除
常见问题
1. 更新后出现 Lint 错误
# Update ruff
pip install --upgrade ruff
# Re-run checks
ruff check .
2. 本地测试失败
# Ensure package is installed
pip install -e ".[all-llms,dev]"
# Clear pytest cache
rm -rf .pytest_cache/
rm -rf **/__pycache__/
# Re-run tests
pytest tests/ -v
3. 覆盖率过低
# Generate detailed coverage report
pytest tests/ --cov=src/skill_seekers --cov-report=html
# Open report
open htmlcov/index.html
# Identify untested code (red lines)
# Add tests for uncovered lines
相关文档
版本: 3.6.0 最后更新: 2026-02-18 状态: ✅ 生产就绪