From python-development
Detect and remove unused Python code using vulture and ruff. Covers unused imports, variables, functions, classes, and unreachable code. Framework-aware false positive handling for Django, FastAPI, pytest, click, and more. TRIGGER WHEN: cleaning up Python codebases, enforcing import hygiene, or integrating dead code checks into CI. DO NOT TRIGGER WHEN: the task is outside the specific scope of this component.
npx claudepluginhub acaprino/alfio-claude-plugins --plugin python-developmentThis skill uses the workspace's default tool permissions.
Detect and remove unused code in Python projects using vulture and ruff. Framework-aware analysis that distinguishes real dead code from convention-driven usage patterns.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Detect and remove unused code in Python projects using vulture and ruff. Framework-aware analysis that distinguishes real dead code from convention-driven usage patterns.
What it detects
Key capabilities
--min-confidence)# Using uv (recommended)
uv tool install vulture
uv tool install ruff
# Or via pip
pip install vulture ruff
# Verify installation
uv run vulture --version 2>/dev/null || vulture --version
uv run ruff --version 2>/dev/null || ruff --version
# Unused imports (F401)
ruff check [target] --select F401
# Unused variables (F841)
ruff check [target] --select F841
# Redefined unused names (F811)
ruff check [target] --select F811
# All unused-code rules together
ruff check [target] --select F401,F811,F841
# Auto-fix safe removals (imports only)
ruff check [target] --select F401 --fix
# Preview what would be fixed
ruff check [target] --select F401 --fix --diff
# Full scan with default confidence (60%)
vulture [target]
# Higher confidence -- fewer false positives (recommended)
vulture [target] --min-confidence 80
# Very high confidence -- only obvious dead code
vulture [target] --min-confidence 90
# Scan with whitelist
vulture [target] whitelist.py --min-confidence 80
# Exclude specific paths
vulture [target] --exclude "venv,__pycache__,.git,migrations"
# Sort by confidence (highest first)
vulture [target] --min-confidence 80 --sort-by-size
Group results into these categories for structured reporting:
| Category | Source | Rule/Detection |
|---|---|---|
| Unused imports | ruff | F401 |
| Unused variables | ruff | F841 |
| Redefined-unused | ruff | F811 |
| Unused functions | vulture | unused function |
| Unused classes | vulture | unused class |
| Unused properties | vulture | unused property |
| Unreachable code | vulture | unreachable code |
src/utils.py:42: unused function 'calculate_tax' (60% confidence)
src/models.py:15: unused class 'LegacyUser' (90% confidence)
src/views.py:8: unused import 'render' (90% confidence)
These patterns are NOT dead code even if vulture flags them:
Django
urls.py via string paths@receiverhandle method)@admin.registerMIDDLEWARE settingFastAPI / Flask
@app.get, @app.post, etc.Depends()@app.on_event)pytest
@pytest.fixture (used by name injection)pytest_* functions)click / typer
@cli.command()General Python
__all__ exports__init__, __str__, __repr__, and other dunder methodsgetattr / importlib dynamic access@app.task or @shared_task# whitelist.py -- tell vulture these are intentionally used
# Django views (referenced via urls.py string paths)
index_view # unused function
detail_view # unused function
# pytest fixtures
db_session # unused function
mock_client # unused function
# Celery tasks
send_notification_email # unused function
# Signal handlers
on_user_created # unused function
Generate a whitelist automatically:
# Vulture can generate a whitelist from its findings
vulture [target] --make-whitelist > whitelist.py
Then review and keep only the intentional entries.
[tool.ruff.lint]
# Enable unused-code rules
select = ["F401", "F811", "F841"]
[tool.ruff.lint.per-file-ignores]
# Allow unused imports in __init__.py (re-exports)
"__init__.py" = ["F401"]
# Allow unused imports in conftest.py (fixtures)
"conftest.py" = ["F401"]
# Allow unused imports in type stubs
"*.pyi" = ["F401"]
[tool.vulture]
min_confidence = 80
exclude = [
"venv/",
".venv/",
"__pycache__/",
"migrations/",
"node_modules/",
]
paths = ["src/", "app/"]
# Symbols that vulture flags but are used dynamically
from myapp.models import * # noqa: F403 -- re-export
Apply removals in safest-first order:
# Auto-fix with ruff
ruff check [target] --select F401 --fix
# Or remove manually via editor
Verify: python -c "import mymodule" still works.
# Review each finding
ruff check [target] --select F841
For unpacking, replace with _:
# Before
x, y, z = get_coords() # z unused
# After
x, y, _ = get_coords()
For each vulture finding with >= 80% confidence:
grep -r "function_name" across the codebase__all__ exports: is it in a public API?@receiver, @app.task, @pytest.fixture, etc.Remove code blocks after unconditional return, raise, break, or continue.
name: Dead Code Check
on:
push:
branches: [main]
pull_request:
jobs:
dead-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- name: Check unused imports
run: uvx ruff check . --select F401,F841 --output-format github
- name: Check dead code (vulture)
run: uvx vulture src/ --min-confidence 90
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.0
hooks:
- id: ruff
args: [--select, "F401,F841", --fix]
- repo: https://github.com/jendrikseipp/vulture
rev: v2.14
hooks:
- id: vulture
args: [--min-confidence, "90"]
.PHONY: dead-code
dead-code:
ruff check . --select F401,F811,F841
vulture src/ --min-confidence 80
ruff check . --select F401
Use in CI for strict import hygiene.
# Step 1: fast lint checks
ruff check . --select F401,F811,F841
# Step 2: deep analysis
vulture . --min-confidence 80 --exclude "venv,migrations,__pycache__"
# Step 3: cross-reference and filter false positives
# Scan specific packages
vulture packages/core/ --min-confidence 80
vulture packages/api/ --min-confidence 80
# Or scan all with shared whitelist
vulture packages/ whitelist.py --min-confidence 80
# Be conservative -- public API symbols may appear unused
vulture src/mylib/ --min-confidence 90
# Exclude __init__.py re-exports
ruff check src/mylib/ --select F401 --per-file-ignores "__init__.py:F401"
--min-confidence to 90--exclude for generated code, migrations, vendored code# Ensure target path is correct
vulture src/ tests/ --min-confidence 80
# Debug: list what vulture scans
vulture src/ --min-confidence 80 2>&1 | head -20
# Check if F401 is enabled
ruff check --show-settings | grep F401
# Force enable
ruff check . --select F401 --config "lint.select = ['F401']"
Ruff and vulture may flag the same import. Use ruff for imports (auto-fixable) and vulture for functions/classes (needs manual review). Deduplicate findings before presenting.