python-error-handling
入力バリデーション、例外階層の設計、部分的な失敗のハンドリングなど、Pythonのエラー処理パターンを提供します。バリデーションロジックの実装、例外戦略の設計、バッチ処理の失敗対応、堅牢なAPIの構築時に活用してください。
description の原文を見る
Python error handling patterns including input validation, exception hierarchies, and partial failure handling. Use when implementing validation logic, designing exception strategies, handling batch processing failures, or building robust APIs.
SKILL.md 本文
Python エラーハンドリング
適切な入力検証、意味のある例外、そして優雅な失敗処理を通じて、堅牢な Python アプリケーションを構築します。良いエラーハンドリングはデバッグを容易にし、システムをより信頼性の高いものにします。
このスキルを使用する場合
- ユーザー入力と API パラメータの検証
- アプリケーションの例外階層設計
- バッチ操作の部分的な失敗処理
- 外部データのドメイン型への変換
- ユーザーフレンドリーなエラーメッセージの構築
- フェイルファスト検証パターンの実装
コア概念
1. フェイルファスト
高コストな操作の前に入力を早期に検証します。可能な限り、すべての検証エラーを一度に報告します。
2. 意味のある例外
適切な例外型を文脈とともに使用します。メッセージは何が失敗したのか、なぜ失敗したのか、どのように修正するのかを説明する必要があります。
3. 部分的な失敗
バッチ操作では、1つの失敗がすべてを中止しないようにします。成功と失敗を別々に追跡します。
4. コンテキスト保持
デバッグのためにエラーの全体像を保持するために、例外を連鎖させます。
クイックスタート
def fetch_page(url: str, page_size: int) -> Page:
if not url:
raise ValueError("'url' is required")
if not 1 <= page_size <= 100:
raise ValueError(f"'page_size' must be 1-100, got {page_size}")
# Now safe to proceed...
基本パターン
パターン 1: 早期入力検証
処理開始前に API 境界で全ての入力を検証します。
def process_order(
order_id: str,
quantity: int,
discount_percent: float,
) -> OrderResult:
"""Process an order with validation."""
# Validate required fields
if not order_id:
raise ValueError("'order_id' is required")
# Validate ranges
if quantity <= 0:
raise ValueError(f"'quantity' must be positive, got {quantity}")
if not 0 <= discount_percent <= 100:
raise ValueError(
f"'discount_percent' must be 0-100, got {discount_percent}"
)
# Validation passed, proceed with processing
return _process_validated_order(order_id, quantity, discount_percent)
パターン 2: ドメイン型への早期変換
文字列と外部データをシステム境界でタイプ化されたドメインオブジェクトに解析します。
from enum import Enum
class OutputFormat(Enum):
JSON = "json"
CSV = "csv"
PARQUET = "parquet"
def parse_output_format(value: str) -> OutputFormat:
"""Parse string to OutputFormat enum.
Args:
value: Format string from user input.
Returns:
Validated OutputFormat enum member.
Raises:
ValueError: If format is not recognized.
"""
try:
return OutputFormat(value.lower())
except ValueError:
valid_formats = [f.value for f in OutputFormat]
raise ValueError(
f"Invalid format '{value}'. "
f"Valid options: {', '.join(valid_formats)}"
)
# Usage at API boundary
def export_data(data: list[dict], format_str: str) -> bytes:
output_format = parse_output_format(format_str) # Fail fast
# Rest of function uses typed OutputFormat
...
パターン 3: 複雑な検証に Pydantic を使用
自動エラーメッセージを備えた構造化入力検証に Pydantic モデルを使用します。
from pydantic import BaseModel, Field, field_validator
class CreateUserInput(BaseModel):
"""Input model for user creation."""
email: str = Field(..., min_length=5, max_length=255)
name: str = Field(..., min_length=1, max_length=100)
age: int = Field(ge=0, le=150)
@field_validator("email")
@classmethod
def validate_email_format(cls, v: str) -> str:
if "@" not in v or "." not in v.split("@")[-1]:
raise ValueError("Invalid email format")
return v.lower()
@field_validator("name")
@classmethod
def normalize_name(cls, v: str) -> str:
return v.strip().title()
# Usage
try:
user_input = CreateUserInput(
email="user@example.com",
name="john doe",
age=25,
)
except ValidationError as e:
# Pydantic provides detailed error information
print(e.errors())
パターン 4: エラーを標準例外にマップ
Python の組み込み例外型を適切に使用し、必要に応じてコンテキストを追加します。
| 失敗タイプ | 例外 | 例 |
|---|---|---|
| 不正な入力 | ValueError | 不正なパラメータ値 |
| 不正な型 | TypeError | 文字列が必要な場所に int を渡した |
| アイテム欠損 | KeyError | 辞書キーが見つからない |
| 操作失敗 | RuntimeError | サービス利用不可 |
| タイムアウト | TimeoutError | 操作が長すぎた |
| ファイル未検出 | FileNotFoundError | パスが存在しない |
| アクセス拒否 | PermissionError | アクセス禁止 |
# Good: Specific exception with context
raise ValueError(f"'page_size' must be 1-100, got {page_size}")
# Avoid: Generic exception, no context
raise Exception("Invalid parameter")
高度なパターン
パターン 5: コンテキスト付きカスタム例外
構造化情報を保持するドメイン固有の例外を作成します。
class ApiError(Exception):
"""Base exception for API errors."""
def __init__(
self,
message: str,
status_code: int,
response_body: str | None = None,
) -> None:
self.status_code = status_code
self.response_body = response_body
super().__init__(message)
class RateLimitError(ApiError):
"""Raised when rate limit is exceeded."""
def __init__(self, retry_after: int) -> None:
self.retry_after = retry_after
super().__init__(
f"Rate limit exceeded. Retry after {retry_after}s",
status_code=429,
)
# Usage
def handle_response(response: Response) -> dict:
match response.status_code:
case 200:
return response.json()
case 401:
raise ApiError("Invalid credentials", 401)
case 404:
raise ApiError(f"Resource not found: {response.url}", 404)
case 429:
retry_after = int(response.headers.get("Retry-After", 60))
raise RateLimitError(retry_after)
case code if 400 <= code < 500:
raise ApiError(f"Client error: {response.text}", code)
case code if code >= 500:
raise ApiError(f"Server error: {response.text}", code)
パターン 6: 例外の連鎖
再発生時に元の例外を保持して、デバッグの全体像を保持します。
import httpx
class ServiceError(Exception):
"""High-level service operation failed."""
pass
def upload_file(path: str) -> str:
"""Upload file and return URL."""
try:
with open(path, "rb") as f:
response = httpx.post("https://upload.example.com", files={"file": f})
response.raise_for_status()
return response.json()["url"]
except FileNotFoundError as e:
raise ServiceError(f"Upload failed: file not found at '{path}'") from e
except httpx.HTTPStatusError as e:
raise ServiceError(
f"Upload failed: server returned {e.response.status_code}"
) from e
except httpx.RequestError as e:
raise ServiceError(f"Upload failed: network error") from e
パターン 7: 部分的な失敗を伴うバッチ処理
1つの不正なアイテムがバッチ全体を中止しないようにします。アイテムごとに結果を追跡します。
from dataclasses import dataclass
@dataclass
class BatchResult[T]:
"""Results from batch processing."""
succeeded: dict[int, T] # index -> result
failed: dict[int, Exception] # index -> error
@property
def success_count(self) -> int:
return len(self.succeeded)
@property
def failure_count(self) -> int:
return len(self.failed)
@property
def all_succeeded(self) -> bool:
return len(self.failed) == 0
def process_batch(items: list[Item]) -> BatchResult[ProcessedItem]:
"""Process items, capturing individual failures.
Args:
items: Items to process.
Returns:
BatchResult with succeeded and failed items by index.
"""
succeeded: dict[int, ProcessedItem] = {}
failed: dict[int, Exception] = {}
for idx, item in enumerate(items):
try:
result = process_single_item(item)
succeeded[idx] = result
except Exception as e:
failed[idx] = e
return BatchResult(succeeded=succeeded, failed=failed)
# Caller handles partial results
result = process_batch(items)
if not result.all_succeeded:
logger.warning(
f"Batch completed with {result.failure_count} failures",
failed_indices=list(result.failed.keys()),
)
パターン 8: 長時間操作の進捗報告
ビジネスロジックを UI に結合することなく、バッチ進捗を可視化します。
from collections.abc import Callable
ProgressCallback = Callable[[int, int, str], None] # current, total, status
def process_large_batch(
items: list[Item],
on_progress: ProgressCallback | None = None,
) -> BatchResult:
"""Process batch with optional progress reporting.
Args:
items: Items to process.
on_progress: Optional callback receiving (current, total, status).
"""
total = len(items)
succeeded = {}
failed = {}
for idx, item in enumerate(items):
if on_progress:
on_progress(idx, total, f"Processing {item.id}")
try:
succeeded[idx] = process_single_item(item)
except Exception as e:
failed[idx] = e
if on_progress:
on_progress(total, total, "Complete")
return BatchResult(succeeded=succeeded, failed=failed)
ベストプラクティスまとめ
- 早期検証 - 高コストな操作の前に入力をチェック
- 特定の例外を使用 - 汎用的な
ExceptionではなくValueError、TypeErrorなど - コンテキストを含める - メッセージは何が、なぜ失敗したのか、どう修正するかを説明
- 境界で型変換 - 文字列を列挙型/ドメイン型に早期に解析
- 例外を連鎖させる -
raise ... from eでデバッグ情報を保持 - 部分的な失敗を処理 - 単一アイテムエラーでバッチを中止しない
- Pydantic を使用 - 構造化エラーを伴う複雑な入力検証に
- 失敗モードを文書化 - ドックストリングに可能な例外をリスト
- コンテキスト付きでログ - ID、数値、その他のデバッグ情報を含める
- エラーパスをテスト - 例外が正しく発生することを確認
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- wshobson
- リポジトリ
- wshobson/agents
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/wshobson/agents / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。