Skip to content

""" Exception Handling Best Practices for EvoSpikeNet

This document is part of the EvoSpikeNet project. Define best practices and guidelines for exception handling. """ 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] For the latest implementation status, please refer to Functional Implementation Status (Remaining Functionality).

1. Using custom exceptions

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

❌ Bad example: Generic Exception

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

✅ Good example: specific custom exceptions

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. Avoiding Bare Exceptions

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

❌ Bad example: Bare except (catch all)

try: data = json.loads(response_text) except: # Also catches KeyboardInterrupt and SystemExit data = {}

✅ Good example: specifying a specific exception type

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

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

3. Using exception chains

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

❌ Bad example: Losing the original exception

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 exception Exception: pass model = torch.load(path, **load_kwargs) exception Exception: raise ModelLoadError("Failed to load model") # original error information is lost

✅ Good example: using exception chaining (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 exception 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 # keep the original exception

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

4. Unified logging

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

❌ Bad example: print statements and inconsistent logs

try: result = process_data(data) except Exception as e: print(f"Error: {e}") # print is deprecated logging.error(str(e)) # exc_info is missing

✅ Good example: structured logging and 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. Resource management with context manager

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

❌ Bad example: Manual cleanup

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

✅ Good example: context manager

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

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

6. Rethrowing the exception

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

❌ Bad example: suppressing exceptions

try: critical_operation() exception Exception: pass # errors are completely ignored

✅ Good example: Process properly and resend

try: critical_operation() except CommunicationError as e: logger.error(f"Communication failed: {e}") # Cleanup process cleanup() # Resend and tell the caller raise

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

7. Handling multiple exception types

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

❌ Bad example: Extensive Exception catch

try: result = api_call() exception Exception as e: # catch everything too much logger.error(f"API failed: {e}")

✅ Good example: Handle specific exceptions individually

try: result = api_call() except ev_exceptions.APIConnectionError as e: log_exception(logger, e, "Failed to connect to API") # Reconnection logic retry_connection() except ev_exceptions.APITimeoutError as e: log_exception(logger, e, "API request timed out") # Timeout processing handle_timeout() except ev_exceptions.APIResponseError as e: log_exception(logger, e, "Invalid API response") # Response error handling handle_invalid_response()

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

8. Early return in validation

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

❌ Bad example: deep nesting

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

✅ Good example: early returns and guard clauses

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. Exception handling in asynchronous code

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

✅ Good example: exception handling with 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. Common error handling using decorators

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

from functools import wraps

def handle_errors(default_return=None, log_level="error"): """Error handling decorator""" logger = get_logger(name)

def decorator(func): @wraps(func) def wrapper(args, kwargs): try: return func(args, **kwargs) except ev_exceptions.EvoSpikeNetError as e: # Project-specific errors log_exception( logger, e, f"EvoSpikeNet error in {func.name}", level=getattr(logging, log_level.upper()) ) return default_return except Exception as e: # unexpected error log_exception( logger, e, f"Unexpected error in {func.name}", level=logging.ERROR ) raise # Unexpected errors are reraised

    return wrapper

return decorator

Usage example

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

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

Checklist

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

""" Exception handling checklist:

□ Are you using Bare except (except:)? □ Are you avoiding extensive Exception catches? □ Are you using custom exception classes? □ Are you using exception chaining (from)? □ Do you include enough context information in your logs? □ Are stack traces recorded with exc_info=True? □ Are resources managed by a context manager? □ Are you unnecessarily suppressing exceptions? □ Are errors in the destructor (del) handled appropriately? □ Are you using the appropriate exception types in your asynchronous code? """