Source code for pymixef.warnings

"""Structured warning catalog and emission helpers.

Warnings remain available as ordinary :class:`warnings.WarningMessage`
instances, while :class:`~pymixef._contracts.WarningRecord` provides the stable
representation stored in results and manifests.
"""

from __future__ import annotations

import json
import warnings as _warnings
from importlib.resources import files
from typing import Any

from ._contracts import WarningRecord


[docs] class PyMixEFWarning(UserWarning): """Python warning carrying a stable PyMixEF diagnostic code.""" def __init__(self, record: WarningRecord) -> None: self.record = record super().__init__(f"[{record.code}] {record.message}") @property def code(self) -> str: """Stable warning code.""" return self.record.code
[docs] class DataAuditWarning(PyMixEFWarning): """Warning associated with data adaptation or record exclusion."""
[docs] class CovarianceWarning(PyMixEFWarning): """Warning associated with singular or boundary covariance behavior."""
[docs] class NumericalWarning(PyMixEFWarning): """Warning associated with an unreliable numerical calculation."""
[docs] def load_warning_catalog() -> dict[str, dict[str, Any]]: """Load and return the packaged warning catalog keyed by stable code.""" resource = files("pymixef").joinpath("warning_catalog.json") with resource.open("r", encoding="utf-8") as stream: raw = json.load(stream) entries = raw.get("warnings", raw) return {str(item["code"]): dict(item) for item in entries}
[docs] def warning_record( code: str, *, message: str | None = None, severity: str | None = None, component: str | None = None, remediation: str | None = None, details: dict[str, Any] | None = None, ) -> WarningRecord: """Create a record, filling omitted fields from the packaged catalog.""" catalog = load_warning_catalog() template = catalog.get(code, {}) return WarningRecord( code=code, severity=severity or str(template.get("severity", "review")), message=message or str(template.get("message", "PyMixEF warning")), component=component or template.get("component"), remediation=remediation or template.get("remediation"), details=details or {}, )
[docs] def emit_warning( code: str, *, category: type[PyMixEFWarning] = PyMixEFWarning, stacklevel: int = 2, **kwargs: Any, ) -> WarningRecord: """Create, emit, and return a structured warning record.""" record = warning_record(code, **kwargs) _warnings.warn(category(record), stacklevel=stacklevel) return record
__all__ = [ "CovarianceWarning", "DataAuditWarning", "NumericalWarning", "PyMixEFWarning", "emit_warning", "load_warning_catalog", "warning_record", ]