CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-connexion

OpenAPI/Swagger spec-first web framework for Python with automatic request validation and response serialization

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

exceptions.mddocs/

Exception Handling

Comprehensive exception system following RFC 7807 Problem Details standard for consistent error responses and debugging. Connexion provides both generic exceptions and HTTP-specific problem exceptions.

Capabilities

Base Exceptions

Core exception classes for Connexion-specific errors.

class ConnexionException(Exception):
    """Base exception class for all Connexion-specific errors"""
    pass

class ResolverError(ConnexionException):
    """Raised when operation resolution fails"""
    def __init__(self, reason: str, operation_id: str = None):
        """
        Initialize resolver error.
        
        Parameters:
        - reason: Description of the resolution failure
        - operation_id: Failed operation identifier
        """

class InvalidSpecification(ConnexionException):
    """Raised when OpenAPI specification is invalid"""
    def __init__(self, reason: str, spec_path: str = None):
        """
        Initialize specification error.
        
        Parameters:
        - reason: Description of the specification issue
        - spec_path: Path to the invalid specification
        """

class UnsupportedMediaTypeProblem(ConnexionException):
    """Raised when request contains unsupported media type"""
    def __init__(self, media_type: str, supported_types: list = None):
        """
        Initialize media type error.
        
        Parameters:
        - media_type: Unsupported media type
        - supported_types: List of supported media types
        """

Problem Exception Base

RFC 7807 Problem Details implementation for HTTP API errors.

class ProblemException(Exception):
    """
    Base class for RFC 7807 Problem Detail exceptions.
    Automatically generates appropriate HTTP error responses.
    """
    
    def __init__(
        self,
        status: int,
        title: str,
        detail: str = None,
        type: str = None,
        instance: str = None,
        **kwargs
    ):
        """
        Initialize problem exception.
        
        Parameters:
        - status: HTTP status code
        - title: Short, human-readable problem summary
        - detail: Human-readable explanation specific to this occurrence
        - type: URI that identifies the problem type
        - instance: URI that identifies the specific occurrence
        - **kwargs: Additional problem-specific properties
        """
        super().__init__(detail or title)
        self.status = status
        self.title = title
        self.detail = detail
        self.type = type
        self.instance = instance
        self.extra = kwargs
    
    def to_problem(self) -> dict:
        """
        Convert exception to RFC 7807 problem dict.
        
        Returns:
        dict: Problem details response
        """

HTTP Client Error Exceptions (4xx)

Exceptions for client-side errors with automatic HTTP status codes.

class BadRequestProblem(ProblemException):
    """400 Bad Request - Client sent invalid request"""
    def __init__(self, title: str = "Bad Request", **kwargs):
        super().__init__(status=400, title=title, **kwargs)

class UnauthorizedProblem(ProblemException):
    """401 Unauthorized - Authentication required or failed"""
    def __init__(self, title: str = "Unauthorized", **kwargs):
        super().__init__(status=401, title=title, **kwargs)

class ForbiddenProblem(ProblemException):
    """403 Forbidden - Access denied"""
    def __init__(self, title: str = "Forbidden", **kwargs):
        super().__init__(status=403, title=title, **kwargs)

class NotFoundProblem(ProblemException):
    """404 Not Found - Resource not found"""
    def __init__(self, title: str = "Not Found", **kwargs):
        super().__init__(status=404, title=title, **kwargs)

class MethodNotAllowedProblem(ProblemException):
    """405 Method Not Allowed - HTTP method not supported"""
    def __init__(self, title: str = "Method Not Allowed", **kwargs):
        super().__init__(status=405, title=title, **kwargs)

class NotAcceptableProblem(ProblemException):
    """406 Not Acceptable - Cannot generate acceptable response"""
    def __init__(self, title: str = "Not Acceptable", **kwargs):
        super().__init__(status=406, title=title, **kwargs)

class ConflictProblem(ProblemException):
    """409 Conflict - Request conflicts with current state"""
    def __init__(self, title: str = "Conflict", **kwargs):
        super().__init__(status=409, title=title, **kwargs)

class UnsupportedMediaTypeProblem(ProblemException):
    """415 Unsupported Media Type - Request media type not supported"""
    def __init__(self, title: str = "Unsupported Media Type", **kwargs):
        super().__init__(status=415, title=title, **kwargs)

class UnprocessableEntityProblem(ProblemException):
    """422 Unprocessable Entity - Request is well-formed but semantically incorrect"""
    def __init__(self, title: str = "Unprocessable Entity", **kwargs):
        super().__init__(status=422, title=title, **kwargs)

HTTP Server Error Exceptions (5xx)

Exceptions for server-side errors.

class InternalServerErrorProblem(ProblemException):
    """500 Internal Server Error - Unexpected server error"""
    def __init__(self, title: str = "Internal Server Error", **kwargs):
        super().__init__(status=500, title=title, **kwargs)

class NotImplementedProblem(ProblemException):
    """501 Not Implemented - Functionality not implemented"""
    def __init__(self, title: str = "Not Implemented", **kwargs):
        super().__init__(status=501, title=title, **kwargs)

class ServiceUnavailableProblem(ProblemException):
    """503 Service Unavailable - Service temporarily unavailable"""
    def __init__(self, title: str = "Service Unavailable", **kwargs):
        super().__init__(status=503, title=title, **kwargs)

Validation Exceptions

Exceptions specifically for validation failures.

class ValidationError(BadRequestProblem):
    """Request validation failure"""
    def __init__(self, detail: str, field: str = None, **kwargs):
        """
        Initialize validation error.
        
        Parameters:
        - detail: Validation failure description
        - field: Field that failed validation
        - **kwargs: Additional validation context
        """
        super().__init__(
            title="Validation Error",
            detail=detail,
            field=field,
            **kwargs
        )

class TypeValidationError(ValidationError):
    """Type validation failure"""
    def __init__(self, field: str, expected_type: str, actual_type: str, **kwargs):
        """
        Initialize type validation error.
        
        Parameters:
        - field: Field with type error
        - expected_type: Expected data type
        - actual_type: Actual data type received
        """
        super().__init__(
            detail=f"Field '{field}' expected {expected_type}, got {actual_type}",
            field=field,
            expected_type=expected_type,
            actual_type=actual_type,
            **kwargs
        )

class ExtraParameterProblem(ValidationError):
    """Extra parameter in request"""
    def __init__(self, param_name: str, **kwargs):
        """
        Initialize extra parameter error.
        
        Parameters:
        - param_name: Name of the unexpected parameter
        """
        super().__init__(
            detail=f"Extra parameter '{param_name}' not allowed",
            parameter=param_name,
            **kwargs
        )

Usage Examples

Basic Exception Handling

from connexion.exceptions import BadRequestProblem, NotFoundProblem

def get_user(user_id: int):
    """Get user by ID with proper error handling"""
    
    # Validate input
    if user_id <= 0:
        raise BadRequestProblem(
            detail="User ID must be a positive integer",
            user_id=user_id
        )
    
    # Look up user
    user = find_user_by_id(user_id)
    if not user:
        raise NotFoundProblem(
            detail=f"User with ID {user_id} not found",
            user_id=user_id
        )
    
    return user.to_dict()

Custom Problem Types

from connexion.exceptions import ProblemException

class RateLimitExceededProblem(ProblemException):
    """429 Too Many Requests - Rate limit exceeded"""
    def __init__(self, limit: int, window: int, **kwargs):
        super().__init__(
            status=429,
            title="Rate Limit Exceeded",
            detail=f"Rate limit of {limit} requests per {window} seconds exceeded",
            type="https://api.example.com/problems/rate-limit-exceeded",
            limit=limit,
            window=window,
            **kwargs
        )

# Usage
def api_endpoint():
    if is_rate_limited(request.remote_addr):
        raise RateLimitExceededProblem(
            limit=100,
            window=3600,
            retry_after=calculate_retry_after()
        )
    
    # Process request...
    return {"result": "success"}

Validation Error Handling

from connexion.exceptions import ValidationError, TypeValidationError

def create_user():
    """Create user with comprehensive validation"""
    data = request.json
    
    # Required field validation
    if not data.get('email'):
        raise ValidationError(
            detail="Email is required",
            field="email"
        )
    
    # Format validation
    if not is_valid_email(data['email']):
        raise ValidationError(
            detail="Invalid email format",
            field="email",
            value=data['email']
        )
    
    # Type validation
    age = data.get('age')
    if age is not None and not isinstance(age, int):
        raise TypeValidationError(
            field="age",
            expected_type="integer",
            actual_type=type(age).__name__
        )
    
    # Business rule validation
    if age is not None and age < 0:
        raise ValidationError(
            detail="Age cannot be negative",
            field="age",
            value=age
        )
    
    # Create user...
    user = create_user_record(data)
    return user.to_dict(), 201

Global Error Handler

from connexion.exceptions import ProblemException, ConnexionException

def global_error_handler(exception):
    """Global error handler for all exceptions"""
    
    # Handle Connexion problem exceptions
    if isinstance(exception, ProblemException):
        return exception.to_problem(), exception.status
    
    # Handle other Connexion exceptions
    if isinstance(exception, ConnexionException):
        return {
            "error": "Internal error",
            "type": type(exception).__name__,
            "detail": str(exception)
        }, 500
    
    # Handle unexpected exceptions
    import traceback
    logger.error(f"Unexpected error: {exception}", exc_info=True)
    
    return {
        "error": "Internal server error",
        "type": "UnexpectedError"
    }, 500

# Register global error handler
app.add_error_handler(Exception, global_error_handler)

Context-Aware Error Handling

from connexion.exceptions import ForbiddenProblem, UnauthorizedProblem

def delete_user(user_id: int):
    """Delete user with authorization checks"""
    
    # Check authentication
    current_user = request.context.get('user')
    if not current_user:
        raise UnauthorizedProblem(
            detail="Authentication required to delete users"
        )
    
    # Check authorization
    if current_user['user_id'] != user_id and 'admin' not in current_user['roles']:
        raise ForbiddenProblem(
            detail="You can only delete your own account",
            current_user_id=current_user['user_id'],
            target_user_id=user_id
        )
    
    # Check if user exists
    if not user_exists(user_id):
        raise NotFoundProblem(
            detail=f"User {user_id} not found"
        )
    
    # Perform deletion
    delete_user_record(user_id)
    return NoContent, 204

Exception with Retry Information

from connexion.exceptions import ServiceUnavailableProblem
import random

def external_service_call():
    """Call external service with retry information on failure"""
    
    try:
        result = call_external_api()
        return result
    except ExternalServiceError as e:
        # Calculate retry delay
        retry_after = random.randint(30, 120)  # 30-120 seconds
        
        raise ServiceUnavailableProblem(
            detail="External service temporarily unavailable",
            type="https://api.example.com/problems/external-service-unavailable",
            service="payment-processor",
            retry_after=retry_after,
            error_code=e.code
        )

Detailed Validation Errors

from connexion.exceptions import UnprocessableEntityProblem

def bulk_create_users():
    """Create multiple users with detailed error reporting"""
    users_data = request.json.get('users', [])
    errors = []
    
    for i, user_data in enumerate(users_data):
        user_errors = validate_user_data(user_data)
        if user_errors:
            errors.append({
                'index': i,
                'errors': user_errors
            })
    
    if errors:
        raise UnprocessableEntityProblem(
            detail="Validation failed for one or more users",
            type="https://api.example.com/problems/bulk-validation-error",
            invalid_users=errors,
            total_users=len(users_data),
            failed_count=len(errors)
        )
    
    # Create all users...
    created_users = [create_user_record(data) for data in users_data]
    return {"created_users": len(created_users)}, 201

def validate_user_data(data):
    """Validate individual user data and return list of errors"""
    errors = []
    
    if not data.get('email'):
        errors.append({'field': 'email', 'message': 'Email is required'})
    elif not is_valid_email(data['email']):
        errors.append({'field': 'email', 'message': 'Invalid email format'})
    
    if 'age' in data and (not isinstance(data['age'], int) or data['age'] < 0):
        errors.append({'field': 'age', 'message': 'Age must be a non-negative integer'})
    
    return errors

Exception Chaining

from connexion.exceptions import InternalServerErrorProblem

def complex_operation():
    """Complex operation with exception chaining"""
    
    try:
        # Step 1: Database operation
        result1 = database_operation()
        
        # Step 2: External API call
        result2 = external_api_call(result1)
        
        # Step 3: Final processing
        return process_results(result1, result2)
        
    except DatabaseError as e:
        raise InternalServerErrorProblem(
            detail="Database operation failed",
            type="https://api.example.com/problems/database-error",
            operation="complex_operation",
            step="database_operation",
            original_error=str(e)
        ) from e
        
    except ExternalAPIError as e:
        raise InternalServerErrorProblem(
            detail="External service call failed",
            type="https://api.example.com/problems/external-api-error",
            operation="complex_operation",
            step="external_api_call",
            service_url=e.url,
            original_error=str(e)
        ) from e

Install with Tessl CLI

npx tessl i tessl/pypi-connexion

docs

applications.md

cli.md

exceptions.md

index.md

operation-resolution.md

request-response.md

security.md

validation.md

tile.json