py-code-health について
py-code-healthは、Pythonコードベースの保守を支援し、未使用コードや重複を見つけて整理・統合するスキルです。vultureとpylintを使い、誤検出の確認と変更後のテスト実行を重視します。
主な機能
- 01vultureによる未使用コード検出
- 02信頼度による結果の絞り込みと並べ替え
- 03pylintによる重複コード検出
- 04重複ロジックの統合と関数のパラメーター化
- 05ホワイトリストによる誤検出への対応
- 06コメントアウトコードの削除とテスト検証
活用シーン
- 01未使用と確認された関数やインポートの削除
- 02複数ファイルにまたがる重複コードの発見
- 03類似ロジックのリファクタリングによる重複削減
- 04コード削除後のテストとカバレッジの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ l-mb/ python-refactoring-skills --skill py-code-healthインストール後、Claude Code で/py-code-healthを入力して読み込まれたか確認します。
詳細
Python コードの健全性の維持
不要なコードを削除し、重複を統合して、コードベースをクリーンで保守しやすい状態に保ちます。
目標
- 未使用のコード(関数、クラス、変数)を検出する
- 確度の高い不要なコードを見つけて削除する
- ファイル間の重複コードを特定する
- 類似したコードをパラメーター化された関数に統合する
- 不要なインポートとコメントアウトされたコードを削除する
必須ツール
[dependency-groups] の dev に追加: "vulture", "pylint"
- vulture:AST ベースの不要なコードの検出
- pylint:重複コードの検出
権限:最初に py-quality-setup を実行し、必要なすべてのツール権限を .claude/settings.local.json に設定してください。
不要なコードの検出
未使用のコードを見つける
# Using vulture (primary tool)
vulture . # Find all unused code
vulture . --min-confidence 80 # High confidence only
vulture . --min-confidence 80 --sort-by-size # Largest dead code first
vulture . --exclude=tests/,venv/,.venv/ # Exclude directories
# Generate report
vulture . --min-confidence 80 > dead_code_report.txt
Vulture の出力を解釈する
unused function 'calculate_tax' (80% confidence)
src/billing.py:45
unused class 'LegacyProcessor' (90% confidence)
src/processors.py:123
unused variable 'debug_mode' (60% confidence)
src/config.py:12
確度レベル:
- 60-70%:誤検出の可能性があります(動的インポート、メタクラスなど)
- 80-89%:未使用の可能性が高いため、削除前に確認してください
- 90-100%:ほぼ確実に未使用です
誤検出への対処
Vulture は実際には使用されているコードを警告する場合があります。
1. 動的インポート/呼び出し:
# Flagged as unused but called dynamically
def plugin_handler():
pass
# Called via: getattr(module, 'plugin_handler')()
2. フレームワークの規約:
# Django models - fields used by ORM
class User(models.Model):
email = models.EmailField() # May be flagged but used by Django
3. 公開 API:
# Part of library's public API, used by external code
def public_function():
pass
解決策:
- ホワイトリストファイルに追加します:
vulture . whitelist.py - コメントを追加します:
# pragma: no coverまたはカスタムマーカー - 公開 API の誤検出は許容します
- 使用されるべきなのに、使用されていないコード:
たとえば、定数、マジックナンバー、型定義、さらには関数が未使用に見えることがあります。しかし、実際にはこれが問題の可能性があります!(部分的なリファクタリングなど、ほかの原因も考慮してください。)
不要に見えるコードを削除する前に、それを参照すべき類似のパターンがないか確認してください。該当するパターンがある場合は、呼び出し箇所や使用箇所を修正してください。
不要なコードを削除する
# BEFORE - Unused functions cluttering codebase
def old_calculate_price(item): # Last used 2 years ago
return item.cost * 1.1
def deprecated_handler(data): # Replaced by new_handler
pass
# AFTER - Clean, only active code remains
# (Dead code completely removed)
重複コードの検出
重複を見つける
# Using pylint (detects similar code blocks)
pylint --disable=all --enable=duplicate-code --recursive=y .
# Configure minimum similar lines (default: 4)
pylint --disable=all --enable=duplicate-code --duplicate-code-min-lines=6 --recursive=y .
# JSON output for parsing
pylint --disable=all --enable=duplicate-code --output-format=json --recursive=y . > duplication.json
Pylint の出力を解釈する
Similar lines in 2 files
src/auth.py:45-60
src/admin.py:123-138
def validate_credentials(username, password):
if not username or not password:
return False
user = db.get_user(username)
if not user:
return False
return check_password(password, user.password_hash)
統合のパターン
類似する関数をパラメーター化する
# BEFORE - Duplicate code with slight variations
def process_user(data: dict) -> User:
if not data.get("email"):
raise ValueError("Missing email")
if not data.get("name"):
raise ValueError("Missing name")
return User(email=data["email"], name=data["name"])
def process_admin(data: dict) -> Admin:
if not data.get("email"):
raise ValueError("Missing email")
if not data.get("name"):
raise ValueError("Missing name")
return Admin(email=data["email"], name=data["name"])
# AFTER - Single parametrized function
T = TypeVar('T', User, Admin)
def process_entity(data: dict, entity_class: type[T]) -> T:
if not data.get("email"):
raise ValueError("Missing email")
if not data.get("name"):
raise ValueError("Missing name")
return entity_class(email=data["email"], name=data["name"])
# Usage
user = process_entity(data, User)
admin = process_entity(data, Admin)
共通ロジックを抽出する
# BEFORE - Duplicated validation in multiple functions
def create_user(email: str, age: int) -> User:
if not email or "@" not in email:
raise ValueError("Invalid email")
if age < 0 or age > 150:
raise ValueError("Invalid age")
return User(email=email, age=age)
def update_user(user_id: int, email: str, age: int) -> User:
if not email or "@" not in email:
raise ValueError("Invalid email")
if age < 0 or age > 150:
raise ValueError("Invalid age")
user = get_user(user_id)
user.email = email
user.age = age
return user
# AFTER - Extract common validation
def validate_email(email: str) -> None:
if not email or "@" not in email:
raise ValueError("Invalid email")
def validate_age(age: int) -> None:
if age < 0 or age > 150:
raise ValueError("Invalid age")
def create_user(email: str, age: int) -> User:
validate_email(email)
validate_age(age)
return User(email=email, age=age)
def update_user(user_id: int, email: str, age: int) -> User:
validate_email(email)
validate_age(age)
user = get_user(user_id)
user.email = email
user.age = age
return user
重複ではなく設定を使用する
# BEFORE - Similar handlers with different values
def process_csv_file(path: str) -> list:
return parse_file(path, delimiter=",", encoding="utf-8", skip_header=True)
def process_tsv_file(path: str) -> list:
return parse_file(path, delimiter="\t", encoding="utf-8", skip_header=True)
def process_psv_file(path: str) -> list:
return parse_file(path, delimiter="|", encoding="utf-8", skip_header=True)
# AFTER - Configuration-driven
FILE_CONFIGS = {
"csv": {"delimiter": ",", "encoding": "utf-8", "skip_header": True},
"tsv": {"delimiter": "\t", "encoding": "utf-8", "skip_header": True},
"psv": {"delimiter": "|", "encoding": "utf-8", "skip_header": True},
}
def process_file(path: str, file_type: str) -> list:
config = FILE_CONFIGS[file_type]
return parse_file(path, **config)
コメントアウトされたコードを削除する
コメントアウトされたコードは、不要なコードの一種です。
# BEFORE - Cluttered with old code
def calculate_total(items: list) -> float:
# Old implementation - kept for reference
# total = 0
# for item in items:
# total += item.price * item.quantity
# return total
# Another old version
# return sum(item.price * item.quantity for item in items)
# Current implementation
return sum(item.total_price() for item in items)
# AFTER - Clean, rely on git history
def calculate_total(items: list) -> float:
return sum(item.total_price() for item in items)
理由:Git の履歴に以前の実装が保存されています。コメントアウトされたコードはノイズになります。
検証チェックリスト
-
vulture . --min-confidence 80で不要なコードが報告されない(または許容する誤検出のみが報告される) -
pylint --disable=all --enable=duplicate-codeで 6 行を超える重複ブロックが報告されない - コメントアウトされたコードブロックが残っていない
- 削除後にすべてのテストが成功する
- コードカバレッジが維持または向上している
- 意図的な誤検出に対応するホワイトリストファイルが作成されている(必要な場合)
例
例:コードの健全性を保つためのクリーンアップワークフロー
1. Scan: vulture . --min-confidence 80; pylint --disable=all --enable=duplicate-code --recursive=y .
2. Found: 5 unused functions (67 lines), 3 duplicate blocks (45 lines)
3. Remove: Verify unused code with Explore agent, delete confirmed dead code
4. Consolidate: Extract duplicates to shared utilities
5. Result: -112 lines total, tests pass, coverage improved to 86%
例:誤検出への対処
1. Scan: vulture . --min-confidence 80
2. Found: public_api_function flagged as unused (actually part of library API)
3. Create whitelist.py with: public_api_function # Used by external consumers
4. Re-scan: vulture . whitelist.py --min-confidence 80
5. Result: False positive suppressed, real dead code still detected
例:重複したバリデーションの統合
1. Scan: pylint --disable=all --enable=duplicate-code --recursive=y .
2. Found: Same validation logic in create_user() and update_user()
3. Extract: Create validate_user_data() helper function
4. Update: Both functions now call validate_user_data()
5. Result: -15 lines, single source of truth for validation
関連スキル
- 前提条件:py-quality-setup(ツールの設定)、py-test-quality(コード削除前のセーフティネット)
- 次のステップ:py-complexity(クリーンアップ後に複雑さを軽減する)
- 関連項目:py-security(大幅な変更の前にセキュリティを確認する)