Skip to main content

cjm-error-handling

Install

pip install cjm_error_handling

Project Structure

nbs/
├── core/ (2)
│   ├── base.ipynb    # Foundation classes for structured error handling with context propagation and dual messaging
│   └── errors.ipynb  # Concrete error classes for common failure scenarios in library ecosystems
└── utils/ (1)
    └── helpers.ipynb  # Utilities for easy adoption and incremental migration to structured error handling

Total: 3 notebooks across 2 directories

Module Dependencies

graph LR
    core_base[core.base<br/>Base Error Classes]
    core_errors[core.errors<br/>Domain-Specific Error Types]
    utils_helpers[utils.helpers<br/>Helper Utilities]

    core_errors --> core_base
    utils_helpers --> core_errors
    utils_helpers --> core_base

3 cross-module dependencies detected

CLI Reference

No CLI commands found in this project.

Module Overview

Detailed documentation for each module in the project:

Base Error Classes (base.ipynb)

Foundation classes for structured error handling with context propagation and dual messaging

Import

from cjm_error_handling.core.base import (
    ErrorSeverity,
    ErrorContext,
    BaseError
)

Classes

class ErrorSeverity(Enum):
    "Error severity levels for categorization and handling."
@dataclass
class ErrorContext:
    "Structured context information for errors."
    
    job_id: Optional[str]  # Job identifier
    plugin_id: Optional[str]  # Plugin identifier
    worker_pid: Optional[int]  # Worker process ID
    session_id: Optional[str]  # Session identifier
    user_id: Optional[str]  # User identifier
    operation: Optional[str]  # Operation being performed
    timestamp: str = field(...)  # When error occurred
    extra: Dict[str, Any] = field(...)  # Domain-specific context
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Convert context to dictionary for serialization."""
            result = asdict(self)
            # Remove None values for cleaner serialization
            return {k: v for k, v in result.items() if v is not None}
        "Convert context to dictionary for serialization."
    
    def from_dict(cls, data: Dict[str, Any]) -> 'ErrorContext':  # ErrorContext instance
            """Create ErrorContext from dictionary."""
            # Extract known fields
            known_fields = {'job_id', 'plugin_id', 'worker_pid', 'session_id',
                           'user_id', 'operation', 'timestamp'}
            kwargs = {k: v for k, v in data.items() if k in known_fields}
        "Create ErrorContext from dictionary."
class BaseError:
    def __init__(
        self,
        message: str,                              # User-friendly error message
        debug_info: Optional[str] = None,          # Optional developer details
        context: Optional[ErrorContext] = None,    # Structured error context
        severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
        is_retryable: bool = False,                # Whether error is transient/retryable
        cause: Optional[Exception] = None          # Original exception if chaining
    )
    "Base exception class with rich context and dual messaging."
    
    def __init__(
            self,
            message: str,                              # User-friendly error message
            debug_info: Optional[str] = None,          # Optional developer details
            context: Optional[ErrorContext] = None,    # Structured error context
            severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
            is_retryable: bool = False,                # Whether error is transient/retryable
            cause: Optional[Exception] = None          # Original exception if chaining
        )
        "Initialize error with message, context, and metadata."
    
    def get_user_message(self) -> str:  # User-friendly message
            """Get the user-friendly error message."""
            return self.message
    
        def get_debug_message(self) -> str:  # Debug message with details
        "Get the user-friendly error message."
    
    def get_debug_message(self) -> str:  # Debug message with details
            """Get detailed debug information."""
            parts = [self.message]
    
            if self.debug_info
        "Get detailed debug information."
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Serialize error to dictionary for transmission across process boundaries."""
            result = {
                'error_type': self.__class__.__name__,
        "Serialize error to dictionary for transmission across process boundaries."
    
    def from_dict(cls, data: Dict[str, Any]) -> 'BaseError':  # Reconstructed error
            """Reconstruct error from dictionary representation."""
            # Reconstruct context
            context = ErrorContext.from_dict(data.get('context', {}))
    
            # Reconstruct severity
            severity_str = data.get('severity', 'error')
            severity = ErrorSeverity(severity_str)
    
            # Note: We can't reconstruct the original cause exception,
        "Reconstruct error from dictionary representation."

Domain-Specific Error Types (errors.ipynb)

Concrete error classes for common failure scenarios in library ecosystems

Import

from cjm_error_handling.core.errors import (
    ValidationError,
    ConfigurationError,
    ResourceError,
    PluginError,
    WorkerError
)

Classes

class ValidationError:
    def __init__(
        self,
        message: str,                              # User-friendly error message
        debug_info: Optional[str] = None,          # Optional developer details
        context: Optional[ErrorContext] = None,    # Structured error context
        severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
        is_retryable: bool = False,                # Validation errors typically need fixes, not retries
        cause: Optional[Exception] = None,         # Original exception if chaining
        validation_errors: Optional[Dict[str, Any]] = None  # Structured validation details
    )
    "Raised when validation fails."
    
    def __init__(
            self,
            message: str,                              # User-friendly error message
            debug_info: Optional[str] = None,          # Optional developer details
            context: Optional[ErrorContext] = None,    # Structured error context
            severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
            is_retryable: bool = False,                # Validation errors typically need fixes, not retries
            cause: Optional[Exception] = None,         # Original exception if chaining
            validation_errors: Optional[Dict[str, Any]] = None  # Structured validation details
        )
        "Initialize validation error with optional structured validation details."
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Serialize error including validation details."""
            result = super().to_dict()
            if self.validation_errors
        "Serialize error including validation details."
class ConfigurationError:
    def __init__(
        self,
        message: str,                              # User-friendly error message
        debug_info: Optional[str] = None,          # Optional developer details
        context: Optional[ErrorContext] = None,    # Structured error context
        severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
        is_retryable: bool = False,                # Config issues usually need manual fixes
        cause: Optional[Exception] = None,         # Original exception if chaining
        config_path: Optional[str] = None          # Path to problematic config file
    )
    "Raised when configuration operations fail."
    
    def __init__(
            self,
            message: str,                              # User-friendly error message
            debug_info: Optional[str] = None,          # Optional developer details
            context: Optional[ErrorContext] = None,    # Structured error context
            severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
            is_retryable: bool = False,                # Config issues usually need manual fixes
            cause: Optional[Exception] = None,         # Original exception if chaining
            config_path: Optional[str] = None          # Path to problematic config file
        )
        "Initialize configuration error with optional config file path."
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Serialize error including config path."""
            result = super().to_dict()
            if self.config_path
        "Serialize error including config path."
class ResourceError:
    def __init__(
        self,
        message: str,                              # User-friendly error message
        debug_info: Optional[str] = None,          # Optional developer details
        context: Optional[ErrorContext] = None,    # Structured error context
        severity: ErrorSeverity = ErrorSeverity.WARNING,  # Often transient
        is_retryable: bool = True,                 # Resource conflicts may be temporary
        cause: Optional[Exception] = None,         # Original exception if chaining
        resource_type: Optional[str] = None,       # "GPU", "Memory", "Disk", etc.
        suggested_action: Optional[str] = None     # Guidance for resolution
    )
    "Raised when resource conflicts or unavailability prevent operation."
    
    def __init__(
            self,
            message: str,                              # User-friendly error message
            debug_info: Optional[str] = None,          # Optional developer details
            context: Optional[ErrorContext] = None,    # Structured error context
            severity: ErrorSeverity = ErrorSeverity.WARNING,  # Often transient
            is_retryable: bool = True,                 # Resource conflicts may be temporary
            cause: Optional[Exception] = None,         # Original exception if chaining
            resource_type: Optional[str] = None,       # "GPU", "Memory", "Disk", etc.
            suggested_action: Optional[str] = None     # Guidance for resolution
        )
        "Initialize resource error with resource type and suggested action."
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Serialize error including resource type and suggested action."""
            result = super().to_dict()
            if self.resource_type
        "Serialize error including resource type and suggested action."
class PluginError:
    def __init__(
        self,
        message: str,                              # User-friendly error message
        debug_info: Optional[str] = None,          # Optional developer details
        context: Optional[ErrorContext] = None,    # Structured error context
        severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
        is_retryable: bool = False,                # Plugin errors usually need fixes
        cause: Optional[Exception] = None,         # Original exception if chaining
        plugin_id: Optional[str] = None,           # ID of problematic plugin
        plugin_name: Optional[str] = None          # Name of problematic plugin
    )
    "Raised when plugin operations fail."
    
    def __init__(
            self,
            message: str,                              # User-friendly error message
            debug_info: Optional[str] = None,          # Optional developer details
            context: Optional[ErrorContext] = None,    # Structured error context
            severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
            is_retryable: bool = False,                # Plugin errors usually need fixes
            cause: Optional[Exception] = None,         # Original exception if chaining
            plugin_id: Optional[str] = None,           # ID of problematic plugin
            plugin_name: Optional[str] = None          # Name of problematic plugin
        )
        "Initialize plugin error with plugin ID and name."
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Serialize error including plugin ID and name."""
            result = super().to_dict()
            if self.plugin_id
        "Serialize error including plugin ID and name."
class WorkerError:
    def __init__(
        self,
        message: str,                              # User-friendly error message
        debug_info: Optional[str] = None,          # Optional developer details
        context: Optional[ErrorContext] = None,    # Structured error context
        severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
        is_retryable: bool = True,                 # Worker errors may be transient
        cause: Optional[Exception] = None,         # Original exception if chaining
        worker_type: Optional[str] = None,         # "transcription", "llm", etc.
        job_id: Optional[str] = None               # Job that failed
    )
    "Raised when worker process operations fail."
    
    def __init__(
            self,
            message: str,                              # User-friendly error message
            debug_info: Optional[str] = None,          # Optional developer details
            context: Optional[ErrorContext] = None,    # Structured error context
            severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity level
            is_retryable: bool = True,                 # Worker errors may be transient
            cause: Optional[Exception] = None,         # Original exception if chaining
            worker_type: Optional[str] = None,         # "transcription", "llm", etc.
            job_id: Optional[str] = None               # Job that failed
        )
        "Initialize worker error with worker type and job ID."
    
    def to_dict(self) -> Dict[str, Any]:  # Dictionary representation
            """Serialize error including worker type and job ID."""
            result = super().to_dict()
            if self.worker_type
        "Serialize error including worker type and job ID."

Helper Utilities (helpers.ipynb)

Utilities for easy adoption and incremental migration to structured error handling

Import

from cjm_error_handling.utils.helpers import (
    error_boundary,
    with_error_handling,
    wrap_exception,
    chain_error
)

Functions

@contextmanager
def error_boundary(
    error_type: Type[BaseError] = BaseError,  # Error type to raise
    message: Optional[str] = None,  # User-facing message (uses original if None)
    context: Optional[ErrorContext] = None,  # Error context
    operation: Optional[str] = None,  # Operation name (added to context)
    job_id: Optional[str] = None,  # Job ID (added to context)
    plugin_id: Optional[str] = None,  # Plugin ID (added to context)
    worker_pid: Optional[int] = None,  # Worker PID (added to context)
    severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity
    is_retryable: Optional[bool] = None,  # Override retryable flag
    catch: tuple = (Exception,),  # Exception types to catch
    **extra_context  # Additional context fields
)
    "Context manager that catches exceptions and wraps them in structured errors."
def with_error_handling(
    error_type: Type[BaseError] = BaseError,  # Error type to raise
    message: Optional[str] = None,  # User-facing message
    operation: Optional[str] = None,  # Operation name (uses function name if None)
    severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity
    is_retryable: Optional[bool] = None,  # Override retryable flag
    catch: tuple = (Exception,),  # Exception types to catch
    context_from_args: Optional[Dict[str, str]] = None  # Map arg names to context fields, e.g. {'job_id': 'job_id'}
)
    "Decorator that wraps function errors with structured error handling."
def wrap_exception(
    exception: Exception,  # Original exception to wrap
    error_type: Type[BaseError] = BaseError,  # Error type to create
    message: Optional[str] = None,  # User-facing message (uses exception if None)
    context: Optional[ErrorContext] = None,  # Error context
    severity: ErrorSeverity = ErrorSeverity.ERROR,  # Error severity
    is_retryable: Optional[bool] = None,  # Override retryable flag
    **context_kwargs  # Additional context fields
) -> BaseError:  # Wrapped structured error
    "Wrap an existing exception in a structured error type."
def chain_error(
    base_error: BaseError,  # Original structured error
    new_message: str,  # New user-facing message
    error_type: Optional[Type[BaseError]] = None,  # New error type (uses base type if None)
    additional_context: Optional[Dict[str, Any]] = None,  # Additional context to add
    operation: Optional[str] = None,  # Update operation name
    severity: Optional[ErrorSeverity] = None  # Override severity
) -> BaseError:  # New error with chained context
    "Chain a structured error with additional context."

Metadata

Release files for cjm-error-handling 0.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cjm-error-handling 0.0.2
File Size Uploaded Provenance
cjm_error_handling-0.0.2.tar.gz 17.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for cjm-error-handling 0.0.2
File Interpreter ABI Platform
cjm_error_handling-0.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 33.6 kB

Release files / cjm_error_handling-0.0.2.tar.gz

Download URL cjm_error_handling-0.0.2.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2d1181a5b2f8e857594ab735441c1aba7e6f70dca48361e7d0fcc0d7f9548b81
BLAKE2b-256 checksum
How to use checksums
b4b9dce3b754aff60648b3f1a9d89a079bdf9d00d19cdc4c7d743df5c917659b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release files / cjm_error_handling-0.0.2-py3-none-any.whl

Download URL cjm_error_handling-0.0.2-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
419525155e47b5c2b4755341ae389cc48fe5d614b0d2bdfcc2e081d50514978a
BLAKE2b-256 checksum
How to use checksums
a8883b16545515b6962ba6a59a26df0d0d419945cf84d6cee2f5d1cac41e0697
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Security for cjm-error-handling 0.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.0.2 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page