Source code for scitex_dev._core.errors

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Error code registry for LLM-friendly error categorization.

Provides machine-readable error codes that map to CLI exit codes
and guide LLM error recovery.
"""

from __future__ import annotations

from enum import Enum


[docs] class ErrorCode(str, Enum): """Machine-readable error codes for structured error responses.""" OK = "E000" VALIDATION = "E001" FILE_NOT_FOUND = "E002" PERMISSION = "E003" DEPENDENCY = "E004" TIMEOUT = "E005" RATE_LIMITED = "E006" NETWORK = "E007" CONFIG = "E008" CONFLICT = "E009" INTERNAL = "E999" @property def exit_code(self) -> int: """POSIX-style exit code for this error category.""" return _EXIT_CODES.get(self, 1)
_EXIT_CODES = { ErrorCode.OK: 0, ErrorCode.VALIDATION: 2, ErrorCode.FILE_NOT_FOUND: 1, ErrorCode.PERMISSION: 4, ErrorCode.DEPENDENCY: 3, ErrorCode.TIMEOUT: 5, ErrorCode.RATE_LIMITED: 5, ErrorCode.NETWORK: 1, ErrorCode.CONFIG: 2, ErrorCode.CONFLICT: 6, ErrorCode.INTERNAL: 1, } _BUILTIN_ERROR_MAP: dict[type, ErrorCode] = { FileNotFoundError: ErrorCode.FILE_NOT_FOUND, PermissionError: ErrorCode.PERMISSION, TimeoutError: ErrorCode.TIMEOUT, ConnectionError: ErrorCode.NETWORK, ImportError: ErrorCode.DEPENDENCY, ValueError: ErrorCode.VALIDATION, TypeError: ErrorCode.VALIDATION, KeyError: ErrorCode.CONFIG, }
[docs] class ScitexError(Exception): """Canonical SciTeX exception for structured failures. Use when the failure crosses an MCP / CLI / HTTP boundary and the caller (often an LLM agent) needs a machine-readable category plus a human remediation hint. For plain Python errors raised inside a script, prefer the standard library exceptions (``ValueError``, ``FileNotFoundError``, etc.) — see ``general/03_interface/01_python-api/09_error-handling.md``. Parameters ---------- message : str Human-readable description of the failure. code : ErrorCode, optional Machine-readable category. Defaults to ``ErrorCode.INTERNAL``. remediation : str, optional How to fix it (e.g. ``"pip install scitex-io[h5]"``). Examples -------- >>> raise ScitexError( ... "h5py not installed", ... code=ErrorCode.DEPENDENCY, ... remediation="pip install scitex-io[h5]", ... ) """ def __init__( self, message: str, *, code: ErrorCode = ErrorCode.INTERNAL, remediation: str | None = None, ) -> None: super().__init__(message) self.message = message self.error_code = code self.remediation = remediation
[docs] def to_dict(self) -> dict[str, str]: """Serialize to the structured shape consumed by MCP tools and `--json`.""" out: dict[str, str] = { "code": self.error_code.value, "message": self.message, } if self.remediation: out["remediation"] = self.remediation return out
def __str__(self) -> str: base = f"[{self.error_code.value}] {self.message}" if self.remediation: return f"{base} (remediation: {self.remediation})" return base
[docs] def classify_exception(exc: Exception) -> ErrorCode: """Classify an exception into an ErrorCode. Checks for a ``.error_code`` attribute first (SciTeXError convention), then falls back to built-in exception type mapping. """ code = getattr(exc, "error_code", None) if code is not None: if isinstance(code, ErrorCode): return code if isinstance(code, str): try: return ErrorCode(code) except ValueError: pass for exc_type, error_code in _BUILTIN_ERROR_MAP.items(): if isinstance(exc, exc_type): return error_code return ErrorCode.INTERNAL
# EOF