Exceptions#

Utilities for extracting and handling structured exception information.

Overview#

The exceptions module provides tools to capture detailed exception information in a structured format, making it easier to:

  • Log exceptions with full context

  • Serialize exception data for APIs

  • Debug production issues with complete stack traces

  • Monitor and track errors systematically

Quick Examples#

Basic Exception Handling#

Extract exception information during error handling:

from core_mixins.exceptions import get_exception_data

try:
    result = 10 / 0
except ZeroDivisionError:
    exc_type, exc_message, stack_trace = get_exception_data()

    print(f"Exception Type: {exc_type}")      # "ZeroDivisionError"
    print(f"Message: {exc_message}")          # "division by zero"
    print(f"Stack Depth: {len(stack_trace)}") # Number of frames

Logging with Full Context#

Capture complete error information for logging:

import logging
from core_mixins.exceptions import get_exception_data

logger = logging.getLogger(__name__)

def process_data(data):
    try:
        # Process data
        result = data['value'] / data['divisor']
        return result
    except Exception:
        exc_type, exc_message, stack_trace = get_exception_data()

        logger.error(
            f"Error processing data: {exc_type} - {exc_message}",
            extra={
                "exception_type": exc_type,
                "exception_message": exc_message,
                "stack_trace": stack_trace
            }
        )
        raise

API Error Responses#

Return structured error information in API responses:

from flask import jsonify
from core_mixins.exceptions import get_exception_data

@app.route('/api/calculate', methods=['POST'])
def calculate():
    try:
        data = request.get_json()
        result = perform_calculation(data)
        return jsonify({"result": result})

    except Exception:
        exc_type, exc_message, stack_trace = get_exception_data()

        return jsonify({
            "error": {
                "type": exc_type,
                "message": exc_message,
                "stack_trace": stack_trace if app.debug else None
            }
        }), 500

Detailed Stack Trace Analysis#

Examine each frame in the stack trace:

from core_mixins.exceptions import get_exception_data

def divide_numbers(a, b):
    return a / b

def calculate():
    return divide_numbers(10, 0)

try:
    calculate()
except Exception:
    exc_type, exc_message, stack_trace = get_exception_data()

    print(f"\nException: {exc_type} - {exc_message}\n")
    print("Stack Trace:")
    for frame in stack_trace:
        print(f"  File: {frame['File']}")
        print(f"  Line: {frame['Line Number']} - {frame['Line']}")
        print(f"  Function: {frame['Function']}")
        print()

# Output:
# Exception: ZeroDivisionError - division by zero
#
# Stack Trace:
#   File: /path/to/script.py
#   Line: 7 - return divide_numbers(10, 0)
#   Function: calculate
#
#   File: /path/to/script.py
#   Line: 4 - return a / b
#   Function: divide_numbers

Error Monitoring System#

Build an error tracking system:

from datetime import datetime
from core_mixins.exceptions import get_exception_data
import json

class ErrorTracker:
    """Track and store application errors."""

    def __init__(self):
        self.errors = []

    def capture_exception(self, context=None):
        """Capture current exception with context."""
        exc_type, exc_message, stack_trace = get_exception_data()

        error_record = {
            "timestamp": datetime.utcnow().isoformat(),
            "type": exc_type,
            "message": exc_message,
            "stack_trace": stack_trace,
            "context": context or {}
        }

        self.errors.append(error_record)
        return error_record

    def export_errors(self, filepath):
        """Export errors to JSON file."""
        with open(filepath, 'w') as f:
            json.dump(self.errors, f, indent=2)

# Usage
tracker = ErrorTracker()

try:
    # Application code
    process_user_data(user_id=123)
except Exception:
    tracker.capture_exception(context={"user_id": 123})
    raise

Return Value Structure#

The get_exception_data() function returns a tuple with three elements:

exc_type, exc_message, stack_trace = get_exception_data()

1. Exception Type (str)

The name of the exception class:

"ZeroDivisionError"
"KeyError"
"ValueError"
"FileNotFoundError"
"Unknown"  # When no exception info available

2. Exception Message (str)

The exception message describing what went wrong:

"division by zero"
"'missing_key'"
"invalid literal for int() with base 10: 'abc'"
"No such file or directory: 'data.txt'"

3. Stack Trace (List[Dict])

A list of dictionaries, each representing a stack frame:

[
    {
        "File": "/path/to/module.py",
        "Line Number": 42,
        "Line": "result = calculate(value)",
        "Function": "process_data"
    },
    {
        "File": "/path/to/helpers.py",
        "Line Number": 15,
        "Line": "return numerator / denominator",
        "Function": "calculate"
    }
]

Use Cases#

Error Logging and Monitoring#

Capture detailed error information for debugging:

  • Log structured exception data to monitoring systems (Sentry, DataDog, etc.)

  • Track error patterns and frequencies

  • Include stack traces in error reports

API Error Handling#

Return consistent error responses:

  • Standardize error response format

  • Include stack traces in development mode

  • Hide sensitive information in production

Debugging and Testing#

Analyze exception context:

  • Examine the call stack to find error origins

  • Test error handling paths

  • Validate exception information in tests

Error Recovery#

Make informed decisions based on exception details:

try:
    perform_operation()
except Exception:
    exc_type, exc_message, stack_trace = get_exception_data()

    # Retry for specific errors
    if exc_type in ("ConnectionError", "TimeoutError"):
        retry_operation()

    # Fail fast for others
    else:
        send_alert(exc_type, exc_message, stack_trace)
        raise

Best Practices#

Always Call Within Exception Handler#

get_exception_data() must be called inside an exception handler:

# ✓ Correct
try:
    risky_operation()
except Exception:
    exc_type, exc_message, stack_trace = get_exception_data()

# ✗ Wrong - No exception information available
exc_type, exc_message, stack_trace = get_exception_data()

Store or Log Immediately#

Exception information is only available within the except block:

# ✓ Correct - Store data immediately
try:
    operation()
except Exception:
    error_data = get_exception_data()
    # error_data can be used later

# ✗ Wrong - Information lost after except block
try:
    operation()
except Exception:
    pass

error_data = get_exception_data()  # Returns empty/unknown

Don’t Expose Stack Traces in Production#

Filter sensitive information before sending to clients:

import os

try:
    process_request()
except Exception:
    exc_type, exc_message, stack_trace = get_exception_data()

    # Include stack trace only in development
    response = {
        "error": exc_type,
        "message": exc_message
    }

    if os.getenv("ENV") == "development":
        response["stack_trace"] = stack_trace

API Reference#

Exception handling utilities for extracting structured exception information and stack traces.

core_mixins.exceptions.get_exception_data() Tuple[str, str, List[Dict]][source]#

To retrieve the error information and the stack trace…

Example:

try:
    8/0

except ZeroDivisionError as error:
    type_, message, trace = get_exception_data()

Exception type: ZeroDivisionError
Exception message: division by zero
Stack trace: ...
Returns:

The error information in a tuple like: (exception_type, exception_message, stack_trace).