Source code for gort.exceptions

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# @Author: José Sánchez-Gallego (gallegoj@uw.edu)
# @Date: 2023-01-17
# @Filename: exceptions.py
# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)

from __future__ import annotations

import inspect

from typing import TYPE_CHECKING, ClassVar

from gort.enums import ErrorCode


if TYPE_CHECKING:
    from clu import Command

    from gort.remote import ActorReply, RemoteCommand


def decapitalize_first_letter(s, upper_rest=False):
    return "".join([s[:1].lower(), (s[1:].upper() if upper_rest else s[1:])])


[docs] class GortError(Exception): """A custom core GortError exception.""" DEFAULT_ERROR_CODE: ClassVar[ErrorCode] = ErrorCode.UNCATEGORISED_ERROR EMIT_EVENT: ClassVar[bool] = True def __init__( self, message: str | None = None, error_code: int | ErrorCode | None = None, payload: dict = {}, ): try: self.error_code = ErrorCode(error_code or self.DEFAULT_ERROR_CODE) except ValueError: self.error_code = ErrorCode.UNKNOWN_ERROR error_code = self.error_code.value self.payload = payload self.raw_message = message prefix = f"Error {self.error_code.value} ({self.error_code.name})" if message is not None and message != "": message = decapitalize_first_letter(message) super().__init__(f"{prefix}: {message}") else: super().__init__(prefix)
[docs] class OverwatcherError(GortError): """An error in the overwatcher.""" pass
[docs] class TroubleshooterCriticalError(OverwatcherError): """A critical error in the troubleshooter that will shut down the system.""" def __init__(self, *args, close_dome: bool = False, **kwargs): self.close_dome = close_dome super().__init__(*args, **kwargs)
[docs] class TroubleshooterTimeoutError(OverwatcherError): """The troubleshooter timed out while running a recipe.""" pass
[docs] class RemoteCommandError(GortError): """An error in a remote command to an actor.""" EMIT_EVENT = False def __init__( self, message: str | None, command: Command, remote_command: RemoteCommand, reply: ActorReply | None = None, ): self.command = command self.remote_command = remote_command self.actor = remote_command._remote_actor.name self.reply = reply super().__init__(message, error_code=ErrorCode.COMMAND_FAILED)
[docs] class InvalidRemoteCommand(GortError): """A command that does not exist or cannot be parsed.""" def __init__( self, message: str | None, command: Command, remote_command: RemoteCommand, ): self.command = command self.remote_command = remote_command self.actor = remote_command._remote_actor.name super().__init__(message, error_code=ErrorCode.COMMAND_FAILED)
[docs] class GortTimeoutError(GortError): """A timeout error, potentially associated with a timed out remote command.""" def __init__( self, message: str | None, command: Command | None = None, remote_command: RemoteCommand | None = None, ): self.command = command self.remote_command = remote_command self.actor = remote_command._remote_actor.name if remote_command else None if self.remote_command: error_code = ErrorCode.COMMAND_TIMEDOUT else: error_code = ErrorCode.TIMEOUT super().__init__(message, error_code=error_code)
[docs] class GortTimeout(GortError): """Raised if a timeout occurs.""" pass
[docs] class GortNotImplemented(GortError): """A custom exception for not yet implemented features.""" DEFAULT_ERROR_CODE = ErrorCode.NOT_IMPLEMENTED def __init__(self, message: str | None = None): message = "This feature is not implemented yet." if not message else message super(GortNotImplemented, self).__init__(message)
[docs] class GortDeviceError(GortError): """A device error, which appends the name of the device to the error message.""" DEFAULT_ERROR_CODE = ErrorCode.DEVICE_ERROR def __init__( self, message: str | None = None, error_code: int | ErrorCode | None = None, payload: dict = {}, ) -> None: from gort.gort import GortDevice if message is not None: stack = inspect.stack() f_locals = stack[1][0].f_locals if "self" in f_locals: obj = f_locals["self"] name = getattr(obj, "name", None) if issubclass(obj.__class__, GortDevice) and name is not None: message = f"({name}) {message}" super().__init__(message, error_code=error_code, payload=payload)
[docs] class GortEnclosureError(GortDeviceError): """Enclosure-related error.""" DEFAULT_ERROR_CODE = ErrorCode.ENCLOSURE_ERROR
[docs] class GortNPSError(GortDeviceError): """NPS-related error.""" DEFAULT_ERROR_CODE = ErrorCode.NPS_ERROR
[docs] class GortGuiderError(GortDeviceError): """Guider-related error.""" DEFAULT_ERROR_CODE = ErrorCode.GUIDER_ERROR
[docs] class GortSpecError(GortDeviceError): """Spectrograph-related error.""" DEFAULT_ERROR_CODE = ErrorCode.SPECTROGRAPH_ERROR
[docs] class GortAGError(GortDeviceError): """AG-related error.""" DEFAULT_ERROR_CODE = ErrorCode.AG_ERROR
[docs] class GortTelescopeError(GortDeviceError): """Telescope-related error.""" DEFAULT_ERROR_CODE = ErrorCode.TELESCOPE_ERROR
[docs] class TileError(GortError): """An error associated with a `.Tile`.""" DEFAULT_ERROR_CODE = ErrorCode.SCHEDULER_TILE_ERROR
[docs] class GortObserverError(GortError): """An error associated with the `.Observer`.""" DEFAULT_ERROR_CODE = ErrorCode.OBSERVER_ERROR
[docs] class GortObserverCancelledError(GortObserverError): """An error issued when the observer is cancelled.""" EMIT_EVENT = False # Only used to trigger a cancellation of the observing loop. pass
[docs] class GortWarning(Warning): """Base warning for Gort.""" pass
[docs] class GortUserWarning(UserWarning, GortWarning): """The primary warning class.""" pass