Exception Classes
Overview
The keychain library provides a comprehensive exception hierarchy for error handling. All keychain-specific exceptions inherit from the base KeychainError
class, which provides error code support and standardized error messaging.
Package: keychain.exceptions
from keychain.exceptions import (
KeychainError,
KeychainInitializationError,
KeychainValidationError,
KeychainMemoryError,
KeychainSecurityError,
KeychainNotFoundError,
KeychainPermissionError
)
Exception Hierarchy
The keychain exception hierarchy is structured as follows:
Exception
└── KeychainError (base exception)
├── KeychainInitializationError
├── KeychainValidationError
├── KeychainMemoryError
├── KeychainSecurityError
├── KeychainNotFoundError
└── KeychainPermissionError
Base Exception Class
KeychainError
class KeychainError(Exception):
"""Base exception for all keychain library errors."""
The base exception class for all keychain-specific errors. Provides error code support and standardized error messaging.
Constructor
def __init__(self, message: str, error_code: Optional[int] = None) -> None
Initialize a KeychainError.
Parameters:
-
message
(str
) - The error message -
error_code
(int
, optional) - Optional error code from the C library
Example:
# Create error with message only
error = KeychainError("Operation failed")
# Create error with message and error code
error = KeychainError("Validation failed", error_code=1)
String Representation
def __str__(self) -> str
String representation of the error.
Returns: Error message with error code if available (format: "[code] message")
Example:
error = KeychainError("Operation failed", error_code=1)
print(str(error)) # Output: [1] Operation failed
error_no_code = KeychainError("Operation failed")
print(str(error_no_code)) # Output: Operation failed
Specific Exception Classes
KeychainInitializationError
class KeychainInitializationError(KeychainError):
"""Raised when the keychain library fails to initialize."""
Raised when the keychain library fails to initialize properly. This typically occurs during library loading or when setting up the C library bindings.
Example:
try:
# Library initialization code
pass
except KeychainInitializationError as e:
print(f"Failed to initialize keychain library: {e}")
KeychainValidationError
class KeychainValidationError(KeychainError):
"""Raised when input validation fails."""
Raised when input validation fails. This includes invalid parameters, malformed data, or data that doesn’t meet the required constraints.
Example:
try:
# Validation of user input
did = DID("invalid:did:format")
except KeychainValidationError as e:
print(f"Validation error: {e}")
KeychainMemoryError
class KeychainMemoryError(KeychainError):
"""Raised when memory allocation or management fails."""
Raised when memory allocation or management operations fail. This typically indicates insufficient memory or memory corruption issues.
Example:
try:
# Memory-intensive operation
large_credential = Credential(very_large_data)
except KeychainMemoryError as e:
print(f"Memory error: {e}")
KeychainSecurityError
class KeychainSecurityError(KeychainError):
"""Raised when cryptographic operations fail."""
Raised when cryptographic operations fail. This includes signature verification failures, encryption/decryption errors, and other security-related issues.
Example:
try:
# Cryptographic operation
verified = credential.verify_signature()
except KeychainSecurityError as e:
print(f"Security error: {e}")
KeychainNotFoundError
class KeychainNotFoundError(KeychainError):
"""Raised when a requested resource is not found."""
Raised when a requested resource is not found. This includes missing entities, non-existent identifiers, or unavailable resources.
Example:
try:
# Resource lookup
contact = gateway.get_contact("nonexistent_id")
except KeychainNotFoundError as e:
print(f"Resource not found: {e}")
KeychainPermissionError
class KeychainPermissionError(KeychainError):
"""Raised when access is denied to a resource."""
Raised when access is denied to a resource. This includes insufficient permissions, authorization failures, and access control violations.
Example:
try:
# Protected operation
result = gateway.perform_admin_operation()
except KeychainPermissionError as e:
print(f"Permission denied: {e}")
Error Code Translation
translate_error_code()
def translate_error_code(error_code: int, message: str = "") -> KeychainError
Translate C library error codes to appropriate Python exceptions.
Parameters:
-
error_code
(int
) - The error code from the C library -
message
(str
, optional) - Optional error message
Returns: Appropriate KeychainError
subclass instance
Error Code Mappings:
-
1
→KeychainValidationError
-
2
→KeychainNotFoundError
-
3
→KeychainPermissionError
-
4
→KeychainMemoryError
-
5
→KeychainSecurityError
-
99
→KeychainInitializationError
-
Other codes →
KeychainError
Example:
from keychain.exceptions import translate_error_code
# Translate error code from C library
error = translate_error_code(1, "Invalid input provided")
# Returns: KeychainValidationError("[1] Invalid input provided")
# Translate without custom message
error = translate_error_code(4)
# Returns: KeychainMemoryError("[4] Operation failed with error code 4")
Example: Exception Handling
from keychain.exceptions import (
KeychainError,
KeychainValidationError,
KeychainSecurityError,
KeychainNotFoundError
)
from keychain.core.gateway import Gateway
from keychain.identity.did import DID
def safe_keychain_operation():
"""Demonstrate comprehensive exception handling."""
try:
# Initialize gateway
gateway = Gateway()
# Validate and create DID
did_string = "did:example:123456"
did = DID(did_string)
# Perform operations that might fail
contact = gateway.get_contact(did)
# Security-sensitive operation
result = contact.verify_credentials()
print("Operation completed successfully")
return result
except KeychainValidationError as e:
print(f"Validation error: {e}")
if e.error_code:
print(f"Error code: {e.error_code}")
return None
except KeychainSecurityError as e:
print(f"Security error: {e}")
# Log security issues for audit
print("Security operation failed - check credentials")
return None
except KeychainNotFoundError as e:
print(f"Resource not found: {e}")
# Handle missing resources gracefully
return None
except KeychainError as e:
print(f"General keychain error: {e}")
# Handle any other keychain-specific errors
return None
except Exception as e:
print(f"Unexpected error: {e}")
# Handle non-keychain errors
return None
# Usage
result = safe_keychain_operation()
if result:
print("Operation succeeded")
else:
print("Operation failed")
Example: Custom Error Handling
from keychain.exceptions import KeychainError, translate_error_code
from keychain.core.gateway import Gateway
class KeychainOperationManager:
"""Manager with comprehensive error handling."""
def __init__(self):
self.gateway = None
self.error_log = []
def initialize(self) -> bool:
"""Initialize with error handling."""
try:
self.gateway = Gateway()
return True
except KeychainError as e:
self._log_error("Initialization failed", e)
return False
def _log_error(self, operation: str, error: KeychainError) -> None:
"""Log error with details."""
error_entry = {
"operation": operation,
"message": error.message,
"error_code": error.error_code,
"exception_type": type(error).__name__
}
self.error_log.append(error_entry)
print(f"Error in {operation}: {error}")
def handle_c_library_error(self, error_code: int, operation: str) -> None:
"""Handle error codes from C library."""
try:
# Translate C error code to Python exception
exception = translate_error_code(error_code, f"C library error in {operation}")
raise exception
except KeychainError as e:
self._log_error(operation, e)
def safe_operation(self, operation_func, *args, **kwargs):
"""Execute operation with comprehensive error handling."""
if not self.gateway:
print("Gateway not initialized")
return None
try:
return operation_func(*args, **kwargs)
except KeychainValidationError as e:
self._log_error("Validation", e)
except KeychainSecurityError as e:
self._log_error("Security", e)
except KeychainNotFoundError as e:
self._log_error("Resource lookup", e)
except KeychainError as e:
self._log_error("General keychain operation", e)
except Exception as e:
# Convert unexpected errors to KeychainError
keychain_error = KeychainError(f"Unexpected error: {str(e)}")
self._log_error("Unexpected error", keychain_error)
return None
def get_error_summary(self) -> dict:
"""Get summary of all errors encountered."""
error_counts = {}
for error in self.error_log:
exc_type = error["exception_type"]
error_counts[exc_type] = error_counts.get(exc_type, 0) + 1
return {
"total_errors": len(self.error_log),
"error_types": error_counts,
"recent_errors": self.error_log[-5:] if self.error_log else []
}
# Usage example
def error_handling_example():
"""Demonstrate error handling manager."""
manager = KeychainOperationManager()
if not manager.initialize():
print("Failed to initialize")
return
# Example operations with error handling
result1 = manager.safe_operation(lambda: DID("invalid:format"))
result2 = manager.safe_operation(lambda: manager.gateway.get_contact("nonexistent"))
# Handle C library error
manager.handle_c_library_error(1, "validate_input")
manager.handle_c_library_error(5, "cryptographic_operation")
# Get error summary
summary = manager.get_error_summary()
print(f"Error summary: {summary}")
if __name__ == "__main__":
error_handling_example()
Example: Exception Context Management
from keychain.exceptions import KeychainError
from keychain.core.gateway import Gateway
from contextlib import contextmanager
from typing import Generator, Optional
@contextmanager
def keychain_context() -> Generator[Optional[Gateway], None, None]:
"""Context manager for keychain operations with automatic cleanup."""
gateway = None
try:
# Initialize gateway
gateway = Gateway()
yield gateway
except KeychainError as e:
print(f"Keychain error in context: {e}")
yield None
except Exception as e:
print(f"Unexpected error in context: {e}")
yield None
finally:
# Cleanup if needed
if gateway:
try:
# Perform cleanup operations
pass
except KeychainError as e:
print(f"Cleanup error: {e}")
def context_usage_example():
"""Demonstrate context manager usage."""
with keychain_context() as gateway:
if gateway is None:
print("Failed to initialize gateway")
return
try:
# Perform operations within context
persona = gateway.get_default_persona()
if persona:
print(f"Default persona: {persona}")
except KeychainError as e:
print(f"Operation failed: {e}")
if __name__ == "__main__":
context_usage_example()
Error Handling Best Practices
-
Catch Specific Exceptions: Always catch the most specific exception type first, then broader ones.
-
Use Error Codes: When available, use error codes for programmatic error handling.
-
Log Security Errors: Always log security-related errors for audit purposes.
-
Graceful Degradation: Handle errors gracefully and provide fallback behavior when possible.
-
Context Information: Include operation context in error messages for better debugging.
Example:
try:
# Risky operation
result = perform_operation()
except KeychainSecurityError as e:
# Log security error
security_logger.error(f"Security violation: {e}")
# Notify security team
notify_security_team(e)
# Graceful fallback
result = get_cached_result()
except KeychainValidationError as e:
# Handle validation errors
user_logger.info(f"User input error: {e}")
# Prompt for correct input
result = prompt_user_correction()
except KeychainError as e:
# Handle general keychain errors
system_logger.error(f"Keychain operation failed: {e}")
result = None
See Also
-
Gateway Class - Main entry point that may raise exceptions
-
Persona Class - Persona operations that may raise exceptions
-
Credential Class - Credential operations that may raise exceptions
-
DID Classes - DID operations that may raise exceptions
-
Constants and Enums - Error code constants