CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-jmcomic

Python API for accessing and downloading content from JMComic with Cloudflare bypass and plugin system.

Pending
Overview
Eval results
Files

exception-handling.mddocs/

Exception Handling

Comprehensive exception hierarchy with context support for robust error handling and debugging. Provides specific error types for different failure scenarios with detailed context information and recovery suggestions.

Types

from typing import Dict, Any, List, Optional, Union

Capabilities

Base Exception Class

Foundation exception class with context support and enhanced error information.

class JmcomicException(Exception):
    """
    Base exception class with context support and enhanced error information.
    
    All JMComic exceptions inherit from this base class, providing
    consistent error handling with context information and debugging support.
    
    Attributes:
    - message: str - Error message
    - context: Dict[str, Any] - Additional context information
    - inner_exception: Exception - Original exception if wrapped
    - error_code: str - Unique error code for categorization
    
    Methods:
    - add_context(key, value): Add context information
    - get_context(key, default=None): Get context value
    - has_context(key): Check if context key exists
    - get_full_message(): Get message with context
    - to_dict(): Convert exception to dictionary
    """
    
    def __init__(self, message: str, context: Dict[str, Any] = None, 
                 inner_exception: Exception = None):
        """
        Initialize exception with message and optional context.
        
        Parameters:
        - message: str - Error message
        - context: dict, optional - Additional context information
        - inner_exception: Exception, optional - Original exception
        """
        super().__init__(message)
        self.message = message
        self.context = context or {}
        self.inner_exception = inner_exception
        self.error_code = self.__class__.__name__
    
    def add_context(self, key: str, value: Any):
        """
        Add context information to the exception.
        
        Parameters:
        - key: str - Context key
        - value: Any - Context value
        """
        self.context[key] = value
    
    def get_context(self, key: str, default: Any = None) -> Any:
        """
        Get context value by key.
        
        Parameters:
        - key: str - Context key
        - default: Any - Default value if key not found
        
        Returns:
        Any - Context value or default
        """
        return self.context.get(key, default)
    
    def get_full_message(self) -> str:
        """
        Get complete error message including context.
        
        Returns:
        str - Full error message with context information
        """
        if not self.context:
            return self.message
        
        context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
        return f"{self.message} (Context: {context_str})"
    
    def to_dict(self) -> Dict[str, Any]:
        """
        Convert exception to dictionary representation.
        
        Returns:
        dict - Exception data as dictionary
        """
        return {
            'error_code': self.error_code,
            'message': self.message,
            'context': self.context,
            'inner_exception': str(self.inner_exception) if self.inner_exception else None
        }

HTTP and Network Exceptions

Exceptions related to HTTP requests, responses, and network communication.

class ResponseUnexpectedException(JmcomicException):
    """
    Exception for HTTP response errors and unexpected status codes.
    
    Raised when HTTP requests return unexpected status codes or
    response content that doesn't match expected format.
    
    Attributes:
    - status_code: int - HTTP status code
    - response_url: str - URL that caused the error
    - response_headers: Dict[str, str] - Response headers
    - response_content: str - Response content (truncated)
    """
    
    def __init__(self, message: str, status_code: int = None, 
                 response_url: str = None, response_content: str = None):
        """
        Initialize HTTP response exception.
        
        Parameters:
        - message: str - Error message
        - status_code: int, optional - HTTP status code
        - response_url: str, optional - URL that failed
        - response_content: str, optional - Response content
        """
        context = {}
        if status_code is not None:
            context['status_code'] = status_code
        if response_url:
            context['url'] = response_url
        if response_content:
            context['content_preview'] = response_content[:500]
        
        super().__init__(message, context)
        self.status_code = status_code
        self.response_url = response_url
        self.response_content = response_content

class RequestRetryAllFailException(JmcomicException):
    """
    Exception when all retry attempts have been exhausted.
    
    Raised when multiple retry attempts fail and no more retries
    are available according to the configuration.
    
    Attributes:
    - retry_count: int - Number of retry attempts made
    - last_exception: Exception - Last exception encountered
    - failed_attempts: List[Exception] - All failed attempts
    """
    
    def __init__(self, message: str, retry_count: int, 
                 failed_attempts: List[Exception]):
        """
        Initialize retry exhaustion exception.
        
        Parameters:
        - message: str - Error message
        - retry_count: int - Number of retries attempted
        - failed_attempts: List[Exception] - All failed attempts
        """
        context = {
            'retry_count': retry_count,
            'attempt_count': len(failed_attempts)
        }
        super().__init__(message, context)
        self.retry_count = retry_count
        self.failed_attempts = failed_attempts
        self.last_exception = failed_attempts[-1] if failed_attempts else None

Data Processing Exceptions

Exceptions related to data parsing, validation, and processing operations.

class RegularNotMatchException(JmcomicException):
    """
    Exception for HTML parsing and regex matching failures.
    
    Raised when regular expressions fail to match expected patterns
    in HTML content or when required data cannot be extracted.
    
    Attributes:
    - pattern: str - Regex pattern that failed to match
    - content_sample: str - Sample of content that failed to match
    """
    
    def __init__(self, message: str, pattern: str = None, content: str = None):
        """
        Initialize regex matching exception.
        
        Parameters:
        - message: str - Error message
        - pattern: str, optional - Regex pattern that failed
        - content: str, optional - Content that failed to match
        """
        context = {}
        if pattern:
            context['pattern'] = pattern
        if content:
            context['content_sample'] = content[:200]
        
        super().__init__(message, context)
        self.pattern = pattern
        self.content_sample = content

class JsonResolveFailException(JmcomicException):
    """
    Exception for JSON parsing and validation errors.
    
    Raised when JSON content cannot be parsed or doesn't match
    the expected structure for API responses.
    
    Attributes:
    - json_content: str - JSON content that failed to parse
    - parse_error: str - Specific parsing error message
    """
    
    def __init__(self, message: str, json_content: str = None, 
                 parse_error: str = None):
        """
        Initialize JSON parsing exception.
        
        Parameters:
        - message: str - Error message
        - json_content: str, optional - JSON that failed to parse
        - parse_error: str, optional - Specific parsing error
        """
        context = {}
        if json_content:
            context['json_preview'] = json_content[:300]
        if parse_error:
            context['parse_error'] = parse_error
        
        super().__init__(message, context)
        self.json_content = json_content
        self.parse_error = parse_error

Content Availability Exceptions

Exceptions related to missing or unavailable content.

class MissingAlbumPhotoException(JmcomicException):
    """
    Exception for content not found or unavailable errors.
    
    Raised when requested albums, photos, or images cannot be found
    or are no longer available on the platform.
    
    Attributes:
    - content_type: str - Type of missing content ('album', 'photo', 'image')
    - content_id: str - ID of the missing content
    - availability_status: str - Reason for unavailability
    """
    
    def __init__(self, message: str, content_type: str = None, 
                 content_id: str = None, availability_status: str = None):
        """
        Initialize missing content exception.
        
        Parameters:
        - message: str - Error message
        - content_type: str, optional - Type of missing content
        - content_id: str, optional - ID of missing content
        - availability_status: str, optional - Reason for unavailability
        """
        context = {}
        if content_type:
            context['content_type'] = content_type
        if content_id:
            context['content_id'] = content_id
        if availability_status:
            context['availability_status'] = availability_status
        
        super().__init__(message, context)
        self.content_type = content_type
        self.content_id = content_id
        self.availability_status = availability_status

Download Operation Exceptions

Exceptions specific to download operations and partial failures.

class PartialDownloadFailedException(JmcomicException):
    """
    Exception for partial download failures.
    
    Raised when some parts of a download operation fail while others
    succeed. Contains information about successful and failed operations.
    
    Attributes:
    - successful_downloads: List[str] - IDs of successful downloads
    - failed_downloads: List[str] - IDs of failed downloads
    - failure_details: Dict[str, Exception] - Detailed failure information
    - success_rate: float - Percentage of successful downloads
    """
    
    def __init__(self, message: str, successful_downloads: List[str] = None,
                 failed_downloads: List[str] = None, 
                 failure_details: Dict[str, Exception] = None):
        """
        Initialize partial download failure exception.
        
        Parameters:
        - message: str - Error message
        - successful_downloads: list, optional - Successful download IDs
        - failed_downloads: list, optional - Failed download IDs
        - failure_details: dict, optional - Detailed failure information
        """
        successful_downloads = successful_downloads or []
        failed_downloads = failed_downloads or []
        total = len(successful_downloads) + len(failed_downloads)
        success_rate = (len(successful_downloads) / total * 100) if total > 0 else 0
        
        context = {
            'successful_count': len(successful_downloads),
            'failed_count': len(failed_downloads),
            'success_rate': f"{success_rate:.1f}%"
        }
        
        super().__init__(message, context)
        self.successful_downloads = successful_downloads
        self.failed_downloads = failed_downloads
        self.failure_details = failure_details or {}
        self.success_rate = success_rate

Exception Utilities

Utility class for exception creation, context management, and error handling.

class ExceptionTool:
    """
    Exception creation and context management utilities.
    
    Provides helper functions for creating exceptions with context,
    validating conditions, and managing error scenarios.
    
    Static Methods:
    - require_true(condition, message, exception_class=None): Assert condition
    - require_not_none(value, message): Assert value is not None
    - require_not_empty(collection, message): Assert collection not empty
    - wrap_exception(exception, message, context=None): Wrap existing exception
    - create_with_context(exception_class, message, **context): Create with context
    """
    
    @staticmethod
    def require_true(condition: bool, message: str, 
                    exception_class: type = None) -> None:
        """
        Assert that condition is true, raise exception if false.
        
        Parameters:
        - condition: bool - Condition to check
        - message: str - Error message if condition fails
        - exception_class: type, optional - Exception class to raise
        
        Raises:
        JmcomicException or specified exception - If condition is false
        """
        if not condition:
            exc_class = exception_class or JmcomicException
            raise exc_class(message)
    
    @staticmethod
    def require_not_none(value: Any, message: str) -> None:
        """
        Assert that value is not None.
        
        Parameters:
        - value: Any - Value to check
        - message: str - Error message if None
        
        Raises:
        JmcomicException - If value is None
        """
        ExceptionTool.require_true(value is not None, message)
    
    @staticmethod
    def require_not_empty(collection: Union[List, Dict, str], message: str) -> None:
        """
        Assert that collection is not empty.
        
        Parameters:
        - collection: list/dict/str - Collection to check
        - message: str - Error message if empty
        
        Raises:
        JmcomicException - If collection is empty
        """
        ExceptionTool.require_true(len(collection) > 0, message)
    
    @staticmethod
    def wrap_exception(exception: Exception, message: str, 
                      context: Dict[str, Any] = None) -> JmcomicException:
        """
        Wrap existing exception with additional context.
        
        Parameters:
        - exception: Exception - Original exception
        - message: str - New error message
        - context: dict, optional - Additional context
        
        Returns:
        JmcomicException - Wrapped exception with context
        """
        wrapped = JmcomicException(message, context, exception)
        wrapped.add_context('original_exception_type', type(exception).__name__)
        return wrapped
    
    @staticmethod
    def create_with_context(exception_class: type, message: str, 
                           **context) -> JmcomicException:
        """
        Create exception with context from keyword arguments.
        
        Parameters:
        - exception_class: type - Exception class to create
        - message: str - Error message
        - **context: Additional context as keyword arguments
        
        Returns:
        JmcomicException - Created exception with context
        """
        return exception_class(message, context)

Usage Examples

# Basic exception handling
try:
    album = download_album("invalid_id")
except JmcomicException as e:
    print(f"Error: {e.get_full_message()}")
    print(f"Error code: {e.error_code}")
    
    # Access specific context
    if e.has_context('album_id'):
        print(f"Failed album ID: {e.get_context('album_id')}")

# HTTP response error handling
try:
    response = client.get_album_detail("123456")
except ResponseUnexpectedException as e:
    print(f"HTTP Error: {e.status_code}")
    print(f"URL: {e.response_url}")
    print(f"Content preview: {e.get_context('content_preview')}")

# Partial download failure handling
try:
    results = download_batch(download_album, album_ids)
except PartialDownloadFailedException as e:
    print(f"Success rate: {e.success_rate}%")
    print(f"Successful: {len(e.successful_downloads)}")
    print(f"Failed: {len(e.failed_downloads)}")
    
    # Process partial results
    for album_id in e.successful_downloads:
        print(f"Successfully downloaded: {album_id}")
    
    for album_id, error in e.failure_details.items():
        print(f"Failed to download {album_id}: {error}")

# Using ExceptionTool for validation
try:
    ExceptionTool.require_not_none(album_id, "Album ID cannot be None")
    ExceptionTool.require_not_empty(photo_list, "Photo list cannot be empty")
    ExceptionTool.require_true(len(album_id) > 0, "Album ID must not be empty")
except JmcomicException as e:
    print(f"Validation error: {e.message}")

# Creating exceptions with context
error = ExceptionTool.create_with_context(
    MissingAlbumPhotoException,
    "Album not found",
    album_id="123456",
    content_type="album",
    availability_status="removed"
)
raise error

Exception Hierarchy

The exception hierarchy provides specific error types for different scenarios:

  • JmcomicException: Base class with context support
    • ResponseUnexpectedException: HTTP and network errors
    • RequestRetryAllFailException: Retry exhaustion
    • RegularNotMatchException: HTML parsing failures
    • JsonResolveFailException: JSON parsing errors
    • MissingAlbumPhotoException: Content not found
    • PartialDownloadFailedException: Partial operation failures

Error Recovery Strategies

The exception system supports various recovery strategies:

  1. Retry Logic: Use RequestRetryAllFailException to implement retry mechanisms
  2. Partial Processing: Handle PartialDownloadFailedException to process successful parts
  3. Graceful Degradation: Use context information to provide alternative approaches
  4. User Feedback: Rich error messages and context for user-friendly error reporting

Install with Tessl CLI

npx tessl i tessl/pypi-jmcomic

docs

client-system.md

command-line-interface.md

configuration-management.md

content-entities.md

core-download-api.md

download-system.md

exception-handling.md

index.md

plugin-system.md

text-data-processing.md

tile.json