This commit is contained in:
ВяткинАртём
2026-04-06 17:54:07 +03:00
parent 6d4e45d407
commit d00f858d98
15 changed files with 1319 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
---
name: python-review
description: "Python 3.13+ code review with mypy strict and ruff. Use when: user asks to review Python code, check types, lint, fix type errors, review pull request, code quality check, mypy errors, ruff violations, optimize Python, улучши код, ревью кода, проверь типы, код ревью питон, исправь mypy, исправь ruff."
argument-hint: "File or folder to review (e.g. src/module.py or src/)"
---
# Python Code Review (3.13+ · mypy strict · ruff)
Two-phase workflow: first **propose** improvements, then **apply** only what the user accepts.
## When to Use
- Review Python files before commit or PR
- Fix mypy strict or ruff violations
- Optimize performance, readability, or architecture
- Prepare code for Python 3.13+ compatibility
## Procedure
### Step 1 — Discover Project Config
Before running any tool, check if `pyproject.toml` exists in the workspace root.
Read it and extract:
- `[tool.mypy]` — use these settings instead of defaults; if `strict = true` is absent, treat it as enabled anyway
- `[tool.ruff]` and `[tool.ruff.lint]` — use `select`, `ignore`, `line-length`, `target-version`
- `[project]``requires-python` — confirm target is 3.13+
If `pyproject.toml` is absent — use the baseline config from
[references/default-config.md](./references/default-config.md).
### Step 2 — Run Static Analysis
Run both tools on the target file(s) and collect all findings:
```bash
# mypy
mypy --strict <target>
# ruff (check only, no fixes yet)
ruff check <target>
```
Also read the source file(s) to perform a manual review pass.
### Step 3 — Categorize Findings
Group all findings into three tiers (see full criteria in
[references/review-checklist.md](./references/review-checklist.md)):
| Tier | Label | Examples |
|------|-------|---------|
| 🔴 Must fix | Correctness / type safety | mypy errors, `Any` leaks, logic bugs, security issues |
| 🟡 Should fix | Code quality | ruff warnings, missing return types, dead code, mutable defaults |
| 🟢 Consider | Optimization / style | performance hints, readability, modern Python 3.13+ idioms |
### Step 4 — Present Proposals (DO NOT EDIT YET)
Output a structured review report:
```
## Code Review: <filename>
### 🔴 Must Fix ({N})
1. **[MYPY/RUFF code]** `symbol` — explanation
```python
# before
# after
```
### 🟡 Should Fix ({N})
...
### 🟢 Consider ({N})
...
---
Accept all fixes? Or specify which tiers/items to apply: "apply 🔴", "apply all", "apply 1,3,5"
```
**Do not modify any files until the user explicitly accepts.**
### Step 5 — Apply Accepted Fixes
Wait for the user's response. Parse their intent:
| User says | Action |
|-----------|--------|
| "apply all" / "применить всё" | Apply all tiers |
| "apply 🔴" / "применить красные" | Apply Must Fix only |
| "apply 🔴🟡" | Apply Must Fix + Should Fix |
| "apply 1,3,5" / "пункты 1,3,5" | Apply specific numbered items |
| "skip" / "отмена" | Do nothing |
Apply fixes following the rules in [references/fix-rules.md](./references/fix-rules.md).
After applying:
1. Re-run `mypy --strict` and `ruff check` to confirm zero remaining violations
2. Report: "Applied N fixes. Remaining issues: X" (or "Clean ✅")
### Step 6 — Final Check
If any 🔴 items were skipped — warn the user:
> ⚠️ {N} critical issue(s) not applied. The code may have type errors or bugs at runtime.
@@ -0,0 +1,56 @@
# Default Config
Baseline mypy and ruff settings used when `pyproject.toml` is absent or incomplete.
## mypy (strict mode)
```ini
[mypy]
python_version = 3.13
strict = true
warn_return_any = true
warn_unused_ignores = true
warn_redundant_casts = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_any_generics = true
no_implicit_reexport = true
```
## ruff
```toml
[tool.ruff]
target-version = "py313"
line-length = 88
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"I", # isort
"RUF", # ruff-specific
"ANN", # flake8-annotations (public API must be typed)
]
ignore = [
"ANN101", # missing type for self
"ANN102", # missing type for cls
]
```
## pyproject.toml Priority Rules
When `pyproject.toml` exists, always prefer its values over these defaults:
| Config key | Behaviour |
|---|---|
| `[tool.mypy] strict = false` | Still enforce strict — mention the deviation to the user |
| `[tool.mypy] python_version` | Use as-is; warn if < 3.13 |
| `[tool.ruff.lint] ignore` | Respect fully — do not flag ignored rules |
| `[tool.ruff] line-length` | Use for E501; default 88 if absent |
| `[tool.ruff.lint] select` | Merge with defaults — do not drop user selections |
@@ -0,0 +1,97 @@
# Fix Application Rules
Rules the agent must follow when applying accepted review fixes.
## General Rules
1. **One file at a time** — edit files sequentially, not in parallel, to avoid conflicts
2. **Minimal diff** — change only what is needed; do not reformat unrelated code
3. **Preserve comments** — keep existing comments unless they are part of the fix
4. **No behaviour changes** — fixes must not alter runtime behaviour unless the original was a bug
5. **Add imports sparingly** — only add imports that are strictly required by the fix
## Type Annotation Fixes
- Add annotations using Python 3.13+ syntax: `X | None` not `Optional[X]`
- Use `list[str]` not `List[str]`, `dict[str, int]` not `Dict[str, int]`
- For functions with no return: annotate as `-> None`
- For `*args` / `**kwargs`: annotate properly, e.g. `*args: str`, `**kwargs: int`
- Do not add `from __future__ import annotations` — use native syntax directly
## mypy `# type: ignore` Usage
- Never add bare `# type: ignore` — always use specific code: `# type: ignore[assignment]`
- Only use `type: ignore` as last resort when fixing the root cause is not feasible
- Add a comment explaining why: `# type: ignore[misc] # third-party stub incomplete`
## ruff Auto-fix Rules
- Run `ruff check --fix <file>` only for safe auto-fixable rules (F401, UP*, I*)
- Do NOT use `--unsafe-fixes` without explicit user confirmation
- After auto-fix, re-read the file to verify no logic was altered
## Mutable Default Arguments
```python
# Before
def process(items: list[str] = []) -> None: ...
# After
def process(items: list[str] | None = None) -> None:
if items is None:
items = []
```
## Union Type Modernization (Python 3.13+)
```python
# Before
from typing import Optional, Union
def f(x: Optional[str]) -> Union[int, str]: ...
# After
def f(x: str | None) -> int | str: ...
```
Remove now-unused `Optional` / `Union` imports after rewriting.
## TypeAlias Modernization (PEP 695)
```python
# Before
from typing import TypeAlias
Vector: TypeAlias = list[float]
# After
type Vector = list[float]
```
## Broad Exception Handling
```python
# Before
try:
...
except Exception:
pass
# After — option A: log and re-raise
except Exception:
logger.exception("Unexpected error")
raise
# After — option B: specific exception (preferred when possible)
except ValueError as exc:
logger.warning("Invalid value: %s", exc)
```
## Post-Fix Validation Sequence
After applying all accepted fixes to a file:
1. `mypy --strict <file>` — must return exit code 0
2. `ruff check <file>` — must return exit code 0
3. If any new violations appear that were NOT in the original report — fix them silently and note in the summary
4. Report final status:
- ✅ Clean — zero violations remaining
- ⚠️ Remaining — list unresolved items with reason
@@ -0,0 +1,73 @@
# Review Checklist
Full criteria for categorizing findings in the Python review skill.
## 🔴 Must Fix — Correctness & Type Safety
These block merge. Apply by default.
### mypy strict violations
- `error:` any mypy error (all are must-fix under strict mode)
- Implicit `Any` — untyped function parameters or return values
- `type: ignore` without a specific error code (e.g. bare `# type: ignore`)
- Incompatible types in assignment or return
- Missing `--strict` compatibility: missing `py.typed`, missing stubs
### Logic & Security (manual review)
- Mutable default arguments: `def f(x: list = [])` → use `None` + guard
- Shadowing built-ins: `list`, `dict`, `id`, `type`, `input`, etc.
- Uncaught exceptions from external I/O without explicit handling
- SQL/shell injection risks (string formatting into queries/commands)
- Hardcoded secrets or credentials
- `assert` used for runtime validation (stripped with `-O`)
---
## 🟡 Should Fix — Code Quality
These hurt maintainability. Fix unless explicitly skipped.
### ruff violations (common)
- `E501` — line too long (respect `line-length` from pyproject.toml)
- `F401` — unused import
- `F841` — local variable assigned but never used
- `B006` — mutable argument default
- `B007` — loop variable unused
- `UP` prefix — pyupgrade rules (use modern Python syntax)
- `SIM` prefix — simplifiable conditions
- `RUF` prefix — Ruff-specific best practices
### Manual quality
- Missing return type annotation on public functions/methods
- Missing docstring on public API (classes, public methods, module)
- `pass` in `except` block without a comment explaining why
- `print()` left in production code (use `logging`)
- Overly broad `except Exception` without re-raise or specific handling
- Dead code: unreachable branches, commented-out code blocks
---
## 🟢 Consider — Optimization & Modern Python 3.13+
Suggestions only. Offer but don't apply without confirmation.
### Python 3.13+ idioms
- Use `type X = ...` (PEP 695) instead of `TypeAlias`
- Use `X | Y` union syntax instead of `Union[X, Y]` or `Optional[X]`
- Use `TypeVar` with `type` statement (PEP 695) instead of old `TypeVar()`
- `@override` decorator (PEP 698) for overriding methods
- `tomllib` (stdlib) instead of third-party TOML parsers
- `pathlib.Path` instead of `os.path` string operations
### Performance
- Generator expressions instead of list comprehensions when result is iterated once
- `__slots__` on data-heavy classes to reduce memory
- `functools.cache` / `functools.lru_cache` for pure deterministic functions
- Avoid repeated attribute lookup in tight loops (`local = self.attr`)
- Prefer `collections.deque` over `list` for O(1) pop from front
### Readability
- Replace long `if/elif` chains with `match/case` (structural pattern matching)
- Extract deeply nested logic into named helper functions
- Use `dataclasses.dataclass` or `pydantic.BaseModel` for plain data containers
- Named tuples or `TypedDict` instead of untyped `dict` returns