文件预览

ruff-rules.md

查看 Code QC 技能包中的文件内容。

文件内容

references/ruff-rules.md

# Ruff Rule Reference for QC

## Rule Selection Decision Tree

Use this flowchart to select the appropriate rule set for your project:

```
Is this a QC audit or development linting?
├── QC Audit (one-time check)
│   └── Use: STANDARD SET (catch issues without blocking)
│
└── Development/CI (ongoing)
    │
    ├── Is this a library/SDK?
    │   └── Use: STRICT SET (quality is paramount)
    │
    ├── Is this a CLI/script?
    │   ├── Allow print()? → Remove T201 from rules
    │   └── Use: STANDARD SET minus T201
    │
    ├── Is this a web application?
    │   └── Use: STANDARD + SECURITY (add S)
    │
    ├── Is this a data science/ML project?
    │   ├── Allow long lines? → Add --line-length 120
    │   └── Use: STANDARD SET (flexible)
    │
    └── Is this a legacy codebase?
        └── Use: MINIMAL SET (fix critical only)
```

## Rule Sets

### MINIMAL SET (Legacy/Quick Fix)
```bash
ruff check --select E722,B006 .
```
Only catches:
- Bare `except:` (potential bug masking)
- Mutable default arguments (actual bugs)

### STANDARD SET (QC Audit)
```bash
ruff check --select E722,T201,B006,F401,F841,UP,I --statistics .
```
Recommended for most projects. Catches common issues without being noisy.

### STRICT SET (Libraries/SDKs)
```bash
ruff check --select E722,T201,B006,F401,F841,UP,I,S,C90,PT,RUF,D --statistics .
```
Full strictness for public libraries.

### SECURITY SET (Web Applications)
```bash
ruff check --select E722,B006,F401,F841,UP,I,S --statistics .
```
Focus on security-relevant issues.

## Rule Explanations

### E722 — Bare except
```python
# Bad — catches KeyboardInterrupt, SystemExit, and hides bugs
try:
    risky()
except:
    pass

# Good — specific exceptions
try:
    risky()
except (ValueError, KeyError) as e:
    logger.error(f"Failed: {e}")

# Also Good — Exception base class (still allows KeyboardInterrupt)
try:
    risky()
except Exception as e:
    logger.exception("Unexpected error")
```

**Why it matters:** Bare `except:` catches `KeyboardInterrupt` (Ctrl+C), `SystemExit` (sys.exit()), and other system signals. This makes debugging impossible and can prevent graceful shutdown.

### T201 — print() found
```python
# Bad — goes to stdout, no timestamps, no levels
print(f"Processing {item}")

# Good — proper logging
logger.info(f"Processing {item}")

# Also Good — explicit CLI output
import sys
sys.stderr.write(f"Processing {item}\n")
```

**When to ignore:** CLIs, scripts, notebooks. Add `# noqa: T201` or exclude T201 from rule set.

### B006 — Mutable default argument
```python
# Bad — shared mutable state across all calls
def process(items=[]):  # Same list for every call!
    items.append("new")
    return items

process()  # ['new']
process()  # ['new', 'new'] — Bug!

# Good — None sentinel
def process(items=None):
    if items is None:
        items = []
    items.append("new")
    return items
```

**Why it matters:** This is a genuine bug, not style. Default arguments are evaluated once at function definition, not each call.

### F401 — Unused import
```python
# Bad — clutters namespace, slows startup
import os  # never used

# Good — remove or use
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    import os  # Only for type hints
```

**When to ignore:** `__init__.py` re-exports, plugins that need imports for side effects.

### F841 — Unused variable
```python
# Bad — confusing, may indicate forgotten logic
result = expensive_call()  # result never used

# Good — explicit discard
_ = expensive_call()

# Or just
expensive_call()
```

### UP — Pyupgrade
Catches outdated syntax for your target Python version:
```python
# Python 3.9+: old
from typing import Dict, List
# Python 3.9+: new
dict, list  # Use built-in generics

# Python 3.10+: old
Union[int, str]
# Python 3.10+: new
int | str
```

### I — isort
Consistent import ordering:
```python
# Bad — inconsistent grouping
from myproject.utils import helper
import os
from typing import Optional
import sys

# Good — grouped and sorted
import os
import sys
from typing import Optional

from myproject.utils import helper
```

## Extended Rules (Optional)

### S — Bandit Security
```bash
ruff check --select S .
```
- `S101` — assert used (disabled in production)
- `S105` — hardcoded password
- `S106` — hardcoded password in function arg
- `S107` — hardcoded password default
- `S108` — insecure temp file
- `S301` — pickle usage (insecure deserialization)
- `S608` — SQL injection

### C90 — McCabe Complexity
```bash
ruff check --select C90 --max-complexity 10 .
```
- `C901` — function too complex (cyclomatic complexity)

### D — Docstrings (pydocstyle)
```bash
ruff check --select D .
```
- `D100` — missing module docstring
- `D101` — missing class docstring
- `D102` — missing method docstring
- `D103` — missing function docstring

### PT — pytest
```bash
ruff check --select PT .
```
- `PT001` — use @pytest.fixture
- `PT006` — wrong type for parametrize args
- `PT009` — use pytest.raises instead of unittest

### RUF — Ruff-specific
```bash
ruff check --select RUF .
```
Ruff's own rules for common Python issues.

## Configuration

### pyproject.toml
```toml
[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["E722", "T201", "B006", "F401", "F841", "UP", "I"]
ignore = ["T201"]  # Allow print in this project

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]  # Allow assert in tests
"scripts/*" = ["T201"]  # Allow print in scripts
```

### CLI Override
```bash
# Ignore specific rules for this run
ruff check --ignore T201,F841 .

# Add extra rules
ruff check --select E722,T201,B006 --extend-select S .
```

## Fix Mode

### Safe Fixes Only
```bash
ruff check --fix .
```
Applies only fixes that are guaranteed safe (won't change behavior).

### Unsafe Fixes
```bash
ruff check --fix --unsafe-fixes .
```
Applies all fixes including potentially behavior-changing ones. Review diff carefully.

### Format
```bash
ruff format .
```
Black-compatible formatting. Run after `--fix`.

## Interpreting Output

```
$ ruff check --statistics .
Found 47 errors.
F401  [ ] 23  `...` imported but unused
T201  [ ] 15  `print` found
F841  [*]  5  Local variable `...` is assigned but never used
E722  [ ]  3  Do not use bare `except`
B006  [ ]  1  Do not use mutable data structures for argument defaults

[*] 5 fixable with `--fix`
```

- **Count by rule** — prioritize high-count rules
- **[*] Fixable** — can be auto-fixed
- **Statistics** — use `--statistics` for summary view

## CI Integration

```yaml
# GitHub Actions
- name: Lint with ruff
  run: |
    pip install ruff
    ruff check --output-format=github .
```

```yaml
# GitLab CI
lint:
  script:
    - pip install ruff
    - ruff check --output-format=gitlab .
```