コンテンツにスキップ

""" Exception Handling Best Practices for EvoSpikeNet

このドキュメントは、EvoSpikeNetプロジェクトにおける 例外処理のベストプラクティスとガイドラインを定義します。 """ import os import json import logging import asyncio import aiohttp import torch

from evospikenet import exceptions as ev_exceptions from evospikenet.logging_utils import get_logger, log_exception

================================================================================

[!NOTE] 最新の実装状況は 機能実装ステータス (Remaining Functionality) を参照してください。

1. カスタム例外の使用

================================================================================

❌ 悪い例: 汎用的なException

def load_model(path): if not os.path.exists(path): raise Exception("File not found") # 不明瞭

✅ 良い例: 特定のカスタム例外

def load_model(path): if not os.path.exists(path): raise ev_exceptions.ModelNotFoundError( f"Model file not found: {path}", details={"path": path, "cwd": os.getcwd()} )

================================================================================

2. Bare Exceptの回避

================================================================================

❌ 悪い例: Bare except(すべてをキャッチ)

try: data = json.loads(response_text) except: # KeyboardInterruptやSystemExitもキャッチしてしまう data = {}

✅ 良い例: 特定の例外型を指定

try: data = json.loads(response_text) except (json.JSONDecodeError, ValueError) as e: logger.warning(f"Failed to parse JSON: {e}") data = {}

================================================================================

3. 例外チェーンの使用

================================================================================

❌ 悪い例: 元の例外を失う

try: import inspect load_kwargs = {"map_location": "cpu"} try: sig = inspect.signature(torch.load) if "weights_only" in sig.parameters: load_kwargs["weights_only"] = True except Exception: pass model = torch.load(path, **load_kwargs) except Exception: raise ModelLoadError("Failed to load model") # 元のエラー情報が失われる

✅ 良い例: 例外チェーン(from)を使用

try: import inspect load_kwargs = {"map_location": "cpu"} try: sig = inspect.signature(torch.load) if "weights_only" in sig.parameters: load_kwargs["weights_only"] = True except Exception: pass model = torch.load(path, **load_kwargs) except (FileNotFoundError, RuntimeError) as e: raise ev_exceptions.ModelLoadError( f"Failed to load model from {path}", details={"path": path} ) from e # 元の例外を保持

================================================================================

4. ログ記録の統一

================================================================================

❌ 悪い例: print文や不統一なログ

try: result = process_data(data) except Exception as e: print(f"Error: {e}") # printは推奨されない logging.error(str(e)) # exc_infoがない

✅ 良い例: 構造化ログとexc_info

logger = get_logger(name)

try: result = process_data(data) except ev_exceptions.DataValidationError as e: log_exception( logger, e, message="Data validation failed", extra_context={"data_size": len(data)} ) raise

================================================================================

5. コンテキストマネージャーでのリソース管理

================================================================================

❌ 悪い例: 手動でクリーンアップ

file = None try: file = open("data.txt") process(file) except Exception as e: logger.error(f"Error: {e}") finally: if file: file.close()

✅ 良い例: コンテキストマネージャー

try: with open("data.txt") as file: process(file) except IOError as e: log_exception(logger, e, "Failed to process file")

================================================================================

6. 例外の再送出

================================================================================

❌ 悪い例: 例外を握りつぶす

try: critical_operation() except Exception: pass # エラーが完全に無視される

✅ 良い例: 適切に処理してから再送出

try: critical_operation() except CommunicationError as e: logger.error(f"Communication failed: {e}") # クリーンアップ処理 cleanup() # 再送出して呼び出し元に伝える raise

================================================================================

7. 複数の例外型のハンドリング

================================================================================

❌ 悪い例: 広範なException catch

try: result = api_call() except Exception as e: # すべてをキャッチしすぎ logger.error(f"API failed: {e}")

✅ 良い例: 特定の例外を個別に処理

try: result = api_call() except ev_exceptions.APIConnectionError as e: log_exception(logger, e, "Failed to connect to API") # 再接続ロジック retry_connection() except ev_exceptions.APITimeoutError as e: log_exception(logger, e, "API request timed out") # タイムアウト処理 handle_timeout() except ev_exceptions.APIResponseError as e: log_exception(logger, e, "Invalid API response") # レスポンスエラー処理 handle_invalid_response()

================================================================================

8. バリデーションでのアーリーリターン

================================================================================

❌ 悪い例: ネストが深い

def process_data(data): try: if data: if isinstance(data, dict): if "key" in data: return data["key"] except Exception: return None

✅ 良い例: アーリーリターンとガード節

def extract_key_from_data(data): if not isinstance(data, dict): raise ev_exceptions.DataValidationError( f"Expected dict, got {type(data).name}" )

if "key" not in data:
    raise ev_exceptions.DataValidationError("Missing required key: 'key'")

return data["key"]

================================================================================

9. 非同期コードでの例外処理

================================================================================

✅ 良い例: async/awaitでの例外処理

async def fetch_data(url: str, timeout: float = 10.0): try: async with aiohttp.ClientSession() as session: async with session.get(url, timeout=timeout) as response: return await response.json() except asyncio.TimeoutError as e: raise ev_exceptions.EvoTimeoutError( f"Request to {url} timed out after {timeout}s", details={"url": url, "timeout": timeout} ) from e except aiohttp.ClientError as e: raise ev_exceptions.APIConnectionError( f"Failed to connect to {url}", details={"url": url} ) from e

================================================================================

10. デコレーターを使った共通エラーハンドリング

================================================================================

from functools import wraps

def handle_errors(default_return=None, log_level="error"): """エラーハンドリングデコレーター""" logger = get_logger(name)

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except ev_exceptions.EvoSpikeNetError as e:
            # プロジェクト固有のエラー
            log_exception(
                logger,
                e,
                f"EvoSpikeNet error in {func.__name__}",
                level=getattr(logging, log_level.upper())
            )
            return default_return
        except Exception as e:
            # 予期しないエラー
            log_exception(
                logger,
                e,
                f"Unexpected error in {func.__name__}",
                level=logging.ERROR
            )
            raise  # 予期しないエラーは再送出

    return wrapper

return decorator

使用例

@handle_errors(default_return=[], log_level="warning") def get_user_data(user_id: int): # 実装 pass

================================================================================

チェックリスト

================================================================================

""" 例外処理のチェックリスト:

□ Bare except(except:)を使用していないか? □ 広範な Exception catch を避けているか? □ カスタム例外クラスを使用しているか? □ 例外チェーン(from)を使用しているか? □ ログに十分なコンテキスト情報を含めているか? □ exc_info=True でスタックトレースを記録しているか? □ リソースはコンテキストマネージャーで管理しているか? □ 例外を不必要に握りつぶしていないか? □ デストラクタ(del)内のエラーを適切に処理しているか? □ 非同期コードで適切な例外型を使用しているか? """