Custom Exception Design
Why Create Custom Exceptions?
Custom exceptions provide more specific error handling and improve code clarity by creating domain-specific error types.
Custom Exception Hierarchy
graph TD
A[BaseException] --> B[Exception]
B --> C[Custom Base Exception]
C --> D[Specific Custom Exception 1]
C --> E[Specific Custom Exception 2]
Basic Custom Exception Structure
class CustomBaseException(Exception):
"""Base exception for application-specific errors"""
def __init__(self, message, error_code=None):
self.message = message
self.error_code = error_code
super().__init__(self.message)
def __str__(self):
return f"Error {self.error_code}: {self.message}"
Exception Design Patterns
Pattern |
Description |
Use Case |
Base Custom Exception |
Centralized error handling |
Application-wide error management |
Specific Exceptions |
Granular error types |
Precise error identification |
Hierarchical Exceptions |
Structured error classification |
Complex error scenarios |
Advanced Custom Exception Example
class DatabaseException(CustomBaseException):
"""Base exception for database-related errors"""
pass
class ConnectionError(DatabaseException):
"""Raised when database connection fails"""
def __init__(self, connection_string):
super().__init__(
f"Failed to connect to database: {connection_string}",
error_code="DB001"
)
class QueryError(DatabaseException):
"""Raised when database query execution fails"""
def __init__(self, query, reason):
super().__init__(
f"Query execution failed: {query}. Reason: {reason}",
error_code="DB002"
)
def execute_database_query(connection, query):
try:
## Simulated database query
if not connection:
raise ConnectionError("localhost:5432/mydb")
if not query:
raise QueryError(query, "Empty query")
except DatabaseException as e:
print(f"Database Error: {e}")
## Additional error handling logic
Exception Design Guidelines
- Inherit from built-in
Exception
class
- Provide clear, informative error messages
- Include additional context when possible
- Create hierarchical exception structures
- Use meaningful error codes
LabEx Best Practices
At LabEx, we recommend designing exceptions that:
- Are specific and descriptive
- Provide context for debugging
- Follow a consistent naming convention
- Support easy error tracking and logging
Advanced Exception Techniques
- Add logging to custom exceptions
- Include traceback information
- Support serialization
- Implement custom error handling methods
Error Handling Strategy
class ValidationError(CustomBaseException):
def __init__(self, field, value):
super().__init__(
f"Invalid value for {field}: {value}",
error_code="VAL001"
)
self.field = field
self.value = value
def validate_user_input(data):
try:
if not data['email']:
raise ValidationError('email', data['email'])
if len(data['password']) < 8:
raise ValidationError('password', data['password'])
except ValidationError as e:
## Centralized error handling
print(f"Validation Failed: {e}")
## Log error, notify user, etc.