Source code for fm_weck.exceptions

# This file is part of fm-weck: executing fm-tools in containerized environments.
# https://gitlab.com/sosy-lab/software/fm-weck
#
# SPDX-FileCopyrightText: 2024 Dirk Beyer <https://www.sosy-lab.org>
#
# SPDX-License-Identifier: Apache-2.0

import logging
import subprocess
import traceback
from dataclasses import dataclass
from typing import Optional

logger = logging.getLogger(__name__)


[docs] class NoImageError(Exception): pass
[docs] class RunFailedError(Exception): def __init__(self, exit_code, command, output=None): super().__init__(f"Run failed with exit code {exit_code}: {output}") self.exit_code = exit_code self.command = command self.output = output
[docs] @dataclass class Failure: kind: str # e.g., "IMAGE_NOT_FOUND", "CALLED_PROCESS_ERROR", "EXCEPTION" message: str # short, user-facing detail: Optional[str] = None # optional verbose info (stderr/traceback)
[docs] def failure_from_exception(e: BaseException) -> Failure: # Special cases first if isinstance(e, NoImageError): failure = Failure("IMAGE_NOT_FOUND", str(e)) elif isinstance(e, RunFailedError): failure = Failure("RUN_FAILED", f"Run failed with exit code {e.exit_code}", detail=e.output) elif isinstance(e, subprocess.CalledProcessError): # Try to surface stderr/stdout stderr = e.stderr if hasattr(e, "stderr") else None stdout = e.stdout if hasattr(e, "stdout") else None text = stderr or stdout or str(e) failure = Failure("CALLED_PROCESS_ERROR", f"Tool failed with exit code {e.returncode}", detail=text) else: # Generic fallback failure = Failure("EXCEPTION", str(e), detail="".join(traceback.format_exception(type(e), e, e.__traceback__))) logger.error(f"[failure] kind={failure.kind}: {failure.message}") return failure
[docs] def failure_to_error_code(failure: "Failure"): from fm_weck.grpc_service.proto.fm_weck_service_pb2 import ErrorCode FAILURE_KIND_TO_ERROR_CODE = { "RUN_NOT_TERMINATED": ErrorCode.EC_RUN_NOT_TERMINATED, "RUN_NOT_FOUND": ErrorCode.EC_RUN_NOT_FOUND, "RUN_CANCELLED": ErrorCode.EC_RUN_CANCELLED, "RUN_FAILED": ErrorCode.EC_RUN_FAILED, "CALLED_PROCESS_ERROR": ErrorCode.EC_RUN_FAILED, "IMAGE_NOT_FOUND": ErrorCode.EC_RUN_FAILED, "EXCEPTION": ErrorCode.EC_UNKNOWN_ERROR, } return FAILURE_KIND_TO_ERROR_CODE.get(failure.kind, ErrorCode.EC_RUN_FAILED)
[docs] class ZenodoError(Exception): pass