Skip to content

Context

provide.foundation.errors.context

TODO: Add module docstring.

Classes

ErrorCategory

Bases: str, Enum

Error categorization for routing and handling.

ErrorContext

Rich error context for diagnostics and monitoring.

Provides a flexible container for error metadata that can be used by different systems (logging, monitoring, Terraform, etc.).

Attributes:

Name Type Description
timestamp datetime

When the error occurred.

severity ErrorSeverity

Error severity level.

category ErrorCategory

Error category for classification.

metadata dict[str, dict[str, Any]]

Namespace-based metadata storage.

tags set[str]

Set of tags for categorization and filtering.

trace_id str | None

Optional trace ID for distributed tracing.

span_id str | None

Optional span ID for distributed tracing.

Examples:

>>> ctx = ErrorContext(severity=ErrorSeverity.HIGH)
>>> ctx.add_namespace("aws", {"region": "us-east-1", "account": "123456"})
>>> ctx.add_tag("production")
Functions
add_namespace
add_namespace(
    namespace: str, data: dict[str, Any]
) -> ErrorContext

Add namespaced metadata.

Parameters:

Name Type Description Default
namespace str

Namespace key (e.g., 'terraform', 'aws', 'http').

required
data dict[str, Any]

Metadata for this namespace.

required

Returns:

Type Description
ErrorContext

Self for method chaining.

Examples:

>>> ctx.add_namespace("terraform", {"provider": "aws", "version": "5.0"})
>>> ctx.add_namespace("http", {"method": POST, "status": 500})
Source code in provide/foundation/errors/context.py
def add_namespace(self, namespace: str, data: dict[str, Any]) -> ErrorContext:
    """Add namespaced metadata.

    Args:
        namespace: Namespace key (e.g., 'terraform', 'aws', 'http').
        data: Metadata for this namespace.

    Returns:
        Self for method chaining.

    Examples:
        >>> ctx.add_namespace("terraform", {"provider": "aws", "version": "5.0"})
        >>> ctx.add_namespace("http", {"method": POST, "status": 500})

    """
    self.metadata[namespace] = data
    return self
add_tag
add_tag(tag: str) -> ErrorContext

Add a tag for categorization.

Parameters:

Name Type Description Default
tag str

Tag to add.

required

Returns:

Type Description
ErrorContext

Self for method chaining.

Source code in provide/foundation/errors/context.py
def add_tag(self, tag: str) -> ErrorContext:
    """Add a tag for categorization.

    Args:
        tag: Tag to add.

    Returns:
        Self for method chaining.

    """
    self.tags.add(tag)
    return self
add_tags
add_tags(*tags: str) -> ErrorContext

Add multiple tags.

Parameters:

Name Type Description Default
*tags str

Tags to add.

()

Returns:

Type Description
ErrorContext

Self for method chaining.

Source code in provide/foundation/errors/context.py
def add_tags(self, *tags: str) -> ErrorContext:
    """Add multiple tags.

    Args:
        *tags: Tags to add.

    Returns:
        Self for method chaining.

    """
    self.tags.update(tags)
    return self
get_namespace
get_namespace(namespace: str) -> dict[str, Any] | None

Get metadata for a specific namespace.

Parameters:

Name Type Description Default
namespace str

Namespace key to retrieve.

required

Returns:

Type Description
dict[str, Any] | None

Namespace metadata or None if not found.

Source code in provide/foundation/errors/context.py
def get_namespace(self, namespace: str) -> dict[str, Any] | None:
    """Get metadata for a specific namespace.

    Args:
        namespace: Namespace key to retrieve.

    Returns:
        Namespace metadata or None if not found.

    """
    return self.metadata.get(namespace)
to_dict
to_dict() -> dict[str, Any]

Convert to dictionary for logging and serialization.

Returns:

Type Description
dict[str, Any]

Flattened dictionary with namespaced keys.

Examples:

>>> ctx.to_dict()
{'timestamp': '2024-01-01T12:00:00', 'severity': 'high', ...}
Source code in provide/foundation/errors/context.py
def to_dict(self) -> dict[str, Any]:
    """Convert to dictionary for logging and serialization.

    Returns:
        Flattened dictionary with namespaced keys.

    Examples:
        >>> ctx.to_dict()
        {'timestamp': '2024-01-01T12:00:00', 'severity': 'high', ...}

    """
    result: dict[str, Any] = {
        "timestamp": self.timestamp.isoformat(),
        "severity": self.severity.value,
        "category": self.category.value,
    }

    # Add tracing if present
    if self.trace_id:
        result["trace_id"] = self.trace_id
    if self.span_id:
        result["span_id"] = self.span_id

    # Flatten namespaced metadata
    for namespace, data in self.metadata.items():
        for key, value in data.items():
            result[f"{namespace}.{key}"] = value

    # Add tags if present
    if self.tags:
        result["tags"] = sorted(list(self.tags))

    return result
to_logging_context
to_logging_context() -> dict[str, Any]

Convert to context suitable for structured logging.

Returns:

Type Description
dict[str, Any]

Dictionary formatted for logger context.

Source code in provide/foundation/errors/context.py
def to_logging_context(self) -> dict[str, Any]:
    """Convert to context suitable for structured logging.

    Returns:
        Dictionary formatted for logger context.

    """
    return self.to_dict()
to_terraform_diagnostic
to_terraform_diagnostic() -> dict[str, Any]

Convert to Terraform diagnostic format.

Returns:

Type Description
dict[str, Any]

Dictionary formatted for Terraform diagnostics.

Examples:

>>> ctx.to_terraform_diagnostic()
{'severity': 'error', 'summary': '...', 'detail': {...}}
Source code in provide/foundation/errors/context.py
def to_terraform_diagnostic(self) -> dict[str, Any]:
    """Convert to Terraform diagnostic format.

    Returns:
        Dictionary formatted for Terraform diagnostics.

    Examples:
        >>> ctx.to_terraform_diagnostic()
        {'severity': 'error', 'summary': '...', 'detail': {...}}

    """
    # Map our severity to Terraform severity
    severity_map = {
        ErrorSeverity.LOW: "warning",
        ErrorSeverity.MEDIUM: "warning",
        ErrorSeverity.HIGH: "error",
        ErrorSeverity.CRITICAL: "error",
    }

    diagnostic: dict[str, Any] = {
        "severity": severity_map[self.severity],
        "detail": {},
    }

    # Add Terraform-specific metadata if present
    tf_meta = self.get_namespace("terraform")
    if tf_meta:
        diagnostic["detail"].update(tf_meta)

    # Add other relevant metadata
    for namespace, data in self.metadata.items():
        if namespace != "terraform":
            diagnostic["detail"][namespace] = data

    return diagnostic
update_namespace
update_namespace(
    namespace: str, data: dict[str, Any]
) -> ErrorContext

Update existing namespace metadata.

Parameters:

Name Type Description Default
namespace str

Namespace key to update.

required
data dict[str, Any]

Metadata to merge into namespace.

required

Returns:

Type Description
ErrorContext

Self for method chaining.

Source code in provide/foundation/errors/context.py
def update_namespace(self, namespace: str, data: dict[str, Any]) -> ErrorContext:
    """Update existing namespace metadata.

    Args:
        namespace: Namespace key to update.
        data: Metadata to merge into namespace.

    Returns:
        Self for method chaining.

    """
    if namespace not in self.metadata:
        self.metadata[namespace] = {}
    self.metadata[namespace].update(data)
    return self

ErrorSeverity

Bases: str, Enum

Error severity levels for prioritization and alerting.

Functions

capture_error_context

capture_error_context(
    error: Exception,
    severity: ErrorSeverity | None = None,
    category: ErrorCategory | None = None,
    **namespaces: dict[str, Any]
) -> ErrorContext

Capture error context from an exception.

Parameters:

Name Type Description Default
error Exception

Exception to capture context from.

required
severity ErrorSeverity | None

Optional severity override.

None
category ErrorCategory | None

Optional category override.

None
**namespaces dict[str, Any]

Namespace data to add to context.

{}

Returns:

Type Description
ErrorContext

ErrorContext with captured information.

Examples:

>>> try:
...     risky_operation()
... except Exception as e:
...     ctx = capture_error_context(
...         e,
...         severity=ErrorSeverity.HIGH,
...         aws={"region": "us-east-1"},
...         http={"status": 500}
...     )
Source code in provide/foundation/errors/context.py
def capture_error_context(
    error: Exception,
    severity: ErrorSeverity | None = None,
    category: ErrorCategory | None = None,
    **namespaces: dict[str, Any],
) -> ErrorContext:
    """Capture error context from an exception.

    Args:
        error: Exception to capture context from.
        severity: Optional severity override.
        category: Optional category override.
        **namespaces: Namespace data to add to context.

    Returns:
        ErrorContext with captured information.

    Examples:
        >>> try:
        ...     risky_operation()
        ... except Exception as e:
        ...     ctx = capture_error_context(
        ...         e,
        ...         severity=ErrorSeverity.HIGH,
        ...         aws={"region": "us-east-1"},
        ...         http={"status": 500}
        ...     )

    """
    # Determine severity and category if not provided
    if severity is None:
        severity = _determine_error_severity(error)
    if category is None:
        category = _determine_error_category(error)

    # Create context
    ctx = ErrorContext(severity=severity, category=category)

    # Add error details
    ctx.add_namespace(
        "error",
        {
            "type": type(error).__name__,
            "message": str(error),
        },
    )

    # Add any namespaces provided
    for namespace, data in namespaces.items():
        ctx.add_namespace(namespace, data)

    # Add context from FoundationError if applicable
    from provide.foundation.errors.base import FoundationError

    if isinstance(error, FoundationError) and error.context:
        grouped = _group_foundation_error_context(error.context)
        for namespace, data in grouped.items():
            ctx.update_namespace(namespace, data)

    return ctx