veilid/veilid-python/veilid/error.py
2023-08-02 12:28:05 -07:00

160 lines
3.5 KiB
Python

import inspect
from dataclasses import dataclass
from typing import Any, Self
_ERROR_REGISTRY: dict[str, type] = {}
class VeilidAPIError(Exception):
"""Veilid API error exception base class"""
label = "Base class"
def __init__(self, *args, **kwargs):
super().__init__(self.label, *args, **kwargs)
def __str__(self) -> str:
args = [('label', self.label)] + sorted(vars(self).items())
return " ".join(f"{key}={value!r}" for (key, value) in args)
@classmethod
def from_json(cls, json: dict) -> Self:
kind = json["kind"]
try:
error_class = _ERROR_REGISTRY[kind]
except KeyError:
return cls(f"Unknown exception type: {kind}")
args = {key: value for key, value in json.items() if key != "kind"}
return error_class(**args)
@dataclass
class VeilidAPIErrorNotInitialized(VeilidAPIError):
"""Veilid was not initialized"""
label = "Not initialized"
@dataclass
class VeilidAPIErrorAlreadyInitialized(VeilidAPIError):
"""Veilid was already initialized"""
label = "Already initialized"
@dataclass
class VeilidAPIErrorTimeout(VeilidAPIError):
"""Veilid operation timed out"""
label = "Timeout"
@dataclass
class VeilidAPIErrorTryAgain(VeilidAPIError):
"""Operation could not be performed at this time, retry again later"""
label = "Try again"
@dataclass
class VeilidAPIErrorShutdown(VeilidAPIError):
"""Veilid was already shut down"""
label = "Shutdown"
@dataclass
class VeilidAPIErrorInvalidTarget(VeilidAPIError):
"""Target of operation is not valid"""
label = "Invalid target"
@dataclass
class VeilidAPIErrorNoConnection(VeilidAPIError):
"""Connection could not be established"""
label = "No connection"
message: str
@dataclass
class VeilidAPIErrorKeyNotFound(VeilidAPIError):
"""Key was not found"""
label = "Key not found"
key: str
@dataclass
class VeilidAPIErrorInternal(VeilidAPIError):
"""Veilid experienced an internal failure"""
label = "Internal"
message: str
@dataclass
class VeilidAPIErrorUnimplemented(VeilidAPIError):
"""Functionality is not yet implemented"""
label = "Unimplemented"
message: str
@dataclass
class VeilidAPIErrorParseError(VeilidAPIError):
"""Value was not in a parseable format"""
label = "Parse error"
message: str
value: str
@dataclass
class VeilidAPIErrorInvalidArgument(VeilidAPIError):
"""Argument is not valid in this context"""
label = "Invalid argument"
context: str
argument: str
value: str
@dataclass
class VeilidAPIErrorMissingArgument(VeilidAPIError):
"""Required argument was missing"""
label = "Missing argument"
context: str
argument: str
@dataclass
class VeilidAPIErrorGeneric(VeilidAPIError):
"""Generic error message"""
label = "Generic"
message: str
# Build a mapping of canonicalized labels to their exception classes. Do this in-place to update
# the object inside the closure so VeilidAPIError.from_json can access the values.
_ERROR_REGISTRY.clear()
_ERROR_REGISTRY.update(
{
obj.label.title().replace(" ", ""): obj
for obj in vars().values()
if inspect.isclass(obj) and issubclass(obj, VeilidAPIError)
}
)
def raise_api_result(api_result: dict) -> Any:
if "value" in api_result:
return api_result["value"]
if "error" in api_result:
raise VeilidAPIError.from_json(api_result["error"])
raise ValueError("Invalid format for ApiResult")